<commit_before>'use strict';
var yeoman = require('yeoman-generator');
var chalk = require('chalk');
var shelljs = require('shelljs');
var crypto = require('crypto');
var _ = require('lodash');

module.exports = yeoman.generators.Base.extend({
    initializing: {
        sayHello: function() {
            this.log(chalk.white('Welcome to the JHipster Docker Compose Sub-Generator '));
            this.log(chalk.white('Files will be generated in folder: ' + chalk.yellow(this.destinationRoot())));
        },

        checkDocker: function() {
            var done = this.async();

            shelljs.exec('docker -v', {silent:true},function(code, stdout, stderr) {
                if (stderr) {
                    this.log(chalk.yellow.bold('WARNING!') + ' docker is not found on your computer.\n' +
                        '         Read http://docs.docker.com/engine/installation/#installation\n');
                }
                done();
            }.bind(this));
        },

        checkDockerCompose: function() {
            var done = this.async();

            shelljs.exec('docker-compose -v', {silent:true}, function(code, stdout, stderr) {
                if (stderr) {
                    this.log(chalk.yellow.bold('WARNING!') + ' docker-compose is not found on your computer.\n' +
                        '         Read https://docs.docker.com/compose/install/\n');
                }
                done();
            }.bind(this));
        },

        loadConfig: function() {

            this.defaultAppsFolders = this.config.get('appsFolders');
            this.useElk = this.config.get('useElk');
            this.profile = this.config.get('profile');
            this.jwtSecretKey = this.config.get('jwtSecretKey');

            if(this.defaultAppsFolders !== undefined) {
                this.regenerate = true;
                this.log('\nFound .yo-rc.json config file...');
            }
        }
    },

    prompting: {
        askForPath: function() {
            var done = this.async();

            var prompts = [{
                type: 'input',
                name: 'directoryPath',
                message: 'Where are the applications located ?',
                default: '../',
                validate: function (input) {
                    var path = this.destinationPath(input);
                    if(shelljs.test('-d', path)) {
                        var files = shelljs.ls('-l',this.destinationPath(input));
                        this.appsFolders = [];

                        files.forEach(function(file) {
                            if(file.isDirectory()) {
                                if(shelljs.test('-f', file.name + '/.yo-rc.json')) {
                                    var fileData = this.fs.readJSON(file.name + '/.yo-rc.json');
                                    if(fileData['generator-jhipster'] !== undefined) {
                                        this.appsFolders.push(file.name.match(/([^\/]*)\/*$/)[1]);
                                    }
                                }
                            }
                        }, this);

                        if(this.appsFolders.length === 0) {
                            return 'No microservice or gateway found in ' + this.destinationPath(input);
                        } else {
                            return true;
                        }
                    } else {
                        return path + ' is not a directory or doesn\'t exist';
                    }

                }.bind(this)
            }];

            this.prompt(prompts, function (props) {
                this.directoryPath = props.directoryPath;

                //Removing monolithic apps and registry from appsFolders
                for(var i = 0; i < this.appsFolders.length; i++) {
                    var path = this.destinationPath(this.directoryPath + this.appsFolders[i]+'/.yo-rc.json');
                    var fileData = this.fs.readJSON(path);
                    var config = fileData['generator-jhipster'];
                    if(config.applicationType === 'monolith' || this.appsFolders[i]==='jhipster-registry' || this.appsFolders[i] === 'registry') {
                        this.appsFolders.splice(i,1);
                        i--;
                    }
                }

                this.log(chalk.green(this.appsFolders.length + ' applications found at ' + this.destinationPath(this.directoryPath) + '\n'));

                done();
            }.bind(this));
        },

        askForApps: function() {
            if(this.abort) return;
            var done = this.async();

            var prompts = [{
                type: 'checkbox',
                name: 'chosenApps',
                message: 'Which applications do you want in your DockerFile ?',
                choices: this.appsFolders,
                default: this.defaultAppsFolders,
                validate: function (input) {
                    if(input.length === 0) {
                        return 'Please choose at least one application';
                    } else return true;
                }
            }];

            this.prompt(prompts, function (props) {
                this.appsFolders = props.chosenApps;

                this.appConfigs = [];

                //Loading configs
                for(var i = 0; i < this.appsFolders.length; i++) {
                    var path = this.destinationPath(this.directoryPath + this.appsFolders[i]+'/.yo-rc.json');
                    var fileData = this.fs.readJSON(path);
                    var config = fileData['generator-jhipster'];
                    this.appConfigs.push(config);
                }

                done();
            }.bind(this));
        },

        askForElk: function() {
            if(this.abort) return;
            var done = this.async();

            var prompts = [{
                type: 'confirm',
                name: 'elk',
                message: 'Do you want ELK to monitor your applications ?',
                default: this.useElk && true
            }];

            this.prompt(prompts, function(props) {
                this.useElk = props.elk;

                done();
            }.bind(this));
        },

        askForProfile: function() {
            if(this.abort) return;
            var done = this.async();

            var choices = ['dev', 'prod'];

            var prompts = [{
                type: 'list',
                name: 'profile',
                message: 'Which profile do you want to use ?',
                choices: choices,
                default: this.profile || 'dev'
            }];

            this.prompt(prompts, function(props) {
                this.profile = props.profile;

                done();
            }.bind(this));
        }
    },

    configuring: {
        checkImages: function() {
            if(this.abort) return;

            this.log('\nChecking Docker images in applications directories...');

            for (var i = 0; i < this.appsFolders.length; i++) {
                this.log(this.appConfigs[i]);
                if(this.appConfigs[i].buildTool === 'maven') {
                    var imagePath = this.destinationPath(this.directoryPath + this.appsFolders[i] + '/target/docker/' + _.kebabCase(this.appConfigs[i].baseName) + '-0.0.1-SNAPSHOT.war');
                    var runCommand = 'mvn package docker:build';
                } else {
                    var imagePath = this.destinationPath(this.directoryPath + this.appsFolders[i] + '/build/docker/' + _.kebabCase(this.appConfigs[i].baseName) + '-0.0.1-SNAPSHOT.war');
                    var runCommand = 'gradle bootRepackage buildDocker';
                }

                if (!shelljs.test('-f', imagePath)) {
                    this.log(chalk.red('\nDocker Image not found at ' + imagePath));
                    this.log(chalk.red('Please run "' + runCommand + '" in ' + this.destinationPath(this.directoryPath + this.appsFolders[i]) + ' to generate Docker image'));
                    this.abort = true;
                }
            }

            if(!this.abort) this.log(chalk.green('Found Docker images, writing files...\n'));
        },

        generateJwtSecret: function() {
            if(this.jwtSecretKey === undefined) {
                this.jwtSecretKey = crypto.randomBytes(20).toString('hex');
            }
        },

        setAppsFolderPaths: function() {
            this.appsFolderPaths = [];
            for (var i = 0; i < this.appsFolders.length; i++) {
                var path = this.destinationPath(this.directoryPath + this.appsFolders[i]);
                this.appsFolderPaths.push(path);
            }
        },

        saveConfig: function() {
            if(this.abort) return;
            this.config.set('appsFolders', this.appsFolders);
            this.config.set('useElk', this.useElk);
            this.config.set('profile', this.profile);
            this.config.set('jwtSecretKey', this.jwtSecretKey);
        }
    },

    writing: {
        writeDockerCompose: function() {
            if(this.abort) return;

            this.template('_docker-compose.yml', 'docker-compose.yml');
        },

        writeRegistryFiles: function() {
            if(this.abort) return;

            this.copy('registry.yml', 'registry.yml');
            this.template('central-server-config/_application.yml', 'central-server-config/application.yml');
        },

        writeElkFiles: function() {
            if(!this.useElk || this.abort) return;

            this.copy('elk.yml', 'elk.yml');
            this.copy('log-monitoring/log-config/logstash.conf', 'log-monitoring/log-config/logstash.conf');
            this.copy('log-monitoring/log-data/.gitignore', 'log-monitoring/log-data/.gitignore');
        }
    },
    end: function() {
        if(this.abort) return;

        this.log('\n' + chalk.bold.green('##### USAGE #####'));
        this.log('First launch the JHipster Registry by running : ' + chalk.cyan('docker-compose up -d jhipster-registry'));
        this.log('Wait a minute, then launch all your applications by running : ' + chalk.cyan('docker-compose up -d'));
    }
});
<commit_msg>updating gradle command with ./gradlew [ci skip]<commit_after>'use strict';
var yeoman = require('yeoman-generator');
var chalk = require('chalk');
var shelljs = require('shelljs');
var crypto = require('crypto');
var _ = require('lodash');

module.exports = yeoman.generators.Base.extend({
    initializing: {
        sayHello: function() {
            this.log(chalk.white('Welcome to the JHipster Docker Compose Sub-Generator '));
            this.log(chalk.white('Files will be generated in folder: ' + chalk.yellow(this.destinationRoot())));
        },

        checkDocker: function() {
            var done = this.async();

            shelljs.exec('docker -v', {silent:true},function(code, stdout, stderr) {
                if (stderr) {
                    this.log(chalk.yellow.bold('WARNING!') + ' docker is not found on your computer.\n' +
                        '         Read http://docs.docker.com/engine/installation/#installation\n');
                }
                done();
            }.bind(this));
        },

        checkDockerCompose: function() {
            var done = this.async();

            shelljs.exec('docker-compose -v', {silent:true}, function(code, stdout, stderr) {
                if (stderr) {
                    this.log(chalk.yellow.bold('WARNING!') + ' docker-compose is not found on your computer.\n' +
                        '         Read https://docs.docker.com/compose/install/\n');
                }
                done();
            }.bind(this));
        },

        loadConfig: function() {

            this.defaultAppsFolders = this.config.get('appsFolders');
            this.useElk = this.config.get('useElk');
            this.profile = this.config.get('profile');
            this.jwtSecretKey = this.config.get('jwtSecretKey');

            if(this.defaultAppsFolders !== undefined) {
                this.regenerate = true;
                this.log('\nFound .yo-rc.json config file...');
            }
        }
    },

    prompting: {
        askForPath: function() {
            var done = this.async();

            var prompts = [{
                type: 'input',
                name: 'directoryPath',
                message: 'Where are the applications located ?',
                default: '../',
                validate: function (input) {
                    var path = this.destinationPath(input);
                    if(shelljs.test('-d', path)) {
                        var files = shelljs.ls('-l',this.destinationPath(input));
                        this.appsFolders = [];

                        files.forEach(function(file) {
                            if(file.isDirectory()) {
                                if(shelljs.test('-f', file.name + '/.yo-rc.json')) {
                                    var fileData = this.fs.readJSON(file.name + '/.yo-rc.json');
                                    if(fileData['generator-jhipster'] !== undefined) {
                                        this.appsFolders.push(file.name.match(/([^\/]*)\/*$/)[1]);
                                    }
                                }
                            }
                        }, this);

                        if(this.appsFolders.length === 0) {
                            return 'No microservice or gateway found in ' + this.destinationPath(input);
                        } else {
                            return true;
                        }
                    } else {
                        return path + ' is not a directory or doesn\'t exist';
                    }

                }.bind(this)
            }];

            this.prompt(prompts, function (props) {
                this.directoryPath = props.directoryPath;

                //Removing monolithic apps and registry from appsFolders
                for(var i = 0; i < this.appsFolders.length; i++) {
                    var path = this.destinationPath(this.directoryPath + this.appsFolders[i]+'/.yo-rc.json');
                    var fileData = this.fs.readJSON(path);
                    var config = fileData['generator-jhipster'];
                    if(config.applicationType === 'monolith' || this.appsFolders[i]==='jhipster-registry' || this.appsFolders[i] === 'registry') {
                        this.appsFolders.splice(i,1);
                        i--;
                    }
                }

                this.log(chalk.green(this.appsFolders.length + ' applications found at ' + this.destinationPath(this.directoryPath) + '\n'));

                done();
            }.bind(this));
        },

        askForApps: function() {
            if(this.abort) return;
            var done = this.async();

            var prompts = [{
                type: 'checkbox',
                name: 'chosenApps',
                message: 'Which applications do you want in your DockerFile ?',
                choices: this.appsFolders,
                default: this.defaultAppsFolders,
                validate: function (input) {
                    if(input.length === 0) {
                        return 'Please choose at least one application';
                    } else return true;
                }
            }];

            this.prompt(prompts, function (props) {
                this.appsFolders = props.chosenApps;

                this.appConfigs = [];

                //Loading configs
                for(var i = 0; i < this.appsFolders.length; i++) {
                    var path = this.destinationPath(this.directoryPath + this.appsFolders[i]+'/.yo-rc.json');
                    var fileData = this.fs.readJSON(path);
                    var config = fileData['generator-jhipster'];
                    this.appConfigs.push(config);
                }

                done();
            }.bind(this));
        },

        askForElk: function() {
            if(this.abort) return;
            var done = this.async();

            var prompts = [{
                type: 'confirm',
                name: 'elk',
                message: 'Do you want ELK to monitor your applications ?',
                default: this.useElk && true
            }];

            this.prompt(prompts, function(props) {
                this.useElk = props.elk;

                done();
            }.bind(this));
        },

        askForProfile: function() {
            if(this.abort) return;
            var done = this.async();

            var choices = ['dev', 'prod'];

            var prompts = [{
                type: 'list',
                name: 'profile',
                message: 'Which profile do you want to use ?',
                choices: choices,
                default: this.profile || 'dev'
            }];

            this.prompt(prompts, function(props) {
                this.profile = props.profile;

                done();
            }.bind(this));
        }
    },

    configuring: {
        checkImages: function() {
            if(this.abort) return;

            this.log('\nChecking Docker images in applications directories...');

            for (var i = 0; i < this.appsFolders.length; i++) {
                this.log(this.appConfigs[i]);
                if(this.appConfigs[i].buildTool === 'maven') {
                    var imagePath = this.destinationPath(this.directoryPath + this.appsFolders[i] + '/target/docker/' + _.kebabCase(this.appConfigs[i].baseName) + '-0.0.1-SNAPSHOT.war');
                    var runCommand = 'mvn package docker:build';
                } else {
                    var imagePath = this.destinationPath(this.directoryPath + this.appsFolders[i] + '/build/docker/' + _.kebabCase(this.appConfigs[i].baseName) + '-0.0.1-SNAPSHOT.war');
                    var runCommand = './gradlew bootRepackage buildDocker';
                }

                if (!shelljs.test('-f', imagePath)) {
                    this.log(chalk.red('\nDocker Image not found at ' + imagePath));
                    this.log(chalk.red('Please run "' + runCommand + '" in ' + this.destinationPath(this.directoryPath + this.appsFolders[i]) + ' to generate Docker image'));
                    this.abort = true;
                }
            }

            if(!this.abort) this.log(chalk.green('Found Docker images, writing files...\n'));
        },

        generateJwtSecret: function() {
            if(this.jwtSecretKey === undefined) {
                this.jwtSecretKey = crypto.randomBytes(20).toString('hex');
            }
        },

        setAppsFolderPaths: function() {
            this.appsFolderPaths = [];
            for (var i = 0; i < this.appsFolders.length; i++) {
                var path = this.destinationPath(this.directoryPath + this.appsFolders[i]);
                this.appsFolderPaths.push(path);
            }
        },

        saveConfig: function() {
            if(this.abort) return;
            this.config.set('appsFolders', this.appsFolders);
            this.config.set('useElk', this.useElk);
            this.config.set('profile', this.profile);
            this.config.set('jwtSecretKey', this.jwtSecretKey);
        }
    },

    writing: {
        writeDockerCompose: function() {
            if(this.abort) return;

            this.template('_docker-compose.yml', 'docker-compose.yml');
        },

        writeRegistryFiles: function() {
            if(this.abort) return;

            this.copy('registry.yml', 'registry.yml');
            this.template('central-server-config/_application.yml', 'central-server-config/application.yml');
        },

        writeElkFiles: function() {
            if(!this.useElk || this.abort) return;

            this.copy('elk.yml', 'elk.yml');
            this.copy('log-monitoring/log-config/logstash.conf', 'log-monitoring/log-config/logstash.conf');
            this.copy('log-monitoring/log-data/.gitignore', 'log-monitoring/log-data/.gitignore');
        }
    },
    end: function() {
        if(this.abort) return;

        this.log('\n' + chalk.bold.green('##### USAGE #####'));
        this.log('First launch the JHipster Registry by running : ' + chalk.cyan('docker-compose up -d jhipster-registry'));
        this.log('Wait a minute, then launch all your applications by running : ' + chalk.cyan('docker-compose up -d'));
    }
});
<|endoftext|>


<commit_before>"use strict";

// Import
// ========

var _ = require('underscore');
var util = require('substance-util');
var errors = util.errors;
var Operation = require('./operation');
var Compound = require('./compound');

var INS = "+";
var DEL = "-";

var TextOperation = function(options) {

  // if this operation should be created using an array
  if (_.isArray(options)) {
    options = {
      type: options[0],
      pos: options[1],
      str: options[2]
    };
  }

  if (options.type === undefined || options.pos === undefined || options.str === undefined) {
    throw new errors.OperationError("Illegal argument: insufficient data.");
  }

  // '+' or '-'
  this.type = options.type;

  // the position where to apply the operation
  this.pos = options.pos;

  // the string to delete or insert
  this.str = options.str;

  // sanity checks
  if(!this.isInsert() && !this.isDelete()) {
    throw new errors.OperationError("Illegal type.");
  }
  if (!_.isString(this.str)) {
    throw new errors.OperationError("Illegal argument: expecting string.");
  }
  if (!_.isNumber(this.pos) && this.pos < 0) {
    throw new errors.OperationError("Illegal argument: expecting positive number as pos.");
  }
};

TextOperation.fromJSON = function(data) {

  if (data.type === Compound.TYPE) {
    var ops = [];
    for (var idx = 0; idx < data.ops.length; idx++) {
      ops.push(TextOperation.fromJSON(data.ops[idx]));
    }
    return TextOperation.Compound(ops);

  } else {
    return new TextOperation(data);
  }
};

TextOperation.__prototype__ = function() {

  this.clone = function() {
    return new TextOperation(this);
  };

  this.isNOP = function() {
    return this.str.length === 0;
  };

  this.isInsert = function() {
    return this.type === INS;
  };

  this.isDelete = function() {
    return this.type === DEL;
  };

  this.length = function() {
    return this.str.length;
  };

  this.apply = function(str) {
    if (this.isEmpty()) return str;

    var adapter = (str instanceof TextOperation.StringAdapter) ? str : new TextOperation.StringAdapter(str);

    if (this.type === INS) {
      adapter.insert(this.pos, this.str);
    }
    else if (this.type === DEL) {
      adapter.delete(this.pos, this.str.length);
    }
    else {
      throw new errors.OperationError("Illegal operation type: " + this.type);
    }

    return adapter.get();
  };

  this.invert = function() {
    var data = {
      type: this.isInsert() ? '-' : '+',
      pos: this.pos,
      str: this.str
    };
    return new TextOperation(data);
  };

  this.hasConflict = function(other) {
    return TextOperation.hasConflict(this, other);
  };

  this.isEmpty = function() {
    return this.str.length === 0;
  };

  this.toJSON = function() {
    return {
      type: this.type,
      pos: this.pos,
      str: this.str
    };
  };

};
TextOperation.__prototype__.prototype = Operation.prototype;
TextOperation.prototype = new TextOperation.__prototype__();

var hasConflict = function(a, b) {

  // Insert vs Insert:
  //
  // Insertions are conflicting iff their insert position is the same.

  if (a.type === INS && b.type === INS)  return (a.pos === b.pos);

  // Delete vs Delete:
  //
  // Deletions are conflicting if their ranges overlap.

  if (a.type === DEL && b.type === DEL) {
    // to have no conflict, either `a` should be after `b` or `b` after `a`, otherwise.
    return !(a.pos >= b.pos + b.str.length || b.pos >= a.pos + a.str.length);
  }

  // Delete vs Insert:
  //
  // A deletion and an insertion are conflicting if the insert position is within the deleted range.

  var del, ins;
  if (a.type === DEL) {
    del = a; ins = b;
  } else {
    del = b; ins = a;
  }

  return (ins.pos >= del.pos && ins.pos < del.pos + del.str.length);
};

// Transforms two Insertions
// --------

function transform_insert_insert(a, b, first) {

  if (a.pos === b.pos) {
    if (first) {
      b.pos += a.str.length;
    } else {
      a.pos += b.str.length;
    }
  }

  else if (a.pos < b.pos) {
    b.pos += a.str.length;
  }

  else {
    a.pos += b.str.length;
  }

}

// Transform two Deletions
// --------
//

function transform_delete_delete(a, b, first) {

  // reduce to a normalized case
  if (a.pos > b.pos) {
    return transform_delete_delete(b, a, !first);
  }

  if (a.pos === b.pos && a.str.length > b.str.length) {
    return transform_delete_delete(b, a, !first);
  }

  // take out overlapping parts
  if (b.pos < a.pos + a.str.length) {
    var s = b.pos - a.pos;
    var s1 = a.str.length - s;
    var s2 = s + b.str.length;

    a.str = a.str.slice(0, s) + a.str.slice(s2);
    b.str = b.str.slice(s1);
    b.pos -= s;
  } else {
    b.pos -= a.str.length;
  }

}

// Transform Insert and Deletion
// --------
//

function transform_insert_delete(a, b) {

  if (a.type === DEL) {
    return transform_insert_delete(b, a);
  }

  // we can assume, that a is an insertion and b is a deletion

  // a is before b
  if (a.pos <= b.pos) {
    b.pos += a.str.length;
  }

  // a is after b
  else if (a.pos >= b.pos + b.str.length) {
    a.pos -= b.str.length;
  }

  // Note: this is a conflict case the user should be noticed about
  // If applied still, the deletion takes precedence
  // a.pos > b.pos && <= b.pos + b.length()
  else {
    var s = a.pos - b.pos;
    b.str = b.str.slice(0, s) + a.str + b.str.slice(s);
    a.str = "";
  }

}

var transform0 = function(a, b, options) {

  options = options || {};

  if (options.check && hasConflict(a, b)) {
    throw Operator.mergeConflict(a, b);
  }

  if (!options.inplace) {
    a = util.clone(a);
    b = util.clone(b);
  }

  if (a.type === INS && b.type === INS)  {
    transform_insert_insert(a, b, true);
  }
  else if (a.type === DEL && b.type === DEL) {
    transform_delete_delete(a, b, true);
  }
  else {
    transform_insert_delete(a,b);
  }

  return [a, b];
};

var __apply__ = function(op, array) {
  if (_.isArray(op)) {
    op = new TextOperation(op);
  }
  else if (!(op instanceof TextOperation)) {
    op = TextOperation.fromJSON(op);
  }
  return op.apply(array);
};

TextOperation.transform = Compound.createTransform(transform0);
TextOperation.apply = __apply__;

var StringAdapter = function(str) {
  this.str = str;
};
StringAdapter.prototype = {
  insert: function(pos, str) {
    if (this.str.length < pos) {
      throw new errors.OperationError("Provided string is too short.");
    }
    this.str = this.str.slice(0, pos) + str + this.str.slice(pos);
  },

  delete: function(pos, length) {
    if (this.str.length < pos + length) {
      throw new errors.OperationError("Provided string is too short.");
    }
    this.str = this.str.slice(0, pos) + this.str.slice(pos + length);
  },

  get: function() {
    return this.str;
  }
};

TextOperation.Insert = function(pos, str) {
  return new TextOperation(["+", pos, str]);
};

TextOperation.Delete = function(pos, str) {
  return new TextOperation(["-", pos, str]);
};

TextOperation.Compound = function(ops) {
  // do not create a Compound if not necessary
  if (ops.length === 1) return ops[0];
  else return new Compound(ops);
};

// Converts from a given a sequence in the format of Tim's lib
// which is an array of numbers and strings.
// 1. positive number: retain a number of characters
// 2. negative number: delete a string with the given length at the current position
// 3. string: insert the given string at the current position

TextOperation.fromOT = function(str, ops) {

  var atomicOps = []; // atomic ops

  // iterating through the sequence and bookkeeping the position
  // in the source and destination str
  var srcPos = 0,
      dstPos = 0;

  if (!_.isArray(ops)) {
    ops = _.toArray(arguments).slice(1);
  }

  _.each(ops, function(op) {
    if (_.isString(op)) { // insert chars
      atomicOps.push(TextOperation.Insert(dstPos, op));
      dstPos += op.length;
    } else if (op<0) { // delete n chars
      var n = -op;
      atomicOps.push(TextOperation.Delete(dstPos, str.slice(srcPos, srcPos+n)));
      srcPos += n;
    } else { // skip n chars
      srcPos += op;
      dstPos += op;
    }
  });

  return TextOperation.Compound(atomicOps);
};

TextOperation.fromSequence = TextOperation.fromOT;

// A helper class to model Text selections and to provide an easy way
// to bookkeep changes by other applied TextOperations
var Range = function(range) {
  if (_.isArray(range)) {
    this.start = range[0];
    this.length = range[1];
  } else {
    this.start = range.start;
    this.length = range.length;
  }
};

// Transforms a given range tuple (offset, length) in-place.
// --------
//

var range_transform = function(range, textOp, expandLeft, expandRight) {

  var changed = false;

  // handle compound operations
  if (textOp.type === Compound.TYPE) {
    for (var idx = 0; idx < textOp.ops.length; idx++) {
      var op = textOp.ops[idx];
      range_transform(range, op);
    }
    return;
  }

  var start, end;

  if (_.isArray(range)) {
    start = range[0];
    end = range[1];
  } else {
    start = range.start;
    end = start + range.length;
  }

  // Delete
  if (textOp.type === DEL) {
    var pos1 = textOp.pos;
    var pos2 = textOp.pos+textOp.str.length;

    if (pos1 <= start) {
      start -= Math.min(pos2-pos1, start-pos1);
      changed = true;
    }
    if (pos1 <= end) {
      end -= Math.min(pos2-pos1, end-pos1);
      changed = true;
    }

  } else if (textOp.type === INS) {
    var pos = textOp.pos;
    var l = textOp.str.length;

    if ( (pos < start) ||
         (pos === start && !expandLeft) ) {
      start += l;
      changed = true;
    }

    if ( (pos < end) ||
         (pos === end && !expandRight) ) {
      end += l;
      changed = true;
    }
  }

  if (changed) {
    if (_.isArray(range)) {
      range[0] = start;
      range[1] = end;
    } else {
      range.start = start;
      range.length = end - start;
    }
  }

  return changed;
};

Range.__prototype__ = function() {

  this.clone = function() {
    return new Range(this);
  };

  this.toJSON = function() {
    var result = {
      start: this.start,
      length: this.length
    };
    // if (this.expand) result.expand = true;
    return result;
  };

  this.transform = function(textOp, expand) {
    return range_transform(this.range, textOp, expand);
  };

};
Range.prototype = new Range.__prototype__();

Range.transform = function(range, op, expand) {
  return range_transform(range, op, expand);
};

Range.fromJSON = function(data) {
  return new Range(data);
};

TextOperation.StringAdapter = StringAdapter;
TextOperation.Range = Range;
TextOperation.INSERT = INS;
TextOperation.DELETE = DEL;

// Export
// ========

module.exports = TextOperation;
<commit_msg>Fix expansion support.<commit_after>"use strict";

// Import
// ========

var _ = require('underscore');
var util = require('substance-util');
var errors = util.errors;
var Operation = require('./operation');
var Compound = require('./compound');

var INS = "+";
var DEL = "-";

var TextOperation = function(options) {

  // if this operation should be created using an array
  if (_.isArray(options)) {
    options = {
      type: options[0],
      pos: options[1],
      str: options[2]
    };
  }

  if (options.type === undefined || options.pos === undefined || options.str === undefined) {
    throw new errors.OperationError("Illegal argument: insufficient data.");
  }

  // '+' or '-'
  this.type = options.type;

  // the position where to apply the operation
  this.pos = options.pos;

  // the string to delete or insert
  this.str = options.str;

  // sanity checks
  if(!this.isInsert() && !this.isDelete()) {
    throw new errors.OperationError("Illegal type.");
  }
  if (!_.isString(this.str)) {
    throw new errors.OperationError("Illegal argument: expecting string.");
  }
  if (!_.isNumber(this.pos) && this.pos < 0) {
    throw new errors.OperationError("Illegal argument: expecting positive number as pos.");
  }
};

TextOperation.fromJSON = function(data) {

  if (data.type === Compound.TYPE) {
    var ops = [];
    for (var idx = 0; idx < data.ops.length; idx++) {
      ops.push(TextOperation.fromJSON(data.ops[idx]));
    }
    return TextOperation.Compound(ops);

  } else {
    return new TextOperation(data);
  }
};

TextOperation.__prototype__ = function() {

  this.clone = function() {
    return new TextOperation(this);
  };

  this.isNOP = function() {
    return this.str.length === 0;
  };

  this.isInsert = function() {
    return this.type === INS;
  };

  this.isDelete = function() {
    return this.type === DEL;
  };

  this.length = function() {
    return this.str.length;
  };

  this.apply = function(str) {
    if (this.isEmpty()) return str;

    var adapter = (str instanceof TextOperation.StringAdapter) ? str : new TextOperation.StringAdapter(str);

    if (this.type === INS) {
      adapter.insert(this.pos, this.str);
    }
    else if (this.type === DEL) {
      adapter.delete(this.pos, this.str.length);
    }
    else {
      throw new errors.OperationError("Illegal operation type: " + this.type);
    }

    return adapter.get();
  };

  this.invert = function() {
    var data = {
      type: this.isInsert() ? '-' : '+',
      pos: this.pos,
      str: this.str
    };
    return new TextOperation(data);
  };

  this.hasConflict = function(other) {
    return TextOperation.hasConflict(this, other);
  };

  this.isEmpty = function() {
    return this.str.length === 0;
  };

  this.toJSON = function() {
    return {
      type: this.type,
      pos: this.pos,
      str: this.str
    };
  };

};
TextOperation.__prototype__.prototype = Operation.prototype;
TextOperation.prototype = new TextOperation.__prototype__();

var hasConflict = function(a, b) {

  // Insert vs Insert:
  //
  // Insertions are conflicting iff their insert position is the same.

  if (a.type === INS && b.type === INS)  return (a.pos === b.pos);

  // Delete vs Delete:
  //
  // Deletions are conflicting if their ranges overlap.

  if (a.type === DEL && b.type === DEL) {
    // to have no conflict, either `a` should be after `b` or `b` after `a`, otherwise.
    return !(a.pos >= b.pos + b.str.length || b.pos >= a.pos + a.str.length);
  }

  // Delete vs Insert:
  //
  // A deletion and an insertion are conflicting if the insert position is within the deleted range.

  var del, ins;
  if (a.type === DEL) {
    del = a; ins = b;
  } else {
    del = b; ins = a;
  }

  return (ins.pos >= del.pos && ins.pos < del.pos + del.str.length);
};

// Transforms two Insertions
// --------

function transform_insert_insert(a, b, first) {

  if (a.pos === b.pos) {
    if (first) {
      b.pos += a.str.length;
    } else {
      a.pos += b.str.length;
    }
  }

  else if (a.pos < b.pos) {
    b.pos += a.str.length;
  }

  else {
    a.pos += b.str.length;
  }

}

// Transform two Deletions
// --------
//

function transform_delete_delete(a, b, first) {

  // reduce to a normalized case
  if (a.pos > b.pos) {
    return transform_delete_delete(b, a, !first);
  }

  if (a.pos === b.pos && a.str.length > b.str.length) {
    return transform_delete_delete(b, a, !first);
  }

  // take out overlapping parts
  if (b.pos < a.pos + a.str.length) {
    var s = b.pos - a.pos;
    var s1 = a.str.length - s;
    var s2 = s + b.str.length;

    a.str = a.str.slice(0, s) + a.str.slice(s2);
    b.str = b.str.slice(s1);
    b.pos -= s;
  } else {
    b.pos -= a.str.length;
  }

}

// Transform Insert and Deletion
// --------
//

function transform_insert_delete(a, b) {

  if (a.type === DEL) {
    return transform_insert_delete(b, a);
  }

  // we can assume, that a is an insertion and b is a deletion

  // a is before b
  if (a.pos <= b.pos) {
    b.pos += a.str.length;
  }

  // a is after b
  else if (a.pos >= b.pos + b.str.length) {
    a.pos -= b.str.length;
  }

  // Note: this is a conflict case the user should be noticed about
  // If applied still, the deletion takes precedence
  // a.pos > b.pos && <= b.pos + b.length()
  else {
    var s = a.pos - b.pos;
    b.str = b.str.slice(0, s) + a.str + b.str.slice(s);
    a.str = "";
  }

}

var transform0 = function(a, b, options) {

  options = options || {};

  if (options.check && hasConflict(a, b)) {
    throw Operator.mergeConflict(a, b);
  }

  if (!options.inplace) {
    a = util.clone(a);
    b = util.clone(b);
  }

  if (a.type === INS && b.type === INS)  {
    transform_insert_insert(a, b, true);
  }
  else if (a.type === DEL && b.type === DEL) {
    transform_delete_delete(a, b, true);
  }
  else {
    transform_insert_delete(a,b);
  }

  return [a, b];
};

var __apply__ = function(op, array) {
  if (_.isArray(op)) {
    op = new TextOperation(op);
  }
  else if (!(op instanceof TextOperation)) {
    op = TextOperation.fromJSON(op);
  }
  return op.apply(array);
};

TextOperation.transform = Compound.createTransform(transform0);
TextOperation.apply = __apply__;

var StringAdapter = function(str) {
  this.str = str;
};
StringAdapter.prototype = {
  insert: function(pos, str) {
    if (this.str.length < pos) {
      throw new errors.OperationError("Provided string is too short.");
    }
    this.str = this.str.slice(0, pos) + str + this.str.slice(pos);
  },

  delete: function(pos, length) {
    if (this.str.length < pos + length) {
      throw new errors.OperationError("Provided string is too short.");
    }
    this.str = this.str.slice(0, pos) + this.str.slice(pos + length);
  },

  get: function() {
    return this.str;
  }
};

TextOperation.Insert = function(pos, str) {
  return new TextOperation(["+", pos, str]);
};

TextOperation.Delete = function(pos, str) {
  return new TextOperation(["-", pos, str]);
};

TextOperation.Compound = function(ops) {
  // do not create a Compound if not necessary
  if (ops.length === 1) return ops[0];
  else return new Compound(ops);
};

// Converts from a given a sequence in the format of Tim's lib
// which is an array of numbers and strings.
// 1. positive number: retain a number of characters
// 2. negative number: delete a string with the given length at the current position
// 3. string: insert the given string at the current position

TextOperation.fromOT = function(str, ops) {

  var atomicOps = []; // atomic ops

  // iterating through the sequence and bookkeeping the position
  // in the source and destination str
  var srcPos = 0,
      dstPos = 0;

  if (!_.isArray(ops)) {
    ops = _.toArray(arguments).slice(1);
  }

  _.each(ops, function(op) {
    if (_.isString(op)) { // insert chars
      atomicOps.push(TextOperation.Insert(dstPos, op));
      dstPos += op.length;
    } else if (op<0) { // delete n chars
      var n = -op;
      atomicOps.push(TextOperation.Delete(dstPos, str.slice(srcPos, srcPos+n)));
      srcPos += n;
    } else { // skip n chars
      srcPos += op;
      dstPos += op;
    }
  });

  return TextOperation.Compound(atomicOps);
};

TextOperation.fromSequence = TextOperation.fromOT;

// A helper class to model Text selections and to provide an easy way
// to bookkeep changes by other applied TextOperations
var Range = function(range) {
  if (_.isArray(range)) {
    this.start = range[0];
    this.length = range[1];
  } else {
    this.start = range.start;
    this.length = range.length;
  }
};

// Transforms a given range tuple (offset, length) in-place.
// --------
//

var range_transform = function(range, textOp, expandLeft, expandRight) {

  var changed = false;

  // handle compound operations
  if (textOp.type === Compound.TYPE) {
    for (var idx = 0; idx < textOp.ops.length; idx++) {
      var op = textOp.ops[idx];
      range_transform(range, op);
    }
    return;
  }

  var start, end;

  if (_.isArray(range)) {
    start = range[0];
    end = range[1];
  } else {
    start = range.start;
    end = start + range.length;
  }

  // Delete
  if (textOp.type === DEL) {
    var pos1 = textOp.pos;
    var pos2 = textOp.pos+textOp.str.length;

    if (pos1 <= start) {
      start -= Math.min(pos2-pos1, start-pos1);
      changed = true;
    }
    if (pos1 <= end) {
      end -= Math.min(pos2-pos1, end-pos1);
      changed = true;
    }

  } else if (textOp.type === INS) {
    var pos = textOp.pos;
    var l = textOp.str.length;

    if ( (pos < start) ||
         (pos === start && !expandLeft) ) {
      start += l;
      changed = true;
    }

    if ( (pos < end) ||
         (pos === end && expandRight) ) {
      end += l;
      changed = true;
    }
  }

  if (changed) {
    if (_.isArray(range)) {
      range[0] = start;
      range[1] = end;
    } else {
      range.start = start;
      range.length = end - start;
    }
  }

  return changed;
};

Range.__prototype__ = function() {

  this.clone = function() {
    return new Range(this);
  };

  this.toJSON = function() {
    var result = {
      start: this.start,
      length: this.length
    };
    // if (this.expand) result.expand = true;
    return result;
  };

  this.transform = function(textOp, expand) {
    return range_transform(this.range, textOp, expand);
  };

};
Range.prototype = new Range.__prototype__();

Range.transform = function(range, op, expandLeft, expandRight) {
  return range_transform(range, op, expandLeft, expandRight);
};

Range.fromJSON = function(data) {
  return new Range(data);
};

TextOperation.StringAdapter = StringAdapter;
TextOperation.Range = Range;
TextOperation.INSERT = INS;
TextOperation.DELETE = DEL;

// Export
// ========

module.exports = TextOperation;
<|endoftext|>


<commit_before>const systemFontStack = [
  "-apple-system",
  "BlinkMacSystemFont",
  '"Segoe UI"',
  "Roboto",
  "Helvetica",
  "Arial",
  "sans-serif",
  '"Apple Color Emoji"',
  '"Segoe UI Emoji"',
  '"Segoe UI Symbol"'
].join(",");

const size = (base, unit = "rem") => (modify = b => b) =>
  `${modify(base)}${unit}`;

const white = "#fff";
const black = "#212529";

export default {
  fontFamily: systemFontStack,
  headingFontFamily: systemFontStack,
  paddingX: size(1),
  paddingY: size(1),
  spacingBase: size(1.25),
  fontSize: size(1),
  readableWidth: size(38),
  borderColor: "#dee2e6",
  textColor: black,
  focusColor: "#fcc419",
  black,
  white,
  linkColor: "#329af0",
  linkVisitedColor: "#8859b6",
  brandPrimary: "#329af0",
  brandSecondary: "#1862ab",
  brandSuccess: "#40c057",
  brandDanger: "#f03e3e",
  brandDisabled: "#ced4da"
};
<commit_msg>reduce padding sizes<commit_after>const systemFontStack = [
  "-apple-system",
  "BlinkMacSystemFont",
  '"Segoe UI"',
  "Roboto",
  "Helvetica",
  "Arial",
  "sans-serif",
  '"Apple Color Emoji"',
  '"Segoe UI Emoji"',
  '"Segoe UI Symbol"'
].join(",");

const size = (base, unit = "rem") => (modify = b => b) =>
  `${modify(base)}${unit}`;

const white = "#fff";
const black = "#212529";

export default {
  fontFamily: systemFontStack,
  headingFontFamily: systemFontStack,
  paddingX: size(0.75),
  paddingY: size(0.75),
  spacingBase: size(1.25),
  fontSize: size(1),
  readableWidth: size(38),
  borderColor: "#dee2e6",
  textColor: black,
  focusColor: "#fcc419",
  black,
  white,
  linkColor: "#329af0",
  linkVisitedColor: "#8859b6",
  brandPrimary: "#329af0",
  brandSecondary: "#1862ab",
  brandSuccess: "#40c057",
  brandDanger: "#f03e3e",
  brandDisabled: "#ced4da"
};
<|endoftext|>


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

const fs = require('fs');
const util = require('util');
const xml2js = require('xml2js');
const meow = require('meow');

const wfg = {};

wfg.ProtocolParser = class {

    ["uint"](argName, optional) {
        return {
            signature: optional ? "?u" : "u",
            jsType: optional ? "?Number" : "Number",
            marshallGen: optional ? util.format("wfc._uintOptional(%s)", argName) : util.format("wfc._uint(%s)", argName)
        };
    }

    ["int"](argName, optional) {
        return {
            signature: optional ? "?i" : "i",
            jsType: optional ? "?Number" : "Number",
            marshallGen: optional ? util.format("wfc._intOptional(%s)", argName) : util.format("wfc._int(%s)", argName)
        };
    }

    ["fixed"](argName, optional) {
        return {
            signature: optional ? "?f" : "f",
            jsType: optional ? "?Fixed" : "Fixed",
            marshallGen: optional ? util.format("wfc._fixedOptional(%s)", argName) : util.format("wfc._fixed(%s)", argName)
        };
    }

    ["object"](argName, optional) {
        return {
            signature: optional ? "?o" : "o",
            jsType: optional ? "?*" : "*",
            marshallGen: optional ? util.format("wfc._objectOptional(%s)", argName) : util.format("wfc._object(%s)", argName)
        };
    }

    ["new_id"](argName, optional) {
        return {
            signature: optional ? "?n" : "n",
            jsType: "*",
            marshallGen: "wfc._newObject()"
        };
    }

    ["string"](argName, optional) {
        return {
            signature: optional ? "?s" : "s",
            jsType: optional ? "?string" : "string",
            marshallGen: optional ? util.format("wfc._stringOptional(%s)", argName) : util.format("wfc._string(%s)", argName)
        };
    }

    ["array"](argName, optional) {
        return {
            signature: optional ? "?a" : "a",
            jsType: optional ? "?ArrayBuffer" : "ArrayBuffer",
            marshallGen: optional ? util.format("wfc._arrayOptional(%s)", argName) : util.format("wfc._array(%s)", argName)
        };
    }

    _generateRequestArgs(out, req) {
        if (req.hasOwnProperty("arg")) {
            const evArgs = req.arg;
            let processedFirstArg = false;
            for (let i = 0; i < evArgs.length; i++) {
                const arg = evArgs[i];
                if (arg.$.type === "new_id") {
                    continue;
                }

                const argName = arg.$.name;
                if (processedFirstArg) {
                    out.write(", ");
                }
                out.write(argName);
                processedFirstArg = true;
            }
        }
    }

    _generateEventArgs(out, ev) {
        if (ev.hasOwnProperty("arg")) {
            const evArgs = ev.arg;
            for (let i = 0; i < evArgs.length; i++) {
                const arg = evArgs[i];
                const argName = arg.$.name;
                if (i !== 0) {
                    out.write(", ");
                }
                out.write(argName);
            }
        }
    }

    _parseEventSignature(ev) {
        let evSig = "";
        if (ev.hasOwnProperty("arg")) {
            const evArgs = ev.arg;
            for (let i = 0; i < evArgs.length; i++) {
                const arg = evArgs[i];

                const argName = arg.$.name;
                const optional = arg.$.hasOwnProperty("allow-null") && (arg.$["allow-null"] === "true");
                const argType = arg.$.type;

                evSig += this[argType](argName, optional).signature;
            }
        }

        return evSig;
    }

    _generateIfEventGlue(out, ev, opcode) {

        const evName = ev.$.name;

        out.write(util.format("\t[%d](message){\n", opcode));
        const evSig = this._parseEventSignature(ev);
        out.write(util.format("\t\tconst args = this._connection._unmarshallArgs(message,\"%s\");\n", evSig));
        out.write(util.format("\t\tthis.listener.%s.call(this.listener", evName));

        if (ev.hasOwnProperty("arg")) {
            const evArgs = ev.arg;
            for (let i = 0; i < evArgs.length; i++) {
                out.write(", ");
                const arg = evArgs[i];
                const argType = arg.$.type;
                out.write(util.format("args[%d]", i));
                if (argType === "new_id") {
                    const argItf = arg.$["interface"];
                    out.write(util.format("(\"%s\")", argItf));
                }
            }
        }

        out.write(");\n");
        out.write("\t}\n\n");
    }

    _parseItfEvent(out, itfEvent) {
        const sinceVersion = itfEvent.$.hasOwnProperty("since") ? parseInt(itfEvent.$.since) : 1;
        const evName = itfEvent.$.name;

        //function docs
        const description = itfEvent.description;
        description.forEach((val) => {
            out.write("\n\t\t\t/**\n");
            if (val.hasOwnProperty("_")) {
                val._.split("\n").forEach((line) => {
                    out.write("\t\t\t *" + line + "\n");
                });
            }

            if (itfEvent.hasOwnProperty("arg")) {
                const evArgs = itfEvent.arg;
                out.write("\t\t\t *\n");
                evArgs.forEach((arg) => {
                    const argDescription = arg.$.summary;
                    const argName = arg.$.name;
                    const optional = arg.$.hasOwnProperty("allow-null") && (arg.$["allow-null"] === "true");
                    const argType = arg.$.type;

                    out.write(util.format("\t\t\t * @param {%s} %s %s \n", this[argType](argName, optional).jsType, argName, argDescription));
                });
                out.write("\t\t\t *\n");

            }
            out.write(util.format("\t\t\t * @since %d\n", sinceVersion));
            out.write("\t\t\t *\n");
            out.write("\t\t\t */\n");
        });

        //function
        out.write(util.format("\t\t\t%s(", evName));
        this._generateEventArgs(out, itfEvent);
        out.write(") {},\n");
    }

    _parseItfRequest(out, itfRequest, opcode, itfVersion) {

        const sinceVersion = itfRequest.$.hasOwnProperty("since") ? parseInt(itfRequest.$.since) : 1;
        if (sinceVersion !== itfVersion) {
            return;
        }

        const reqName = itfRequest.$.name;

        //function docs
        const description = itfRequest.description;
        description.forEach((val) => {
            out.write("\n\t/**\n");
            if (val.hasOwnProperty("_")) {
                val._.split("\n").forEach((line) => {
                    out.write("\t *" + line + "\n");
                });
            }

            if (itfRequest.hasOwnProperty("arg")) {
                const reqArgs = itfRequest.arg;
                out.write("\t *\n");
                reqArgs.forEach((arg) => {
                    const argDescription = arg.$.summary;
                    const argName = arg.$.name;
                    const optional = arg.$.hasOwnProperty("allow-null") && (arg.$["allow-null"] === "true");
                    const argType = arg.$.type;
                    if (argType !== "new_id") {
                        out.write(util.format("\t * @param {%s} %s %s \n", this[argType](argName, optional).jsType, argName, argDescription));
                    }
                });

                reqArgs.forEach((arg) => {
                    const argDescription = arg.$.summary;
                    const argItf = arg.$["interface"];
                    const argType = arg.$.type;
                    if (argType === "new_id") {
                        out.write(util.format("\t * @return {%s} %s \n", argItf, argDescription));
                    }
                });
                out.write("\t *\n");

            }
            out.write(util.format("\t * @since %d\n", sinceVersion));
            out.write("\t *\n");
            out.write("\t */\n");
        });

        //function
        out.write(util.format("\t%s(", reqName));
        this._generateRequestArgs(out, itfRequest);
        out.write(") {\n");

        let itfName;
        //function args
        let argArray = "[";
        if (itfRequest.hasOwnProperty("arg")) {
            const reqArgs = itfRequest.arg;

            for (let i = 0; i < reqArgs.length; i++) {
                const arg = reqArgs[i];
                const argType = arg.$.type;
                const argName = arg.$.name;
                const optional = arg.$.hasOwnProperty("allow-null") && (arg.$["allow-null"] === "true");

                if (argType === "new_id") {
                    itfName = arg.$["interface"];
                }

                if (i !== 0) {
                    argArray += ", ";
                }

                argArray += this[argType](argName, optional).marshallGen;
            }
        }
        argArray += "]";

        if (itfName) {
            out.write(util.format("\t\treturn this._connection._marshallConstructor(this._id, %d, \"%s\", %s);\n", opcode, itfName, argArray));
        } else {
            out.write(util.format("\t\tthis._connection._marshall(this._id, %d, %s);\n", opcode, argArray));
        }

        out.write("\t}\n");
    }

    _parseInterface(out, protocolItf) {
        const itfName = protocolItf.$.name;
        let itfVersion = 1;

        if (protocolItf.$.hasOwnProperty("version")) {
            itfVersion = parseInt(protocolItf.$.version);
        }

        console.log(util.format("Processing interface %s v%d", itfName, itfVersion));

        for (let i = 1; i <= itfVersion; i++) {

            //class docs
            const description = protocolItf.description;
            if (description) {
                description.forEach((val) => {
                    out.write("\n/**\n");
                    if (val.hasOwnProperty("_")) {
                        val._.split("\n").forEach((line) => {
                            out.write(" *" + line + "\n");
                        });
                    }
                    out.write(" */\n");
                });
            }

            //class
            if (i === 1) {
                out.write(util.format("wfc.%s = class %s extends wfc.WObject {\n", itfName, itfName));
            } else {
                const className = util.format("%sV%d", itfName, i);
                if (i === 2) {
                    out.write(util.format("wfc.%s = class %s extends wfc.%s {\n", className, className, itfName));
                } else {
                    out.write(util.format("wfc.%s = class %s extends wfc.%sV%d {\n", className, className, itfName, i - 1));
                }
            }

            //requests
            if (protocolItf.hasOwnProperty("request")) {
                const itfRequests = protocolItf.request;
                for (let j = 0; j < itfRequests.length; j++) {
                    this._parseItfRequest(out, itfRequests[j], j + 1, i);
                }
            }

            //constructor
            out.write("\n\tconstructor(connection) {\n");
            out.write("\t\tsuper(connection, {\n");
            out.write(util.format("\t\t\tname: \"%s\",\n", itfName));
            out.write(util.format("\t\t\tversion: %d,\n", i));
            //events
            if (protocolItf.hasOwnProperty("event")) {

                const itfEvents = protocolItf.event;
                for (let j = 0; j < itfEvents.length; j++) {
                    const itfEvent = itfEvents[j];
                    let since = "1";
                    if (itfEvent.$.hasOwnProperty("since")) {
                        since = itfEvent.$.since;
                    }

                    if (parseInt(since) <= i) {
                        this._parseItfEvent(out, itfEvent);
                    }
                }
            }
            out.write("\t\t});\n");
            out.write("\t}\n\n");

            //glue event functions
            if (protocolItf.hasOwnProperty("event")) {
                const itfEvents = protocolItf.event;
                for (let j = 0; j < itfEvents.length; j++) {
                    const itfEvent = itfEvents[j];
                    let since = "1";
                    if (itfEvent.$.hasOwnProperty("since")) {
                        since = itfEvent.$.since;
                    }

                    if (parseInt(since) === i) {
                        this._generateIfEventGlue(out, itfEvent, j + 1);
                    }
                }
            }

            out.write("};\n");
        }
    }

    _parseProtocol(jsonProtocol) {
        const protocolName = jsonProtocol.protocol.$.name;
        const out = fs.createWriteStream(util.format("westfield-client-%s.js", protocolName));
        out.on('open', (fd) => {
            out.write("/*\n");
            jsonProtocol.protocol.copyright.forEach((val) => {
                val.split("\n").forEach((line) => {
                    out.write(" *" + line + "\n");
                });
            });
            out.write(" */\n");

            jsonProtocol.protocol.interface.forEach((itf) => {
                this._parseInterface(out, itf);
            });

            console.log('Done');
        });
    }

    parse() {
        fs.readFile(this.protocolFile, (err, data) => {
            if (err) throw err;
            new xml2js.Parser().parseString(data, (err, result) => {
                if (err) throw err;

                //uncomment to see the protocol as json output
                //console.log(util.inspect(result, false, null));

                this._parseProtocol(result);
            });
        });
    }

    constructor(protocolFile) {
        this.protocolFile = protocolFile;
    }
};

const cli = meow(`Usage:
        westfield-scanner.js FILE... [options]

    Generates a javascript protocol file based on the given FILE argument.
    The FILE argument is a relative or absolute path to a Westfield compatible Wayland XML.
    The generated javascript protocol file is named "westfield-client-FILE.js".

    Options:
        -h, --help         print usage information
        -v, --version      show version info and exit

`, {});

cli.input.forEach((protocol) => {
    new wfg.ProtocolParser(protocol).parse();
});
<commit_msg>update help description to make it clear we're generating client side bindings<commit_after>#!/usr/bin/env node
'use strict';

const fs = require('fs');
const util = require('util');
const xml2js = require('xml2js');
const meow = require('meow');

const wfg = {};

wfg.ProtocolParser = class {

    ["uint"](argName, optional) {
        return {
            signature: optional ? "?u" : "u",
            jsType: optional ? "?Number" : "Number",
            marshallGen: optional ? util.format("wfc._uintOptional(%s)", argName) : util.format("wfc._uint(%s)", argName)
        };
    }

    ["int"](argName, optional) {
        return {
            signature: optional ? "?i" : "i",
            jsType: optional ? "?Number" : "Number",
            marshallGen: optional ? util.format("wfc._intOptional(%s)", argName) : util.format("wfc._int(%s)", argName)
        };
    }

    ["fixed"](argName, optional) {
        return {
            signature: optional ? "?f" : "f",
            jsType: optional ? "?Fixed" : "Fixed",
            marshallGen: optional ? util.format("wfc._fixedOptional(%s)", argName) : util.format("wfc._fixed(%s)", argName)
        };
    }

    ["object"](argName, optional) {
        return {
            signature: optional ? "?o" : "o",
            jsType: optional ? "?*" : "*",
            marshallGen: optional ? util.format("wfc._objectOptional(%s)", argName) : util.format("wfc._object(%s)", argName)
        };
    }

    ["new_id"](argName, optional) {
        return {
            signature: optional ? "?n" : "n",
            jsType: "*",
            marshallGen: "wfc._newObject()"
        };
    }

    ["string"](argName, optional) {
        return {
            signature: optional ? "?s" : "s",
            jsType: optional ? "?string" : "string",
            marshallGen: optional ? util.format("wfc._stringOptional(%s)", argName) : util.format("wfc._string(%s)", argName)
        };
    }

    ["array"](argName, optional) {
        return {
            signature: optional ? "?a" : "a",
            jsType: optional ? "?ArrayBuffer" : "ArrayBuffer",
            marshallGen: optional ? util.format("wfc._arrayOptional(%s)", argName) : util.format("wfc._array(%s)", argName)
        };
    }

    _generateRequestArgs(out, req) {
        if (req.hasOwnProperty("arg")) {
            const evArgs = req.arg;
            let processedFirstArg = false;
            for (let i = 0; i < evArgs.length; i++) {
                const arg = evArgs[i];
                if (arg.$.type === "new_id") {
                    continue;
                }

                const argName = arg.$.name;
                if (processedFirstArg) {
                    out.write(", ");
                }
                out.write(argName);
                processedFirstArg = true;
            }
        }
    }

    _generateEventArgs(out, ev) {
        if (ev.hasOwnProperty("arg")) {
            const evArgs = ev.arg;
            for (let i = 0; i < evArgs.length; i++) {
                const arg = evArgs[i];
                const argName = arg.$.name;
                if (i !== 0) {
                    out.write(", ");
                }
                out.write(argName);
            }
        }
    }

    _parseEventSignature(ev) {
        let evSig = "";
        if (ev.hasOwnProperty("arg")) {
            const evArgs = ev.arg;
            for (let i = 0; i < evArgs.length; i++) {
                const arg = evArgs[i];

                const argName = arg.$.name;
                const optional = arg.$.hasOwnProperty("allow-null") && (arg.$["allow-null"] === "true");
                const argType = arg.$.type;

                evSig += this[argType](argName, optional).signature;
            }
        }

        return evSig;
    }

    _generateIfEventGlue(out, ev, opcode) {

        const evName = ev.$.name;

        out.write(util.format("\t[%d](message){\n", opcode));
        const evSig = this._parseEventSignature(ev);
        out.write(util.format("\t\tconst args = this._connection._unmarshallArgs(message,\"%s\");\n", evSig));
        out.write(util.format("\t\tthis.listener.%s.call(this.listener", evName));

        if (ev.hasOwnProperty("arg")) {
            const evArgs = ev.arg;
            for (let i = 0; i < evArgs.length; i++) {
                out.write(", ");
                const arg = evArgs[i];
                const argType = arg.$.type;
                out.write(util.format("args[%d]", i));
                if (argType === "new_id") {
                    const argItf = arg.$["interface"];
                    out.write(util.format("(\"%s\")", argItf));
                }
            }
        }

        out.write(");\n");
        out.write("\t}\n\n");
    }

    _parseItfEvent(out, itfEvent) {
        const sinceVersion = itfEvent.$.hasOwnProperty("since") ? parseInt(itfEvent.$.since) : 1;
        const evName = itfEvent.$.name;

        //function docs
        const description = itfEvent.description;
        description.forEach((val) => {
            out.write("\n\t\t\t/**\n");
            if (val.hasOwnProperty("_")) {
                val._.split("\n").forEach((line) => {
                    out.write("\t\t\t *" + line + "\n");
                });
            }

            if (itfEvent.hasOwnProperty("arg")) {
                const evArgs = itfEvent.arg;
                out.write("\t\t\t *\n");
                evArgs.forEach((arg) => {
                    const argDescription = arg.$.summary;
                    const argName = arg.$.name;
                    const optional = arg.$.hasOwnProperty("allow-null") && (arg.$["allow-null"] === "true");
                    const argType = arg.$.type;

                    out.write(util.format("\t\t\t * @param {%s} %s %s \n", this[argType](argName, optional).jsType, argName, argDescription));
                });
                out.write("\t\t\t *\n");

            }
            out.write(util.format("\t\t\t * @since %d\n", sinceVersion));
            out.write("\t\t\t *\n");
            out.write("\t\t\t */\n");
        });

        //function
        out.write(util.format("\t\t\t%s(", evName));
        this._generateEventArgs(out, itfEvent);
        out.write(") {},\n");
    }

    _parseItfRequest(out, itfRequest, opcode, itfVersion) {

        const sinceVersion = itfRequest.$.hasOwnProperty("since") ? parseInt(itfRequest.$.since) : 1;
        if (sinceVersion !== itfVersion) {
            return;
        }

        const reqName = itfRequest.$.name;

        //function docs
        const description = itfRequest.description;
        description.forEach((val) => {
            out.write("\n\t/**\n");
            if (val.hasOwnProperty("_")) {
                val._.split("\n").forEach((line) => {
                    out.write("\t *" + line + "\n");
                });
            }

            if (itfRequest.hasOwnProperty("arg")) {
                const reqArgs = itfRequest.arg;
                out.write("\t *\n");
                reqArgs.forEach((arg) => {
                    const argDescription = arg.$.summary;
                    const argName = arg.$.name;
                    const optional = arg.$.hasOwnProperty("allow-null") && (arg.$["allow-null"] === "true");
                    const argType = arg.$.type;
                    if (argType !== "new_id") {
                        out.write(util.format("\t * @param {%s} %s %s \n", this[argType](argName, optional).jsType, argName, argDescription));
                    }
                });

                reqArgs.forEach((arg) => {
                    const argDescription = arg.$.summary;
                    const argItf = arg.$["interface"];
                    const argType = arg.$.type;
                    if (argType === "new_id") {
                        out.write(util.format("\t * @return {%s} %s \n", argItf, argDescription));
                    }
                });
                out.write("\t *\n");

            }
            out.write(util.format("\t * @since %d\n", sinceVersion));
            out.write("\t *\n");
            out.write("\t */\n");
        });

        //function
        out.write(util.format("\t%s(", reqName));
        this._generateRequestArgs(out, itfRequest);
        out.write(") {\n");

        let itfName;
        //function args
        let argArray = "[";
        if (itfRequest.hasOwnProperty("arg")) {
            const reqArgs = itfRequest.arg;

            for (let i = 0; i < reqArgs.length; i++) {
                const arg = reqArgs[i];
                const argType = arg.$.type;
                const argName = arg.$.name;
                const optional = arg.$.hasOwnProperty("allow-null") && (arg.$["allow-null"] === "true");

                if (argType === "new_id") {
                    itfName = arg.$["interface"];
                }

                if (i !== 0) {
                    argArray += ", ";
                }

                argArray += this[argType](argName, optional).marshallGen;
            }
        }
        argArray += "]";

        if (itfName) {
            out.write(util.format("\t\treturn this._connection._marshallConstructor(this._id, %d, \"%s\", %s);\n", opcode, itfName, argArray));
        } else {
            out.write(util.format("\t\tthis._connection._marshall(this._id, %d, %s);\n", opcode, argArray));
        }

        out.write("\t}\n");
    }

    _parseInterface(out, protocolItf) {
        const itfName = protocolItf.$.name;
        let itfVersion = 1;

        if (protocolItf.$.hasOwnProperty("version")) {
            itfVersion = parseInt(protocolItf.$.version);
        }

        console.log(util.format("Processing interface %s v%d", itfName, itfVersion));

        for (let i = 1; i <= itfVersion; i++) {

            //class docs
            const description = protocolItf.description;
            if (description) {
                description.forEach((val) => {
                    out.write("\n/**\n");
                    if (val.hasOwnProperty("_")) {
                        val._.split("\n").forEach((line) => {
                            out.write(" *" + line + "\n");
                        });
                    }
                    out.write(" */\n");
                });
            }

            //class
            if (i === 1) {
                out.write(util.format("wfc.%s = class %s extends wfc.WObject {\n", itfName, itfName));
            } else {
                const className = util.format("%sV%d", itfName, i);
                if (i === 2) {
                    out.write(util.format("wfc.%s = class %s extends wfc.%s {\n", className, className, itfName));
                } else {
                    out.write(util.format("wfc.%s = class %s extends wfc.%sV%d {\n", className, className, itfName, i - 1));
                }
            }

            //requests
            if (protocolItf.hasOwnProperty("request")) {
                const itfRequests = protocolItf.request;
                for (let j = 0; j < itfRequests.length; j++) {
                    this._parseItfRequest(out, itfRequests[j], j + 1, i);
                }
            }

            //constructor
            out.write("\n\tconstructor(connection) {\n");
            out.write("\t\tsuper(connection, {\n");
            out.write(util.format("\t\t\tname: \"%s\",\n", itfName));
            out.write(util.format("\t\t\tversion: %d,\n", i));
            //events
            if (protocolItf.hasOwnProperty("event")) {

                const itfEvents = protocolItf.event;
                for (let j = 0; j < itfEvents.length; j++) {
                    const itfEvent = itfEvents[j];
                    let since = "1";
                    if (itfEvent.$.hasOwnProperty("since")) {
                        since = itfEvent.$.since;
                    }

                    if (parseInt(since) <= i) {
                        this._parseItfEvent(out, itfEvent);
                    }
                }
            }
            out.write("\t\t});\n");
            out.write("\t}\n\n");

            //glue event functions
            if (protocolItf.hasOwnProperty("event")) {
                const itfEvents = protocolItf.event;
                for (let j = 0; j < itfEvents.length; j++) {
                    const itfEvent = itfEvents[j];
                    let since = "1";
                    if (itfEvent.$.hasOwnProperty("since")) {
                        since = itfEvent.$.since;
                    }

                    if (parseInt(since) === i) {
                        this._generateIfEventGlue(out, itfEvent, j + 1);
                    }
                }
            }

            out.write("};\n");
        }
    }

    _parseProtocol(jsonProtocol) {
        const protocolName = jsonProtocol.protocol.$.name;
        const out = fs.createWriteStream(util.format("westfield-client-%s.js", protocolName));
        out.on('open', (fd) => {
            out.write("/*\n");
            jsonProtocol.protocol.copyright.forEach((val) => {
                val.split("\n").forEach((line) => {
                    out.write(" *" + line + "\n");
                });
            });
            out.write(" */\n");

            jsonProtocol.protocol.interface.forEach((itf) => {
                this._parseInterface(out, itf);
            });

            console.log('Done');
        });
    }

    parse() {
        fs.readFile(this.protocolFile, (err, data) => {
            if (err) throw err;
            new xml2js.Parser().parseString(data, (err, result) => {
                if (err) throw err;

                //uncomment to see the protocol as json output
                //console.log(util.inspect(result, false, null));

                this._parseProtocol(result);
            });
        });
    }

    constructor(protocolFile) {
        this.protocolFile = protocolFile;
    }
};

const cli = meow(`Usage:
        westfield-scanner.js FILE... [options]

    Generates a javascript client-side protocol file based on the given FILE argument.
    The FILE argument is a relative or absolute path to a Westfield compatible Wayland XML.
    The generated javascript protocol file is named "westfield-client-FILE.js".

    Options:
        -h, --help         print usage information
        -v, --version      show version info and exit

`, {});

cli.input.forEach((protocol) => {
    new wfg.ProtocolParser(protocol).parse();
});
<|endoftext|>


<commit_before>import external from '../externalModules.js';
import BaseTool from './base/BaseTool.js';
import { clipToBox } from '../util/clip.js';
import zoomUtils from '../util/zoom/index.js';

const { correctShift, changeViewportScale } = zoomUtils;

/**
 * @public
 * @class ZoomTool
 * @memberof Tools
 *
 * @classdesc Tool for changing magnification.
 * @extends Tools.Base.BaseTool
 */
export default class ZoomTool extends BaseTool {
  constructor(configuration = {}) {
    const defaultConfig = {
      name: 'Zoom',
      strategies: {
        default: defaultStrategy,
        translate: translateStrategy,
        zoomToCenter: zoomToCenterStrategy,
      },
      defaultStrategy: 'default',
      supportedInteractionTypes: ['Mouse', 'Touch'],
      configuration: {
        invert: false,
        preventZoomOutsideImage: false,
        minScale: 0.25,
        maxScale: 20.0,
      },
    };
    const initialConfiguration = Object.assign(defaultConfig, configuration);

    super(initialConfiguration);

    this.initialConfiguration = initialConfiguration;
  }

  touchDragCallback(evt) {
    dragCallback.call(this, evt);
  }

  mouseDragCallback(evt) {
    dragCallback.call(this, evt);
  }
}

const dragCallback = function(evt) {
  const deltaY = evt.detail.deltaPoints.page.y;

  if (!deltaY) {
    return false;
  }

  this.applyActiveStrategy(evt, this.configuration);
  external.cornerstone.setViewport(evt.detail.element, evt.detail.viewport);
};

/**
 * The default strategy keeps the target location fixed on the page
 * as we zoom in/out.
 *
 * @param {*} evt
 * @param {*} { invert, maxScale, minScale }
 * @returns {void}
 */
function defaultStrategy(evt, { invert, maxScale, minScale }) {
  const deltaY = evt.detail.deltaPoints.page.y;
  const ticks = invert ? -deltaY / 100 : deltaY / 100;
  const { element, viewport } = evt.detail;
  const [startX, startY, imageX, imageY] = [
    evt.detail.startPoints.page.x,
    evt.detail.startPoints.page.y,
    evt.detail.startPoints.image.x,
    evt.detail.startPoints.image.y,
  ];

  // Calculate the new scale factor based on how far the mouse has changed
  const updatedViewport = changeViewportScale(viewport, ticks, {
    maxScale,
    minScale,
  });

  external.cornerstone.setViewport(element, updatedViewport);

  // Now that the scale has been updated, determine the offset we need to apply to the center so we can
  // Keep the original start location in the same position
  const newCoords = external.cornerstone.pageToPixel(element, startX, startY);

  // The shift we will use is the difference between the original image coordinates of the point we've selected
  // And the image coordinates of the same point on the page after the viewport scaling above has been performed
  // This shift is in image coordinates, and is designed to keep the target location fixed on the page.
  let shift = {
    x: imageX - newCoords.x,
    y: imageY - newCoords.y,
  };

  // Correct the required shift using the viewport rotation and flip parameters
  shift = correctShift(shift, updatedViewport);

  // Apply the shift to the Viewport's translation setting
  viewport.translation.x -= shift.x;
  viewport.translation.y -= shift.y;
}

function translateStrategy(
  evt,
  { invert, preventZoomOutsideImage, maxScale, minScale }
) {
  const deltaY = evt.detail.deltaPoints.page.y;
  const ticks = invert ? -deltaY / 100 : deltaY / 100;
  const image = evt.detail.image;
  const viewport = evt.detail.viewport;
  const [startX, startY] = [
    evt.detail.startPoints.image.x,
    evt.detail.startPoints.image.y,
  ];

  // Calculate the new scale factor based on how far the mouse has changed
  // Note that in this case we don't need to update the viewport after the initial
  // Zoom step since we aren't don't intend to keep the target position static on
  // The page
  const updatedViewport = changeViewportScale(viewport, ticks, {
    maxScale,
    minScale,
  });

  // Define the default shift to take place during this zoom step
  const shift = {
    x: 0,
    y: 0,
  };

  // Define the parameters for the translate strategy
  const translateSpeed = 8;
  const outwardsMinScaleToTranslate = 3;
  const minTranslation = 0.01;

  if (ticks < 0) {
    // Zoom outwards from the image center
    if (updatedViewport.scale < outwardsMinScaleToTranslate) {
      // If the current translation is smaller than the minimum desired translation,
      // Set the translation to zero
      if (Math.abs(updatedViewport.translation.x) < minTranslation) {
        updatedViewport.translation.x = 0;
      } else {
        shift.x = updatedViewport.translation.x / translateSpeed;
      }

      // If the current translation is smaller than the minimum desired translation,
      // Set the translation to zero
      if (Math.abs(updatedViewport.translation.y) < minTranslation) {
        updatedViewport.translation.y = 0;
      } else {
        shift.y = updatedViewport.translation.y / translateSpeed;
      }
    }
  } else {
    // Zoom inwards to the current image point

    // Identify the coordinates of the point the user is trying to zoom into
    // If we are not allowed to zoom outside the image, bound the user-selected position to
    // A point inside the image
    if (preventZoomOutsideImage) {
      clipToBox(evt.detail.startPoints.image, image);
    }

    // Calculate the translation value that would place the desired image point in the center
    // Of the viewport
    let desiredTranslation = {
      x: image.width / 2 - startX,
      y: image.height / 2 - startY,
    };

    // Correct the target location using the viewport rotation and flip parameters
    desiredTranslation = correctShift(desiredTranslation, updatedViewport);

    // Calculate the difference between the current viewport translation value and the
    // Final desired translation values
    const distanceToDesired = {
      x: updatedViewport.translation.x - desiredTranslation.x,
      y: updatedViewport.translation.y - desiredTranslation.y,
    };

    // If the current translation is smaller than the minimum desired translation,
    // Stop translating in the x-direction
    if (Math.abs(distanceToDesired.x) < minTranslation) {
      updatedViewport.translation.x = desiredTranslation.x;
    } else {
      // Otherwise, shift the viewport by one step
      shift.x = distanceToDesired.x / translateSpeed;
    }

    // If the current translation is smaller than the minimum desired translation,
    // Stop translating in the y-direction
    if (Math.abs(distanceToDesired.y) < minTranslation) {
      updatedViewport.translation.y = desiredTranslation.y;
    } else {
      // Otherwise, shift the viewport by one step
      shift.y = distanceToDesired.y / translateSpeed;
    }
  }

  // Apply the shift to the Viewport's translation setting
  updatedViewport.translation.x -= shift.x;
  updatedViewport.translation.y -= shift.y;
}

function zoomToCenterStrategy(evt, { invert, maxScale, minScale }) {
  const deltaY = evt.detail.deltaPoints.page.y;
  const ticks = invert ? -deltaY / 100 : deltaY / 100;
  const viewport = evt.detail.viewport;

  // Calculate the new scale factor based on how far the mouse has changed
  changeViewportScale(viewport, ticks, {
    maxScale,
    minScale,
  });
}
<commit_msg>add Zoom Cursor.<commit_after>import external from '../externalModules.js';
import BaseTool from './base/BaseTool.js';
import MouseCursor from '../util/MouseCursor.js';
import { clipToBox } from '../util/clip.js';
import zoomUtils from '../util/zoom/index.js';

const { correctShift, changeViewportScale } = zoomUtils;

/**
 * @public
 * @class ZoomTool
 * @memberof Tools
 *
 * @classdesc Tool for changing magnification.
 * @extends Tools.Base.BaseTool
 */
export default class ZoomTool extends BaseTool {
  constructor(configuration = {}) {
    const defaultConfig = {
      name: 'Zoom',
      strategies: {
        default: defaultStrategy,
        translate: translateStrategy,
        zoomToCenter: zoomToCenterStrategy,
      },
      defaultStrategy: 'default',
      supportedInteractionTypes: ['Mouse', 'Touch'],
      configuration: {
        invert: false,
        preventZoomOutsideImage: false,
        minScale: 0.25,
        maxScale: 20.0,
      },
    };
    const initialConfiguration = Object.assign(defaultConfig, configuration);

    super(initialConfiguration);

    this.initialConfiguration = initialConfiguration;

    this.configuration.svgCursor = this.configuration.svgCursor || zoomCursor;
  }

  touchDragCallback(evt) {
    dragCallback.call(this, evt);
  }

  mouseDragCallback(evt) {
    dragCallback.call(this, evt);
  }
}

const dragCallback = function(evt) {
  const deltaY = evt.detail.deltaPoints.page.y;

  if (!deltaY) {
    return false;
  }

  this.applyActiveStrategy(evt, this.configuration);
  external.cornerstone.setViewport(evt.detail.element, evt.detail.viewport);
};

/**
 * The default strategy keeps the target location fixed on the page
 * as we zoom in/out.
 *
 * @param {*} evt
 * @param {*} { invert, maxScale, minScale }
 * @returns {void}
 */
function defaultStrategy(evt, { invert, maxScale, minScale }) {
  const deltaY = evt.detail.deltaPoints.page.y;
  const ticks = invert ? -deltaY / 100 : deltaY / 100;
  const { element, viewport } = evt.detail;
  const [startX, startY, imageX, imageY] = [
    evt.detail.startPoints.page.x,
    evt.detail.startPoints.page.y,
    evt.detail.startPoints.image.x,
    evt.detail.startPoints.image.y,
  ];

  // Calculate the new scale factor based on how far the mouse has changed
  const updatedViewport = changeViewportScale(viewport, ticks, {
    maxScale,
    minScale,
  });

  external.cornerstone.setViewport(element, updatedViewport);

  // Now that the scale has been updated, determine the offset we need to apply to the center so we can
  // Keep the original start location in the same position
  const newCoords = external.cornerstone.pageToPixel(element, startX, startY);

  // The shift we will use is the difference between the original image coordinates of the point we've selected
  // And the image coordinates of the same point on the page after the viewport scaling above has been performed
  // This shift is in image coordinates, and is designed to keep the target location fixed on the page.
  let shift = {
    x: imageX - newCoords.x,
    y: imageY - newCoords.y,
  };

  // Correct the required shift using the viewport rotation and flip parameters
  shift = correctShift(shift, updatedViewport);

  // Apply the shift to the Viewport's translation setting
  viewport.translation.x -= shift.x;
  viewport.translation.y -= shift.y;
}

function translateStrategy(
  evt,
  { invert, preventZoomOutsideImage, maxScale, minScale }
) {
  const deltaY = evt.detail.deltaPoints.page.y;
  const ticks = invert ? -deltaY / 100 : deltaY / 100;
  const image = evt.detail.image;
  const viewport = evt.detail.viewport;
  const [startX, startY] = [
    evt.detail.startPoints.image.x,
    evt.detail.startPoints.image.y,
  ];

  // Calculate the new scale factor based on how far the mouse has changed
  // Note that in this case we don't need to update the viewport after the initial
  // Zoom step since we aren't don't intend to keep the target position static on
  // The page
  const updatedViewport = changeViewportScale(viewport, ticks, {
    maxScale,
    minScale,
  });

  // Define the default shift to take place during this zoom step
  const shift = {
    x: 0,
    y: 0,
  };

  // Define the parameters for the translate strategy
  const translateSpeed = 8;
  const outwardsMinScaleToTranslate = 3;
  const minTranslation = 0.01;

  if (ticks < 0) {
    // Zoom outwards from the image center
    if (updatedViewport.scale < outwardsMinScaleToTranslate) {
      // If the current translation is smaller than the minimum desired translation,
      // Set the translation to zero
      if (Math.abs(updatedViewport.translation.x) < minTranslation) {
        updatedViewport.translation.x = 0;
      } else {
        shift.x = updatedViewport.translation.x / translateSpeed;
      }

      // If the current translation is smaller than the minimum desired translation,
      // Set the translation to zero
      if (Math.abs(updatedViewport.translation.y) < minTranslation) {
        updatedViewport.translation.y = 0;
      } else {
        shift.y = updatedViewport.translation.y / translateSpeed;
      }
    }
  } else {
    // Zoom inwards to the current image point

    // Identify the coordinates of the point the user is trying to zoom into
    // If we are not allowed to zoom outside the image, bound the user-selected position to
    // A point inside the image
    if (preventZoomOutsideImage) {
      clipToBox(evt.detail.startPoints.image, image);
    }

    // Calculate the translation value that would place the desired image point in the center
    // Of the viewport
    let desiredTranslation = {
      x: image.width / 2 - startX,
      y: image.height / 2 - startY,
    };

    // Correct the target location using the viewport rotation and flip parameters
    desiredTranslation = correctShift(desiredTranslation, updatedViewport);

    // Calculate the difference between the current viewport translation value and the
    // Final desired translation values
    const distanceToDesired = {
      x: updatedViewport.translation.x - desiredTranslation.x,
      y: updatedViewport.translation.y - desiredTranslation.y,
    };

    // If the current translation is smaller than the minimum desired translation,
    // Stop translating in the x-direction
    if (Math.abs(distanceToDesired.x) < minTranslation) {
      updatedViewport.translation.x = desiredTranslation.x;
    } else {
      // Otherwise, shift the viewport by one step
      shift.x = distanceToDesired.x / translateSpeed;
    }

    // If the current translation is smaller than the minimum desired translation,
    // Stop translating in the y-direction
    if (Math.abs(distanceToDesired.y) < minTranslation) {
      updatedViewport.translation.y = desiredTranslation.y;
    } else {
      // Otherwise, shift the viewport by one step
      shift.y = distanceToDesired.y / translateSpeed;
    }
  }

  // Apply the shift to the Viewport's translation setting
  updatedViewport.translation.x -= shift.x;
  updatedViewport.translation.y -= shift.y;
}

function zoomToCenterStrategy(evt, { invert, maxScale, minScale }) {
  const deltaY = evt.detail.deltaPoints.page.y;
  const ticks = invert ? -deltaY / 100 : deltaY / 100;
  const viewport = evt.detail.viewport;

  // Calculate the new scale factor based on how far the mouse has changed
  changeViewportScale(viewport, ticks, {
    maxScale,
    minScale,
  });
}

const zoomCursor = new MouseCursor(
  `<svg
    data-icon="zoom" role="img" xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 640 512" width="40" height="32"
  >
    <path fill="#ffffff" d="M508.5 481.6l-129-129c-2.3-2.3-5.3-3.5-8.5-3.5h-10.3C395
      312 416 262.5 416 208 416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c54.5
      0 104-21 141.1-55.2V371c0 3.2 1.3 6.2 3.5 8.5l129 129c4.7 4.7 12.3 4.7 17
      0l9.9-9.9c4.7-4.7 4.7-12.3 0-17zM208 384c-97.3 0-176-78.7-176-176S110.7 32 208
      32s176 78.7 176 176-78.7 176-176 176z"
    />
    <path fill="#ffffff" transform="scale(0.22,0.22) translate(1400,0)" d="M1216
      320q0 26-19 45t-45 19h-128v1024h128q26 0 45 19t19 45-19 45l-256 256q-19
      19-45 19t-45-19l-256-256q-19-19-19-45t19-45 45-19h128v-1024h-128q-26
      0-45-19t-19-45 19-45l256-256q19-19 45-19t45 19l256 256q19 19 19 45z"
    />
  </svg>`,
  'topLeft'
);
<|endoftext|>


<commit_before>'use strict';
var util = require('util'),
    shelljs = require('shelljs'),
    generators = require('yeoman-generator'),
    chalk = require('chalk'),
    jhuml = require('jhipster-uml'),
    jdl = require('jhipster-domain-language'),
    scriptBase = require('../generator-base');

var JDLGenerator = generators.Base.extend({});

util.inherits(JDLGenerator, scriptBase);

module.exports = JDLGenerator.extend({
    constructor: function () {
        generators.Base.apply(this, arguments);
        this.argument('jdlFiles', {type: Array, required: true});

    },

    initializing: {
        validate: function () {
            this.jdlFiles && this.jdlFiles.forEach(function (key) {
                if (!shelljs.test('-f', key)) {
                    this.env.error(chalk.red('\nCould not find ' + key + ', make sure the path is correct!\n'));
                }
            }, this);
        },

        getConfig: function () {
            this.log('The jdl is being imported.');
            this.baseName = this.config.get('baseName');
            this.databaseType = this.config.get('databaseType');
        }
    },

    _filterScheduledClasses: function(classToFilter, scheduledClasses) {
        return scheduledClasses.filter(function(element) {
            return element !== classToFilter;
        });
    },

    _initDatabaseTypeHolder: function(databaseTypeName) {
        switch (databaseTypeName) {
        case 'mongodb':
            return jhuml.MongoDBTypes;
        case 'cassandra':
            return jhuml.CassandraTypes;
        default:
            return jhuml.SQLTypes;
        }
    },
    default: {
        insight: function () {
            var insight = this.insight();
            insight.trackWithEvent('generator', 'import-jdl');
        },

        parseJDL: function () {

            var Editors = jhuml.editors;
            var EntitiesCreator = jhuml.EntitiesCreator;
            var ClassScheduler = jhuml.ClassScheduler;

            var types = this._initDatabaseTypeHolder(this.databaseType);

            var parser = new Editors.Parsers.dsl(jdl.parseFromFiles(this.jdlFiles), types);
            var parsedData = parser.parse();
            var scheduler = new ClassScheduler(
                Object.keys(parsedData.classes),
                parsedData.associations
            );

            var scheduledClasses = scheduler.schedule();
            if (parsedData.userClassId) {
                scheduledClasses =
                this._filterScheduledClasses(parsedData.userClassId, scheduledClasses);
            }

            var creator = new EntitiesCreator(
                parsedData,
                parser.databaseTypes,
                [], {}, {}
            );

            creator.createEntities();
            if (!this.options['force']) {
                scheduledClasses = creator.filterOutUnchangedEntities(scheduledClasses);
            }
            creator.writeJSON(scheduledClasses);
        },

        generateEntities: function () {
            this.getExistingEntities().forEach(function (entity) {
                this.composeWith('jhipster:entity', {
                    options: {
                        regenerate: true,
                        'skip-install': true
                    },
                    args: [entity.name]
                }, {
                    local: require.resolve('../entity')
                });
            }, this);
        }
    },

    install: function () {
        var injectJsFilesToIndex = function () {
            this.log('\n' + chalk.bold.green('Running gulp Inject to add javascript to index\n'));
            this.spawnCommand('gulp', ['inject']);
        };
        if (!this.options['skip-install'] && !this.skipClient) {
            injectJsFilesToIndex.call(this);
        }
    }

});
<commit_msg>update logs<commit_after>'use strict';
var util = require('util'),
    shelljs = require('shelljs'),
    generators = require('yeoman-generator'),
    chalk = require('chalk'),
    jhuml = require('jhipster-uml'),
    jdl = require('jhipster-domain-language'),
    scriptBase = require('../generator-base');

var JDLGenerator = generators.Base.extend({});

util.inherits(JDLGenerator, scriptBase);

module.exports = JDLGenerator.extend({
    constructor: function () {
        generators.Base.apply(this, arguments);
        this.argument('jdlFiles', {type: Array, required: true});

    },

    initializing: {
        validate: function () {
            this.jdlFiles && this.jdlFiles.forEach(function (key) {
                if (!shelljs.test('-f', key)) {
                    this.env.error(chalk.red('\nCould not find ' + key + ', make sure the path is correct!\n'));
                }
            }, this);
        },

        getConfig: function () {
            this.baseName = this.config.get('baseName');
            this.databaseType = this.config.get('databaseType');
        }
    },

    _filterScheduledClasses: function(classToFilter, scheduledClasses) {
        return scheduledClasses.filter(function(element) {
            return element !== classToFilter;
        });
    },

    _initDatabaseTypeHolder: function(databaseTypeName) {
        switch (databaseTypeName) {
        case 'mongodb':
            return jhuml.MongoDBTypes;
        case 'cassandra':
            return jhuml.CassandraTypes;
        default:
            return jhuml.SQLTypes;
        }
    },
    default: {
        insight: function () {
            var insight = this.insight();
            insight.trackWithEvent('generator', 'import-jdl');
        },

        parseJDL: function () {
            this.log('The jdl is being parsed.');
            var Editors = jhuml.editors;
            var EntitiesCreator = jhuml.EntitiesCreator;
            var ClassScheduler = jhuml.ClassScheduler;

            var types = this._initDatabaseTypeHolder(this.databaseType);

            var parser = new Editors.Parsers.dsl(jdl.parseFromFiles(this.jdlFiles), types);
            var parsedData = parser.parse();
            var scheduler = new ClassScheduler(
                Object.keys(parsedData.classes),
                parsedData.associations
            );

            var scheduledClasses = scheduler.schedule();
            if (parsedData.userClassId) {
                scheduledClasses =
                this._filterScheduledClasses(parsedData.userClassId, scheduledClasses);
            }

            var creator = new EntitiesCreator(
                parsedData,
                parser.databaseTypes,
                [], {}, {}
            );

            creator.createEntities();
            if (!this.options['force']) {
                scheduledClasses = creator.filterOutUnchangedEntities(scheduledClasses);
            }
            this.log('Writing entity JSON files.');
            creator.writeJSON(scheduledClasses);
        },

        generateEntities: function () {
            this.log('Generating entities.');
            this.getExistingEntities().forEach(function (entity) {
                this.composeWith('jhipster:entity', {
                    options: {
                        regenerate: true,
                        'skip-install': true
                    },
                    args: [entity.name]
                }, {
                    local: require.resolve('../entity')
                });
            }, this);
        }
    },

    install: function () {
        var injectJsFilesToIndex = function () {
            this.log('\n' + chalk.bold.green('Running gulp Inject to add javascript to index\n'));
            this.spawnCommand('gulp', ['inject']);
        };
        if (!this.options['skip-install'] && !this.skipClient) {
            injectJsFilesToIndex.call(this);
        }
    }

});
<|endoftext|>