<commit_before>/* -------------------------------------------------------------------------------------------------

Build Configuration
Contributors: Luan Gjokaj

-------------------------------------------------------------------------------------------------- */
'use strict';
var babel = require('gulp-babel');
var browserSync = require('browser-sync');
var concat = require('gulp-concat');
var connect = require('gulp-connect-php');
var cssnano = require('cssnano');
var cssnext = require('postcss-cssnext');
var del = require('del');
var fs = require('fs');
var gulp = require('gulp');
var gutil = require('gulp-util');
var inject = require('gulp-inject-string');
var partialimport = require('postcss-easy-import');
var plumber = require('gulp-plumber');
var postcss = require('gulp-postcss');
var remoteSrc = require('gulp-remote-src');
var sourcemaps = require('gulp-sourcemaps');
var uglify = require('gulp-uglify');
var unzip = require('gulp-unzip');
var zip = require('gulp-zip');
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
PostCSS Plugins
-------------------------------------------------------------------------------------------------- */
var pluginsDev = [
	partialimport,
	cssnext({
		features: {
			colorHexAlpha: false
		}
	})
];
var pluginsProd = [
	partialimport,
	cssnext({
		features: {
			colorHexAlpha: false
		}
	})
];
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
Header & Footer JavaScript Boundles
-------------------------------------------------------------------------------------------------- */
var headerJS = [
	'node_modules/jquery/dist/jquery.js',
	'node_modules/nprogress/nprogress.js',
	'node_modules/aos/dist/aos.js',
	'node_modules/isotope-layout/dist/isotope.pkgd.js'
];
var footerJS = [
	'src/js/**'
];
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
Theme Name
-------------------------------------------------------------------------------------------------- */
var themeName = 'wordpressify';
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
Installation Tasks
-------------------------------------------------------------------------------------------------- */
gulp.task('default');

gulp.task('cleanup', function () {
	del(['build/**']);
	del(['dist/**']);
});

gulp.task('download-wordpress', function () {
	remoteSrc(['latest.zip'], {
		base: 'https://wordpress.org/'
	})
		.pipe(gulp.dest('build/'));
});

gulp.task('setup', [
	'unzip-wordpress',
	'copy-config'
]);

gulp.task('unzip-wordpress', function () {
	gulp.src('build/latest.zip')
		.pipe(unzip())
		.pipe(gulp.dest('build/'))
});

gulp.task('copy-config', function () {
	gulp.src('wp-config.php')
		.pipe(gulp.dest('build/wordpress'));
});

gulp.task('disable-cron', function () {
	gulp.src('build/wordpress/wp-config.php')
		.pipe(inject.after('define(\'DB_COLLATE\', \'\');', '\ndefine(\'DISABLE_WP_CRON\', true);'))
		.pipe(gulp.dest('build/wordpress'))
		.on('end', function () {
			gutil.beep();
			console.log(devServerReady);
			console.log(thankYou);
		});
});
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
Development Tasks
-------------------------------------------------------------------------------------------------- */
gulp.task('build-dev', [
	'copy-theme-dev',
	'copy-fonts-dev',
	'style-dev',
	'header-scripts-dev',
	'footer-scripts-dev',
	'watch'

], function () {
	connect.server({
		base: 'build/wordpress',
		port: '3020'
	}, function () {
		browserSync({
			proxy: '127.0.0.1:3020'
		});
	});
});

gulp.task('copy-theme-dev', function () {
	if (!fs.existsSync('./build')) {
		console.log(buildNotFound);
		process.exit(1);
	} else {
		gulp.src('src/theme/**')
			.pipe(gulp.dest('build/wordpress/wp-content/themes/' + themeName));
	}
});

gulp.task('copy-fonts-dev', function () {
	gulp.src('src/fonts/**')
		.pipe(gulp.dest('build/wordpress/wp-content/themes/' + themeName + '/fonts'))
});

gulp.task('style-dev', function () {
	return gulp.src('src/style/style.css')
		.pipe(plumber({ errorHandler: onError }))
		.pipe(sourcemaps.init())
		.pipe(postcss(pluginsDev))
		.pipe(sourcemaps.write('.'))
		.pipe(gulp.dest('build/wordpress/wp-content/themes/' + themeName))
		.pipe(browserSync.stream({ match: '**/*.css' }));
});

gulp.task('header-scripts-dev', function () {
	return gulp.src(headerJS)
		.pipe(plumber({ errorHandler: onError }))
		.pipe(sourcemaps.init())
		.pipe(concat('header-bundle.js'))
		.pipe(sourcemaps.write('.'))
		.pipe(gulp.dest('build/wordpress/wp-content/themes/' + themeName + '/js'));
});

gulp.task('footer-scripts-dev', function () {
	return gulp.src(footerJS)
		.pipe(plumber({ errorHandler: onError }))
		.pipe(sourcemaps.init())
		.pipe(babel({
			presets: ['env']
		}))
		.pipe(concat('footer-bundle.js'))
		.pipe(sourcemaps.write('.'))
		.pipe(gulp.dest('build/wordpress/wp-content/themes/' + themeName + '/js'));
});

gulp.task('reload-js', ['footer-scripts-dev', 'header-scripts-dev'], function (done) {
	browserSync.reload();
	done();
});

gulp.task('reload-fonts', ['copy-fonts-dev'], function (done) {
	browserSync.reload();
	done();
});

gulp.task('reload-theme', ['copy-theme-dev'], function (done) {
	browserSync.reload();
	done();
});

gulp.task('watch', function () {
	gulp.watch(['src/style/**/*.css'], ['style-dev']);
	gulp.watch(['src/js/**'], ['reload-js']);
	gulp.watch(['src/fonts/**'], ['reload-fonts']);
	gulp.watch(['src/theme/**'], ['reload-theme']);
});
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
Production Tasks
-------------------------------------------------------------------------------------------------- */
gulp.task('build-prod', [
	'copy-theme-prod',
	'copy-fonts-prod',
	'style-prod',
	'header-scripts-prod',
	'footer-scripts-prod',
	'zip-theme'
], function () {
	gutil.beep();
	console.log(filesGenerated);
	console.log(thankYou);
});

gulp.task('copy-theme-prod', function () {
	gulp.src('src/theme/**')
		.pipe(gulp.dest('dist/themes/' + themeName))
});

gulp.task('copy-fonts-prod', function () {
	gulp.src('src/fonts/**')
		.pipe(gulp.dest('dist/themes/' + themeName + '/fonts'))
});

gulp.task('style-prod', function () {
	return gulp.src('src/style/style.css')
		.pipe(plumber({ errorHandler: onError }))
		.pipe(postcss(pluginsProd))
		.pipe(gulp.dest('dist/themes/' + themeName))
});

gulp.task('header-scripts-prod', function () {
	return gulp.src(headerJS)
		.pipe(plumber({ errorHandler: onError }))
		.pipe(concat('header-bundle.js'))
		.pipe(uglify())
		.pipe(gulp.dest('dist/themes/' + themeName + '/js'));
});

gulp.task('footer-scripts-prod', function () {
	return gulp.src(footerJS)
		.pipe(plumber({ errorHandler: onError }))
		.pipe(babel({
			presets: ['env']
		}))
		.pipe(concat('footer-bundle.js'))
		.pipe(uglify())
		.pipe(gulp.dest('dist/themes/' + themeName + '/js'));
});

gulp.task('zip-theme', ['copy-theme-prod', 'copy-fonts-prod', 'style-prod', 'header-scripts-prod', 'footer-scripts-prod'], function () {
	gulp.src('dist/themes/' + themeName + '/**')
		.pipe(zip(themeName + '.zip'))
		.pipe(gulp.dest('dist'))
});
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
Utilitie Tasks
 ------------------------------------------------------------------------------------------------- */
var onError = function (err) {
	gutil.beep();
	console.log(wpFy + ' - ' + errorMsg + ' ' + err.toString());
	this.emit('end');
};

var date = new Date().toLocaleDateString('en-GB').replace(/\//g, '.');
var devServerReady = 'Your development server is ready, start the workflow with the command: $ \x1b[1mnpm run dev\x1b[0m';
var buildNotFound = '⚠　- You need to install WordPress first. Run the command: $ \x1b[1mnpm run install:wordpress\x1b[0m';
var filesGenerated = 'Your ZIP template file was generated in: \x1b[1m' + __dirname + '/dist/' + themeName + '.zip\x1b[0m - ✅';
var backupsGenerated = 'Your backup was generated in: \x1b[1m' + __dirname + '/backups/' + date + '.zip\x1b[0m - ✅';
var wpFy = '\x1b[42m\x1b[1mWordPressify\x1b[0m';
var wpFyUrl = '\x1b[2m - http://www.wordpressify.co/\x1b[0m';
var thankYou = 'Thank you for using ' + wpFy + wpFyUrl;
var errorMsg = '\x1b[41mError\x1b[0m';

gulp.task('backup', function () {
	if (!fs.existsSync('./build')) {
		console.log(buildNotFound);
		process.exit(1);
	} else {
		gulp.src('build/wordpress/**')
			.pipe(zip(date + '.zip'))
			.pipe(gulp.dest('backups'))
			.on('end', function () {
				gutil.beep();
				console.log(backupsGenerated);
				console.log(thankYou);
			});
	}
});
/* -------------------------------------------------------------------------------------------------
End of all Tasks
-------------------------------------------------------------------------------------------------- */
<commit_msg>Error messages.<commit_after>/* -------------------------------------------------------------------------------------------------

Build Configuration
Contributors: Luan Gjokaj

-------------------------------------------------------------------------------------------------- */
'use strict';
var babel = require('gulp-babel');
var browserSync = require('browser-sync');
var concat = require('gulp-concat');
var connect = require('gulp-connect-php');
var cssnano = require('cssnano');
var cssnext = require('postcss-cssnext');
var del = require('del');
var fs = require('fs');
var gulp = require('gulp');
var gutil = require('gulp-util');
var inject = require('gulp-inject-string');
var partialimport = require('postcss-easy-import');
var plumber = require('gulp-plumber');
var postcss = require('gulp-postcss');
var remoteSrc = require('gulp-remote-src');
var sourcemaps = require('gulp-sourcemaps');
var uglify = require('gulp-uglify');
var unzip = require('gulp-unzip');
var zip = require('gulp-zip');
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
PostCSS Plugins
-------------------------------------------------------------------------------------------------- */
var pluginsDev = [
	partialimport,
	cssnext({
		features: {
			colorHexAlpha: false
		}
	})
];
var pluginsProd = [
	partialimport,
	cssnext({
		features: {
			colorHexAlpha: false
		}
	})
];
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
Header & Footer JavaScript Boundles
-------------------------------------------------------------------------------------------------- */
var headerJS = [
	'node_modules/jquery/dist/jquery.js',
	'node_modules/nprogress/nprogress.js',
	'node_modules/aos/dist/aos.js',
	'node_modules/isotope-layout/dist/isotope.pkgd.js'
];
var footerJS = [
	'src/js/**'
];
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
Theme Name
-------------------------------------------------------------------------------------------------- */
var themeName = 'wordpressify';
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
Installation Tasks
-------------------------------------------------------------------------------------------------- */
gulp.task('default');

gulp.task('cleanup', function () {
	del(['build/**']);
	del(['dist/**']);
});

gulp.task('download-wordpress', function () {
	remoteSrc(['latest.zip'], {
		base: 'https://wordpress.org/'
	})
		.pipe(gulp.dest('build/'));
});

gulp.task('setup', [
	'unzip-wordpress',
	'copy-config'
]);

gulp.task('unzip-wordpress', function () {
	gulp.src('build/latest.zip')
		.pipe(unzip())
		.pipe(gulp.dest('build/'))
});

gulp.task('copy-config', function () {
	gulp.src('wp-config.php')
		.pipe(gulp.dest('build/wordpress'));
});

gulp.task('disable-cron', function () {
	gulp.src('build/wordpress/wp-config.php')
		.pipe(inject.after('define(\'DB_COLLATE\', \'\');', '\ndefine(\'DISABLE_WP_CRON\', true);'))
		.pipe(gulp.dest('build/wordpress'))
		.on('end', function () {
			gutil.beep();
			console.log(devServerReady);
			console.log(thankYou);
		});
});
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
Development Tasks
-------------------------------------------------------------------------------------------------- */
gulp.task('build-dev', [
	'copy-theme-dev',
	'copy-fonts-dev',
	'style-dev',
	'header-scripts-dev',
	'footer-scripts-dev',
	'watch'

], function () {
	connect.server({
		base: 'build/wordpress',
		port: '3020'
	}, function () {
		browserSync({
			proxy: '127.0.0.1:3020'
		});
	});
});

gulp.task('copy-theme-dev', function () {
	if (!fs.existsSync('./build')) {
		console.log(buildNotFound);
		process.exit(1);
	} else {
		gulp.src('src/theme/**')
			.pipe(gulp.dest('build/wordpress/wp-content/themes/' + themeName));
	}
});

gulp.task('copy-fonts-dev', function () {
	gulp.src('src/fonts/**')
		.pipe(gulp.dest('build/wordpress/wp-content/themes/' + themeName + '/fonts'))
});

gulp.task('style-dev', function () {
	return gulp.src('src/style/style.css')
		.pipe(plumber({ errorHandler: onError }))
		.pipe(sourcemaps.init())
		.pipe(postcss(pluginsDev))
		.pipe(sourcemaps.write('.'))
		.pipe(gulp.dest('build/wordpress/wp-content/themes/' + themeName))
		.pipe(browserSync.stream({ match: '**/*.css' }));
});

gulp.task('header-scripts-dev', function () {
	return gulp.src(headerJS)
		.pipe(plumber({ errorHandler: onError }))
		.pipe(sourcemaps.init())
		.pipe(concat('header-bundle.js'))
		.pipe(sourcemaps.write('.'))
		.pipe(gulp.dest('build/wordpress/wp-content/themes/' + themeName + '/js'));
});

gulp.task('footer-scripts-dev', function () {
	return gulp.src(footerJS)
		.pipe(plumber({ errorHandler: onError }))
		.pipe(sourcemaps.init())
		.pipe(babel({
			presets: ['env']
		}))
		.pipe(concat('footer-bundle.js'))
		.pipe(sourcemaps.write('.'))
		.pipe(gulp.dest('build/wordpress/wp-content/themes/' + themeName + '/js'));
});

gulp.task('reload-js', ['footer-scripts-dev', 'header-scripts-dev'], function (done) {
	browserSync.reload();
	done();
});

gulp.task('reload-fonts', ['copy-fonts-dev'], function (done) {
	browserSync.reload();
	done();
});

gulp.task('reload-theme', ['copy-theme-dev'], function (done) {
	browserSync.reload();
	done();
});

gulp.task('watch', function () {
	gulp.watch(['src/style/**/*.css'], ['style-dev']);
	gulp.watch(['src/js/**'], ['reload-js']);
	gulp.watch(['src/fonts/**'], ['reload-fonts']);
	gulp.watch(['src/theme/**'], ['reload-theme']);
});
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
Production Tasks
-------------------------------------------------------------------------------------------------- */
gulp.task('build-prod', [
	'copy-theme-prod',
	'copy-fonts-prod',
	'style-prod',
	'header-scripts-prod',
	'footer-scripts-prod',
	'zip-theme'
], function () {
	gutil.beep();
	console.log(filesGenerated);
	console.log(thankYou);
});

gulp.task('copy-theme-prod', function () {
	gulp.src('src/theme/**')
		.pipe(gulp.dest('dist/themes/' + themeName))
});

gulp.task('copy-fonts-prod', function () {
	gulp.src('src/fonts/**')
		.pipe(gulp.dest('dist/themes/' + themeName + '/fonts'))
});

gulp.task('style-prod', function () {
	return gulp.src('src/style/style.css')
		.pipe(plumber({ errorHandler: onError }))
		.pipe(postcss(pluginsProd))
		.pipe(gulp.dest('dist/themes/' + themeName))
});

gulp.task('header-scripts-prod', function () {
	return gulp.src(headerJS)
		.pipe(plumber({ errorHandler: onError }))
		.pipe(concat('header-bundle.js'))
		.pipe(uglify())
		.pipe(gulp.dest('dist/themes/' + themeName + '/js'));
});

gulp.task('footer-scripts-prod', function () {
	return gulp.src(footerJS)
		.pipe(plumber({ errorHandler: onError }))
		.pipe(babel({
			presets: ['env']
		}))
		.pipe(concat('footer-bundle.js'))
		.pipe(uglify())
		.pipe(gulp.dest('dist/themes/' + themeName + '/js'));
});

gulp.task('zip-theme', ['copy-theme-prod', 'copy-fonts-prod', 'style-prod', 'header-scripts-prod', 'footer-scripts-prod'], function () {
	gulp.src('dist/themes/' + themeName + '/**')
		.pipe(zip(themeName + '.zip'))
		.pipe(gulp.dest('dist'))
});
//--------------------------------------------------------------------------------------------------
/* -------------------------------------------------------------------------------------------------
Utilitie Tasks
 ------------------------------------------------------------------------------------------------- */
var onError = function (err) {
	gutil.beep();
	console.log(wpFy + ' - ' + errorMsg + ' ' + err.toString());
	this.emit('end');
};

var date = new Date().toLocaleDateString('en-GB').replace(/\//g, '.');
var errorMsg = '\x1b[41mError\x1b[0m';
var devServerReady = 'Your development server is ready, start the workflow with the command: $ \x1b[1mnpm run dev\x1b[0m';
var buildNotFound = errorMsg + ' ⚠　- You need to install WordPress first. Run the command: $ \x1b[1mnpm run install:wordpress\x1b[0m';
var filesGenerated = 'Your ZIP template file was generated in: \x1b[1m' + __dirname + '/dist/' + themeName + '.zip\x1b[0m - ✅';
var backupsGenerated = 'Your backup was generated in: \x1b[1m' + __dirname + '/backups/' + date + '.zip\x1b[0m - ✅';
var wpFy = '\x1b[42m\x1b[1mWordPressify\x1b[0m';
var wpFyUrl = '\x1b[2m - http://www.wordpressify.co/\x1b[0m';
var thankYou = 'Thank you for using ' + wpFy + wpFyUrl;

gulp.task('backup', function () {
	if (!fs.existsSync('./build')) {
		console.log(buildNotFound);
		process.exit(1);
	} else {
		gulp.src('build/wordpress/**')
			.pipe(zip(date + '.zip'))
			.pipe(gulp.dest('backups'))
			.on('end', function () {
				gutil.beep();
				console.log(backupsGenerated);
				console.log(thankYou);
			});
	}
});
/* -------------------------------------------------------------------------------------------------
End of all Tasks
-------------------------------------------------------------------------------------------------- */
<|endoftext|>


<commit_before>'use strict';

var GENDERSKELNAMES = {
  0: 'male_skel',
  1: 'female_skel'
};

var MOTION_TABLE = {
  MALE_MOTION: 0, // STR
  FEMALE_MOTION: 1, // STR
  MOTION_TYPE: 2, // INT
  DESCRIPTION: 3 // STR
};

var AVTANI = {
  STOP1: 0,
  STOP2: 1,
  WALK: 2,
  RUN: 3,
  SITTING: 4,
  SIT: 5,
  STANDUP: 6,
  STOP3: 7,
  ATTACK: 8,
  ATTACK2: 9,
  ATTACK3: 10,
  HIT: 11,
  FALL: 12,
  DIE: 13,
  RAISE: 14,
  JUMP1: 15,
  JUMP2: 16,
  PICKITEM: 17
};

var AVTBODYPART = {
  Face: 0,
  Hair: 1,
  Cap: 2,
  Body: 3,
  Arms: 4,
  Foot: 5,
  FaceItem: 6,
  Back: 7,
  Weapon: 8,
  SubWeapon: 9,
  Max: 10
};

var AVTRIDEPART = {
  Max: 5
};

var AVTSHOTTYPE = {
  Max: 3
};

var BoneIndex = {
  Pelvis: 0,
  Head: 4
};
var DummyIndex = {
  RightHand: 0,
  LeftHand: 1,
  LeftHandShield: 2,
  Back: 3,
  Mouse: 4,
  Eyes: 5,
  Cap: 6
};

var MAVTPARTTYPES = [
  { dataName: 'itm_mface', boneIdx: 4 },
  { dataName: 'itm_mhair', boneIdx: 4 },
  { dataName: 'itm_mcap', dummyIdx: DummyIndex.Cap },
  { dataName: 'itm_mbody' },
  { dataName: 'itm_marms' },
  { dataName: 'itm_mfoot' },
  { dataName: 'itm_mfaceitem', dummyIdx: DummyIndex.Mouse },
  { dataName: 'itm_back', dummyIdx: DummyIndex.Back },
  { dataName: 'itm_weapon' },
  { dataName: 'itm_subwpn' }
];
var FAVTPARTTYPES = [
  { dataName: 'itm_fface', boneIdx: 4 },
  { dataName: 'itm_fhair', boneIdx: 4 },
  { dataName: 'itm_fcap', dummyIdx: DummyIndex.Cap },
  { dataName: 'itm_fbody' },
  { dataName: 'itm_farms' },
  { dataName: 'itm_ffoot' },
  { dataName: 'itm_ffaceitem', dummyIdx: DummyIndex.Mouse },
  { dataName: 'itm_back', dummyIdx: DummyIndex.Back },
  { dataName: 'itm_weapon' },
  { dataName: 'itm_subwpn' }
];

/**
 * @constructor
 *
 * @property {Object.<string,SkeletonAnimator>} motionCache
 * Holds a cache of all animations that are actually loaded against
 * this skeleton and can be used immediately (and should not be loaded
 * against the skeleton again).
 */
function CharPawn(go) {
  this.rootObj = new THREE.Object3D();
  this.rootObj.owner = this;
  this.gender = -1;
  this.skel = null;
  this.skelWaiters = [];
  this.motionCache = null;
  this.activeMotions = [];
  this.defaultMotionIdx = -1;
  this.nameTag = null;
  this.modelParts = [];

  if (go) {
    this.owner = go;

    var self = this;
    this.rootObj.name = 'CHAR_' + '????';
    this.setGender(go.gender, function() {
      for (var j = 0; j < AVTBODYPART.Max; ++j) {
        self.setModelPart(j, go.visParts[j].itemNo);
      }
    });

    go.on('damage', function(amount) {
      self.newDamage(amount);
    });

    this.createNamePlate();
  }
}

CharPawn.prototype.createNamePlate = function() {
  var texture = ui.createNamePlate(this.owner);

  // Recreate the sprite with new texture
  var material = new THREE.SpriteMaterial({ map: texture, color: 0xffffff });
  material.depthWrite = false;
  var sprite = new OrthoSprite(material);
  sprite.position.set(0, 0, 2.0);
  sprite.scale.set(texture.image.width, texture.image.height, 1);
  sprite.offset.set(0, -24, 0);

  this.rootObj.add(sprite);
  this.nameTag = sprite;
};

/**
 * Holds a cache of all loaded animation files data.  This is just the data,
 * and not bound to any particular skeleton.
 *
 * @type {DataCache.<AnimationData>}
 */
CharPawn.motionFileCache = new DataCache(AnimationData);

CharPawn.prototype._setSkeleton = function(skelData) {
  this.skel = skelData.create(this.rootObj);

  // Reset the loaded motions if the skeleton changed...
  this.motionCache = {};
  this.activeMotions = [];

  // Let everyone waiting know!
  for (var i = 0; i < this.skelWaiters.length; ++i) {
    this.skelWaiters[i]();
  }
  this.skelWaiters = [];
};

CharPawn.prototype._waitSkeleton = function(callback) {
  if (this.gender !== 0 && this.gender !== 1) {
    throw new Error('Cannot wait on a skeleton that is not loading.');
  }

  if (this.skel) {
    callback();
  } else {
    this.skelWaiters.push(callback);
  }
};

CharPawn.prototype.setGender = function(genderIdx, callback) {
  // Dont waste time doing nothing
  if (this.gender === genderIdx) {
    return;
  }

  var self = this;
  this.gender = genderIdx;

  var skelName = GENDERSKELNAMES[genderIdx];
  if (!skelName) {
    throw new Error('Invalid gender specified (' + genderIdx + ')');
  }

  GDM.get(skelName, function(skelData) {
    self._setSkeleton(skelData);

    // Reload items
    for (var i = 0; i < self.modelParts.length; ++i) {
      if (self.modelParts[i]) {
        self.setModelPart(i, self.modelParts[i].id);
      }
    }

    if (callback) {
      callback();
    }

    self.defaultMotionIdx = -1;
    self.playDefaultMotion();
  });
};

CharPawn.prototype._setModelPart = function(modelList, partIdx, modelIdx, bindBone, bindDummy) {
  var self = this;

  // Remove old model
  if (this.modelParts[partIdx]) {
    for (var i = 0; i < this.modelParts[partIdx].meshes.length; ++i) {
      var part = this.modelParts[partIdx].meshes[i];
      part.parent.remove(part);
    }
  }

  this.modelParts[partIdx] = {
    id: modelIdx,
    meshes: []
  };

  // Add new model
  var model = modelList.data.models[modelIdx];

  if (!model) {
    if (modelIdx !== 0) {
      console.warn('Tried to set avatar part to invalid item (' + partIdx + ', ' + modelIdx + ', ' + bindBone + ')');
    }
    return;
  }

  for (var j = 0; j < model.parts.length; ++j) {
    (function(part) {
      var material = modelList._createMaterial(part.materialIdx);
      var meshPath = modelList.data.meshes[part.meshIdx];

      Mesh.load(meshPath, function (geometry) {
        var mesh;

        if (part.boneIndex !== undefined) {
          bindBone = part.boneIndex;
        }

        if (part.dummyIndex !== undefined) {
          bindDummy = part.dummyIndex;
        }

        if (bindBone === undefined && bindDummy === undefined) {
          mesh = new THREE.SkinnedMesh(geometry, material);
          mesh.rootObject = self.rootObj;
          mesh.bind(self.skel);
          self.rootObj.add(mesh);
        } else {
          mesh = new THREE.Mesh(geometry, material);
          mesh.rootObject = self.rootObj;

          if (bindBone !== undefined) {
            self.skel.bones[bindBone].add(mesh);
          } else if (bindDummy !== undefined) {
            self.skel.dummies[bindDummy].add(mesh);
          } else {
            console.warn('Loaded part with no bind location');
          }
        }

        self.modelParts[partIdx].meshes.push(mesh);
      });
    })(model.parts[j]);
  }
};

CharPawn.prototype.setModelPart = function(partIdx, modelIdx, callback) {
  var self = this;
  var partType;
  if (this.gender === 0) {
    partType = MAVTPARTTYPES[partIdx];
  } else {
    partType = FAVTPARTTYPES[partIdx];
  }
  GDM.get(partType.dataName, function(partModelList) {
    self._setModelPart(partModelList, partIdx, modelIdx,
        partType.boneIdx, partType.dummyIdx);
    if (callback) {
      callback();
    }
  });
};

CharPawn.prototype._getMotionData = function(motionIdx, callback) {
  GDM.get('char_motiontypes', 'char_motions', function(motionTypes, charMotions) {
    this._waitSkeleton(function() {
      var motionFileIdx = motionTypes.item(motionIdx, 1);
      var motionRow = charMotions.row(motionFileIdx);
      var motionFile = motionRow[MOTION_TABLE.MALE_MOTION + this.gender];
      if (!motionFile && this.gender !== 0) {
        motionFile = motionRow[MOTION_TABLE.MALE_MOTION];
      }
      CharPawn.motionFileCache.get(motionFile, function (animData) {
        if (callback) {
          callback(animData);
        }
      });
    }.bind(this));
  }.bind(this));
};

CharPawn.prototype._getMotion = function(motionIdx, callback) {
  // We check the cache after the getMotionData to avoid synchronization
  //   issues from calling this method twice in a row for the same motion.
  this._getMotionData(motionIdx, function(animData) {
    if (this.motionCache[motionIdx]) {
      callback(this.motionCache[motionIdx]);
      return;
    }
    var anim = new SkeletonAnimator(this.skel, animData);
    this.motionCache[motionIdx] = anim;
    callback(anim);
  }.bind(this));
};

CharPawn.prototype._playMotion = function(motionIdx, timeScale, loop, callback) {
  this._getMotion(motionIdx, function(anim) {
    var activeIdx = this.activeMotions.indexOf(anim);
    if (activeIdx !== -1) {
      this.activeMotions.splice(activeIdx, 1);
    }

    anim.timeScale = timeScale;
    anim.loop = loop;
    if (!anim.isPlaying) {
      anim.play(0, 0);
    }

    // If no motions were previously playing, immediately activate this one.
    if (this.activeMotions.length === 0) {
      anim.weight = 1;
    }

    this.activeMotions.unshift(anim);

    if (callback) {
      callback(anim);
    }
  }.bind(this));
};

CharPawn.prototype.playDefaultMotion = function() {
  var timeScale = 1.0;
  var newIdleMotion = AVTANI.STOP1;
  if (this.owner) {
    if (this.owner.isSitting) {
      newIdleMotion = AVTANI.SITTING;
    } else if (this.owner.speed > 0) {
      if (this.owner.isRunning) {
        timeScale = (this.owner.moveSpeed + 180) / 600;
        newIdleMotion = AVTANI.RUN;
      } else {
        newIdleMotion = AVTANI.WALK;
      }
    }
  }

  if (newIdleMotion === this.defaultMotionIdx) {
    return;
  }

  this._playMotion(newIdleMotion, timeScale, true);
  this.defaultMotionIdx = newIdleMotion
};

CharPawn.prototype.playMotion = function(motionIdx, timeScale, loop, callback) {
  this._playMotion(motionIdx, timeScale, loop, callback);
  this.defaultMotionIdx = -1;
};

CharPawn.prototype.playAttackMotion = function(onFinish) {
  var timeScale = this.owner.stats.getAttackSpeed() / 100;
  var motionIdx = AVTANI.ATTACK;
  var animNum = Math.floor(Math.random() * 3);
  if (animNum === 1) {
    motionIdx = AVTANI.ATTACK2;
  } else if (animNum === 2) {
    motionIdx = AVTANI.ATTACK3;
  }
  this.playMotion(motionIdx, timeScale, false, function(anim) {
    anim.once('finish', onFinish);
  });
};

CharPawn.prototype.newDamage = function(amount) {
  DamageRender.add(amount,
      this.rootObj.localToWorld(new THREE.Vector3(0, 0, 2.4)));
};

CharPawn.prototype.update = function(delta) {
  // Update Animation Blending
  var blendWeightDelta = 6 * delta;
  if (this.activeMotions.length >= 1) {
    var activeMotion = this.activeMotions[0];
    if (activeMotion.weight < 1) {
      activeMotion.weight += blendWeightDelta;
    }

    for (var i = 1; i < this.activeMotions.length; ++i) {
      var motion = this.activeMotions[i];
      motion.weight -= blendWeightDelta;
      if (!motion.isPlaying || motion.weight <= 0) {
        motion.stop();
        this.activeMotions.splice(i, 1);
        --i;
      }
    }

    if (!this.activeMotions[0].isPlaying) {
      this.playDefaultMotion();
    }
  }

  // Update stuff
  if (this.owner) {
    var self = this;
    var dirStep = (Math.PI * 3) * delta;
    var deltaDir = slerp1d(this.owner.direction, self.rootObj.rotation.z);

    if (deltaDir > dirStep || deltaDir < -dirStep) {
      if (deltaDir < 0) {
        self.rootObj.rotation.z -= dirStep;
      } else {
        self.rootObj.rotation.z += dirStep;
      }
    } else {
      self.rootObj.rotation.z = this.owner.direction;
    }

    self.rootObj.position.copy(this.owner.position);
  }

  if (this.defaultMotionIdx !== -1) {
    this.playDefaultMotion();
  }
};
<commit_msg>Fixed minor issues with motions on CharPawn.<commit_after>'use strict';

var GENDERSKELNAMES = {
  0: 'male_skel',
  1: 'female_skel'
};

var MOTION_TABLE = {
  MALE_MOTION: 0, // STR
  FEMALE_MOTION: 1, // STR
  MOTION_TYPE: 2, // INT
  DESCRIPTION: 3 // STR
};

var AVTANI = {
  STOP1: 0,
  STOP2: 1,
  WALK: 2,
  RUN: 3,
  SITTING: 4,
  SIT: 5,
  STANDUP: 6,
  STOP3: 7,
  ATTACK: 8,
  ATTACK2: 9,
  ATTACK3: 10,
  HIT: 11,
  FALL: 12,
  DIE: 13,
  RAISE: 14,
  JUMP1: 15,
  JUMP2: 16,
  PICKITEM: 17
};

var AVTBODYPART = {
  Face: 0,
  Hair: 1,
  Cap: 2,
  Body: 3,
  Arms: 4,
  Foot: 5,
  FaceItem: 6,
  Back: 7,
  Weapon: 8,
  SubWeapon: 9,
  Max: 10
};

var AVTRIDEPART = {
  Max: 5
};

var AVTSHOTTYPE = {
  Max: 3
};

var BoneIndex = {
  Pelvis: 0,
  Head: 4
};
var DummyIndex = {
  RightHand: 0,
  LeftHand: 1,
  LeftHandShield: 2,
  Back: 3,
  Mouse: 4,
  Eyes: 5,
  Cap: 6
};

var MAVTPARTTYPES = [
  { dataName: 'itm_mface', boneIdx: 4 },
  { dataName: 'itm_mhair', boneIdx: 4 },
  { dataName: 'itm_mcap', dummyIdx: DummyIndex.Cap },
  { dataName: 'itm_mbody' },
  { dataName: 'itm_marms' },
  { dataName: 'itm_mfoot' },
  { dataName: 'itm_mfaceitem', dummyIdx: DummyIndex.Mouse },
  { dataName: 'itm_back', dummyIdx: DummyIndex.Back },
  { dataName: 'itm_weapon' },
  { dataName: 'itm_subwpn' }
];
var FAVTPARTTYPES = [
  { dataName: 'itm_fface', boneIdx: 4 },
  { dataName: 'itm_fhair', boneIdx: 4 },
  { dataName: 'itm_fcap', dummyIdx: DummyIndex.Cap },
  { dataName: 'itm_fbody' },
  { dataName: 'itm_farms' },
  { dataName: 'itm_ffoot' },
  { dataName: 'itm_ffaceitem', dummyIdx: DummyIndex.Mouse },
  { dataName: 'itm_back', dummyIdx: DummyIndex.Back },
  { dataName: 'itm_weapon' },
  { dataName: 'itm_subwpn' }
];

/**
 * @constructor
 *
 * @property {Object.<string,SkeletonAnimator>} motionCache
 * Holds a cache of all animations that are actually loaded against
 * this skeleton and can be used immediately (and should not be loaded
 * against the skeleton again).
 */
function CharPawn(go) {
  this.rootObj = new THREE.Object3D();
  this.rootObj.owner = this;
  this.gender = -1;
  this.skel = null;
  this.skelWaiters = [];
  this.motionCache = null;
  this.activeMotions = [];
  this.defaultMotionIdx = -1;
  this.nameTag = null;
  this.modelParts = [];

  if (go) {
    this.owner = go;

    var self = this;
    this.rootObj.name = 'CHAR_' + '????';
    this.setGender(go.gender, function() {
      for (var j = 0; j < AVTBODYPART.Max; ++j) {
        self.setModelPart(j, go.visParts[j].itemNo);
      }
    });

    go.on('damage', function(amount) {
      self.newDamage(amount);
    });

    this.createNamePlate();
  }
}

CharPawn.prototype.createNamePlate = function() {
  var texture = ui.createNamePlate(this.owner);

  // Recreate the sprite with new texture
  var material = new THREE.SpriteMaterial({ map: texture, color: 0xffffff });
  material.depthWrite = false;
  var sprite = new OrthoSprite(material);
  sprite.position.set(0, 0, 2.0);
  sprite.scale.set(texture.image.width, texture.image.height, 1);
  sprite.offset.set(0, -24, 0);

  this.rootObj.add(sprite);
  this.nameTag = sprite;
};

/**
 * Holds a cache of all loaded animation files data.  This is just the data,
 * and not bound to any particular skeleton.
 *
 * @type {DataCache.<AnimationData>}
 */
CharPawn.motionFileCache = new DataCache(AnimationData);

CharPawn.prototype._setSkeleton = function(skelData) {
  this.skel = skelData.create(this.rootObj);

  // Reset the loaded motions if the skeleton changed...
  this.motionCache = {};
  this.activeMotions = [];

  // Let everyone waiting know!
  for (var i = 0; i < this.skelWaiters.length; ++i) {
    this.skelWaiters[i]();
  }
  this.skelWaiters = [];
};

CharPawn.prototype._waitSkeleton = function(callback) {
  if (this.gender !== 0 && this.gender !== 1) {
    throw new Error('Cannot wait on a skeleton that is not loading.');
  }

  if (this.skel) {
    callback();
  } else {
    this.skelWaiters.push(callback);
  }
};

CharPawn.prototype.setGender = function(genderIdx, callback) {
  // Dont waste time doing nothing
  if (this.gender === genderIdx) {
    return;
  }

  var self = this;
  this.gender = genderIdx;

  var skelName = GENDERSKELNAMES[genderIdx];
  if (!skelName) {
    throw new Error('Invalid gender specified (' + genderIdx + ')');
  }

  GDM.get(skelName, function(skelData) {
    self._setSkeleton(skelData);

    // Reload items
    for (var i = 0; i < self.modelParts.length; ++i) {
      if (self.modelParts[i]) {
        self.setModelPart(i, self.modelParts[i].id);
      }
    }

    if (callback) {
      callback();
    }

    self.defaultMotionIdx = -1;
    self.playDefaultMotion();
  });
};

CharPawn.prototype._setModelPart = function(modelList, partIdx, modelIdx, bindBone, bindDummy) {
  var self = this;

  // Remove old model
  if (this.modelParts[partIdx]) {
    for (var i = 0; i < this.modelParts[partIdx].meshes.length; ++i) {
      var part = this.modelParts[partIdx].meshes[i];
      part.parent.remove(part);
    }
  }

  this.modelParts[partIdx] = {
    id: modelIdx,
    meshes: []
  };

  // Add new model
  var model = modelList.data.models[modelIdx];

  if (!model) {
    if (modelIdx !== 0) {
      console.warn('Tried to set avatar part to invalid item (' + partIdx + ', ' + modelIdx + ', ' + bindBone + ')');
    }
    return;
  }

  for (var j = 0; j < model.parts.length; ++j) {
    (function(part) {
      var material = modelList._createMaterial(part.materialIdx);
      var meshPath = modelList.data.meshes[part.meshIdx];

      Mesh.load(meshPath, function (geometry) {
        var mesh;

        if (part.boneIndex !== undefined) {
          bindBone = part.boneIndex;
        }

        if (part.dummyIndex !== undefined) {
          bindDummy = part.dummyIndex;
        }

        if (bindBone === undefined && bindDummy === undefined) {
          mesh = new THREE.SkinnedMesh(geometry, material);
          mesh.rootObject = self.rootObj;
          mesh.bind(self.skel);
          self.rootObj.add(mesh);
        } else {
          mesh = new THREE.Mesh(geometry, material);
          mesh.rootObject = self.rootObj;

          if (bindBone !== undefined) {
            self.skel.bones[bindBone].add(mesh);
          } else if (bindDummy !== undefined) {
            self.skel.dummies[bindDummy].add(mesh);
          } else {
            console.warn('Loaded part with no bind location');
          }
        }

        self.modelParts[partIdx].meshes.push(mesh);
      });
    })(model.parts[j]);
  }
};

CharPawn.prototype.setModelPart = function(partIdx, modelIdx, callback) {
  var self = this;
  var partType;
  if (this.gender === 0) {
    partType = MAVTPARTTYPES[partIdx];
  } else {
    partType = FAVTPARTTYPES[partIdx];
  }
  GDM.get(partType.dataName, function(partModelList) {
    self._setModelPart(partModelList, partIdx, modelIdx,
        partType.boneIdx, partType.dummyIdx);
    if (callback) {
      callback();
    }
  });
};

CharPawn.prototype._getMotionData = function(motionIdx, callback) {
  GDM.get('char_motiontypes', 'char_motions', function(motionTypes, charMotions) {
    this._waitSkeleton(function() {
      var motionFileIdx;
      if (motionIdx >= 0) {
        motionFileIdx = motionTypes.item(motionIdx, 1);
      } else {
        motionFileIdx = -motionIdx;
      }
      var motionRow = charMotions.row(motionFileIdx);
      var motionFile = motionRow[MOTION_TABLE.MALE_MOTION + this.gender];
      if (!motionFile && this.gender !== 0) {
        motionFile = motionRow[MOTION_TABLE.MALE_MOTION];
      }
      CharPawn.motionFileCache.get(motionFile, function (animData) {
        if (callback) {
          callback(animData);
        }
      });
    }.bind(this));
  }.bind(this));
};

CharPawn.prototype._getMotion = function(motionIdx, callback) {
  // We check the cache after the getMotionData to avoid synchronization
  //   issues from calling this method twice in a row for the same motion.
  this._getMotionData(motionIdx, function(animData) {
    if (this.motionCache[motionIdx]) {
      callback(this.motionCache[motionIdx]);
      return;
    }
    var anim = new SkeletonAnimator(this.skel, animData);
    this.motionCache[motionIdx] = anim;
    callback(anim);
  }.bind(this));
};

CharPawn.prototype._playMotion = function(motionIdx, timeScale, loop, callback) {
  this._getMotion(motionIdx, function(anim) {
    var activeIdx = this.activeMotions.indexOf(anim);
    if (activeIdx !== -1) {
      this.activeMotions.splice(activeIdx, 1);
    }

    anim.timeScale = timeScale;
    anim.loop = loop;
    if (!anim.isPlaying) {
      anim.play(0, 0);
    }

    // If no motions were previously playing, immediately activate this one.
    if (this.activeMotions.length === 0) {
      anim.weight = 1;
    }

    this.activeMotions.unshift(anim);

    if (callback) {
      callback(anim);
    }
  }.bind(this));
};

CharPawn.prototype.playDefaultMotion = function() {
  var timeScale = 1.0;
  var newIdleMotion = AVTANI.STOP1;
  if (this.owner) {
    if (this.owner.isSitting) {
      newIdleMotion = AVTANI.SITTING;
    } else if (this.owner.speed > 0) {
      if (this.owner.isRunning) {
        timeScale = (this.owner.moveSpeed + 180) / 600;
        newIdleMotion = AVTANI.RUN;
      } else {
        newIdleMotion = AVTANI.WALK;
      }
    }
  }

  if (newIdleMotion === this.defaultMotionIdx) {
    return;
  }

  this._playMotion(newIdleMotion, timeScale, true);
  this.defaultMotionIdx = newIdleMotion
};

CharPawn.prototype.playMotion = function(motionIdx, timeScale, loop, callback) {
  this._playMotion(motionIdx, timeScale, loop, callback);
  this.defaultMotionIdx = -1;
};

CharPawn.prototype.playAttackMotion = function(onFinish) {
  var timeScale = this.owner.stats.getAttackSpeed() / 100;
  var motionIdx = AVTANI.ATTACK;
  var animNum = Math.floor(Math.random() * 3);
  if (animNum === 1) {
    motionIdx = AVTANI.ATTACK2;
  } else if (animNum === 2) {
    motionIdx = AVTANI.ATTACK3;
  }
  this.playMotion(motionIdx, timeScale, false, function(anim) {
    anim.once('finish', onFinish);
  });
};

CharPawn.prototype.newDamage = function(amount) {
  DamageRender.add(amount,
      this.rootObj.localToWorld(new THREE.Vector3(0, 0, 2.4)));
};

CharPawn.prototype.update = function(delta) {
  // Update Animation Blending
  var blendWeightDelta = 6 * delta;
  if (this.activeMotions.length >= 1) {
    var activeMotion = this.activeMotions[0];
    if (activeMotion.weight < 1) {
      activeMotion.weight += blendWeightDelta;
    }

    for (var i = 1; i < this.activeMotions.length; ++i) {
      var motion = this.activeMotions[i];
      motion.weight -= blendWeightDelta;

      // If the motion is stopped, we turn off advancing and play
      //  it so we can blend the last frame to the next motion.
      if (!motion.isPlaying) {
        motion.loop = true;
        motion.timeScale = 0;
        motion.play();
      }

      // If we're done blending away, stop the motion and remove
      //  it from the list of motions.
      if (motion.weight <= 0) {
        motion.stop();
        this.activeMotions.splice(i, 1);
        --i;
      }
    }

    if (!this.activeMotions[0].isPlaying) {
      this.playDefaultMotion();
    }
  }

  // Update stuff
  if (this.owner) {
    var self = this;
    var dirStep = (Math.PI * 3) * delta;
    var deltaDir = slerp1d(this.owner.direction, self.rootObj.rotation.z);

    if (deltaDir > dirStep || deltaDir < -dirStep) {
      if (deltaDir < 0) {
        self.rootObj.rotation.z -= dirStep;
      } else {
        self.rootObj.rotation.z += dirStep;
      }
    } else {
      self.rootObj.rotation.z = this.owner.direction;
    }

    self.rootObj.position.copy(this.owner.position);
  }

  if (this.defaultMotionIdx !== -1) {
    this.playDefaultMotion();
  }
};
<|endoftext|>


<commit_before>'use strict';

const path = require('path');
const http = require('http');
const https = require('https');
const fsExtra = require('fs-extra');
const url = require('url');

const tarWrapper = require('../commandWrappers/tarWrapper');
const errors = require('../errors');

function validateOptions(options) {
    if (options.compression && !(options.compression in tarWrapper.compression)) {
        throw new errors.InvalidOptionsError(`Invalid compression: ${options.compression}`);
    }

    if (!options.compression) {
        options.compression = 'gzip';
    }

    if (!options.resolveUrl) {
        throw new errors.InvalidOptionsError('`resolveUrl` function must be provided');
    }

    if (!options.strict) {
        options.strict = false;
    }
}

function pull(hash, options, cacheDir) {
    let resolvedUrlPromise = options.resolveUrl(hash);
    const archivePath = path.join(cacheDir, `${hash}.tar${tarWrapper.compression[options.compression]}`);

    if (!(resolvedUrlPromise instanceof Promise)) {
        resolvedUrlPromise = Promise.resolve(resolvedUrlPromise);
    }

    return resolvedUrlPromise
        .then(resolvedUrl => {
            return new Promise((resolve, reject) => {
                const parsedUrl = url.parse(resolvedUrl);
                let transport;
                let done = false;

                if (parsedUrl.protocol === 'http:') {
                    transport = http;
                } else if (parsedUrl.protocol === 'https:') {
                    transport = https;
                } else {
                    done = true;
                    return reject(new InvalidProtocolError(
                        `http backend can't work with \`${parsedUrl.protocol}\` protocol. ` +
                        `Only \`http:\` and \`https:\` are supported`
                    ));
                }

                transport.get(parsedUrl.href, res => {
                    if (res.statusCode === 404) {
                        done = true;

                        return reject(new errors.BundleNotFoundError);
                    } else if (res.statusCode !== 200) {
                        done = true;

                        if (options.strict) {
                            return reject(new InvalidStatusCodeError(
                                `Request to \'${parsedUrl}\' failed. Invalid status code: \`${res.statusCode}\``
                            ));
                        }

                        return reject(new errors.BundleNotFoundError);
                    }
                    const tarWrapperToken = {};

                    tarWrapper.extractArchiveFromStream(res, {controlToken: tarWrapperToken});

                    res.on('end', () => {
                        if (done) {
                            return;
                        }

                        done = true;
                        resolve();
                    });

                    res.on('error', error => {
                        if (done) {
                            return;
                        }

                        done = true;

                        if (tarWrapperToken.terminate) {
                            tarWrapperToken.terminate();
                        }

                        if (options.strict) {
                            return reject(new BundleDownloadError(error));
                        }

                        return reject(new errors.BundleNotFoundError(error));
                    })
                });
            });
        });
}

class InvalidProtocolError extends errors.VeendorError {}
class InvalidStatusCodeError extends errors.VeendorError {}
class BundleDownloadError extends errors.VeendorError {}

module.exports = {
    validateOptions,
    pull,
    push() {
        throw new errors.VeendorError('`http` backend is read-only, pushing is not implemented');
    },
    InvalidProtocolError,
    InvalidStatusCodeError,
    BundleDownloadError,
};
<commit_msg>A little better streaming for http<commit_after>'use strict';

const path = require('path');
const http = require('http');
const https = require('https');
const fsExtra = require('fs-extra');
const url = require('url');

const tarWrapper = require('../commandWrappers/tarWrapper');
const errors = require('../errors');

function validateOptions(options) {
    if (options.compression && !(options.compression in tarWrapper.compression)) {
        throw new errors.InvalidOptionsError(`Invalid compression: ${options.compression}`);
    }

    if (!options.compression) {
        options.compression = 'gzip';
    }

    if (!options.resolveUrl) {
        throw new errors.InvalidOptionsError('`resolveUrl` function must be provided');
    }

    if (!options.strict) {
        options.strict = false;
    }
}

function pull(hash, options, cacheDir) {
    let resolvedUrlPromise = options.resolveUrl(hash);
    const archivePath = path.join(cacheDir, `${hash}.tar${tarWrapper.compression[options.compression]}`);

    if (!(resolvedUrlPromise instanceof Promise)) {
        resolvedUrlPromise = Promise.resolve(resolvedUrlPromise);
    }

    return resolvedUrlPromise
        .then(resolvedUrl => {
            return new Promise((resolve, reject) => {
                const parsedUrl = url.parse(resolvedUrl);
                let transport;
                let done = false;

                if (parsedUrl.protocol === 'http:') {
                    transport = http;
                } else if (parsedUrl.protocol === 'https:') {
                    transport = https;
                } else {
                    done = true;
                    return reject(new InvalidProtocolError(
                        `http backend can't work with \`${parsedUrl.protocol}\` protocol. ` +
                        `Only \`http:\` and \`https:\` are supported`
                    ));
                }

                transport.get(parsedUrl.href, res => {
                    if (res.statusCode === 404) {
                        done = true;

                        return reject(new errors.BundleNotFoundError);
                    } else if (res.statusCode !== 200) {
                        done = true;

                        if (options.strict) {
                            return reject(new InvalidStatusCodeError(
                                `Request to \'${parsedUrl}\' failed. Invalid status code: \`${res.statusCode}\``
                            ));
                        }

                        return reject(new errors.BundleNotFoundError);
                    }

                    const tarWrapperToken = {};

                    const extractPromise = new Promise((resolve, reject) => {
                        tarWrapper.extractArchiveFromStream(res, {controlToken: tarWrapperToken})
                            .then(resolve, reject);
                    });

                    const resPromise = new Promise((resolve, reject) => {
                        res.on('end', () => {
                            if (done) {
                                return;
                            }

                            done = true;
                            resolve();
                        });

                        res.on('error', error => {
                            if (done) {
                                return;
                            }

                            done = true;

                            if (tarWrapperToken.terminate) {
                                tarWrapperToken.terminate();
                            }

                            if (options.strict) {
                                return reject(new BundleDownloadError(error));
                            }

                            return reject(new errors.BundleNotFoundError(error));
                        })
                    });

                    return Promise.all([resPromise, extractPromise]).then(resolve, reject);
                });
            });
        });
}

class InvalidProtocolError extends errors.VeendorError {}
class InvalidStatusCodeError extends errors.VeendorError {}
class BundleDownloadError extends errors.VeendorError {}

module.exports = {
    validateOptions,
    pull,
    push() {
        throw new errors.VeendorError('`http` backend is read-only, pushing is not implemented');
    },
    InvalidProtocolError,
    InvalidStatusCodeError,
    BundleDownloadError,
};
<|endoftext|>


<commit_before>/**
 * @fileoverview Build file. Based in large part on functionality found in
 *               eslint's own makefile, with necessary tweaks for building an
 *               eslint plugin instead.
 *
 * @author Casey Visco
 */

"use strict";

/* global cat */
/* global echo */
/* global exec */
/* global exit */
/* global find */
/* global mv */
/* global rm */
/* global target */
/* global test */

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

require("shelljs/make");

var path = require("path");
var semver = require("semver");
var dateformat = require("dateformat");
var nodeCLI = require("shelljs-nodecli");

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

// Tools
var ESLINT = "./node_modules/.bin/eslint ";
var MOCHA = "./node_modules/mocha/bin/_mocha ";

// Files
var MAKEFILE = "./makefile.js";
var JS_FILES = find("lib/").filter(fileType("js")).join(" ");
var TEST_FILES = find("tests/lib/").filter(fileType("js")).join(" ");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Generate a function that matches files with a particular extension.
 *
 * @private
 * @param  {String}   extension - the file extension (i.e. "js")
 * @return {Function} function to pass into a filter method
 */
function fileType(extension) {
    return function (filename) {
        return filename.substring(filename.lastIndexOf(".") + 1) === extension;
    };
}

/**
 * Execute a command and return the output instead of printing it to stdout.
 *
 * @private
 * @param  {String} cmd - command to execute
 * @return {String} result of executed command
 */
function execSilent(cmd) {
    return exec(cmd, { silent: true }).output;
}

/**
 * Push supplied `tag` to supplied `list` only if it is a valid semver. This is
 * a reducer function.
 *
 * @private
 * @param  {Array}  list - array of valid semver tags
 * @param  {String} tag  - tag to push if valid
 * @return {Array}  modified `list`
 */
function validSemverTag(list, tag) {
    if (semver.valid(tag)) {
        list.push(tag);
    }

    return list;
}

/**
 * Retrieve a list of semver tags in descending order.
 *
 * @private
 * @return {Array} list of version tags
 */
function getVersionTags() {
    return execSilent("git tag")
        .trim()
        .split("\n")
        .reduce(validSemverTag, [])
        .sort(semver.compare);
}

/**
 * Create a release version, push tags and publish.
 *
 * @private
 * @param  {String} type - type of release to do (patch, minor, major)
 */
function release(type) {
    var newVersion;

    target.test();

    echo("Generating new version");
    newVersion = execSilent("npm version " + type).trim();

    target.changelog();

    // add changelog to commit
    exec("git add CHANGELOG.md");
    exec("git commit --amend --no-edit");

    // replace existing tag
    exec("git tag -f " + newVersion);

    // push all the things
    echo("Publishing to git");
    exec("git push origin master --tags");

    echo("Publishing to npm");
    exec("npm publish");
}

//------------------------------------------------------------------------------
// Tasks
//------------------------------------------------------------------------------

target.lint = function () {
    var errors = 0;

    echo("Linting makefile");
    errors += exec(ESLINT + MAKEFILE).code;

    echo("Linting JavaScript files");
    errors += exec(ESLINT + JS_FILES).code;

    echo("Linting JavaScript test files");
    errors += exec(ESLINT + TEST_FILES).code;

    if (errors) {
        exit(1);
    }
};

/**
 * Verify that each rule is defined in index.js with the correct default
 * setting, has documentation and has tests.
 */
target.checkRules = function () {
    echo("Verifying rules");

    var errors = 0;

    var ruleFiles = find("lib/rules/").filter(fileType("js"));
    var configFile = require("./index");
    var readmeText = cat("./README.md");

    function isDefinedInConfig(rule) {
        return configFile.rules.hasOwnProperty(rule);
    }

    function hasDefaultSettingInConfig(rule) {
        var setting = configFile.rulesConfig[rule];

        return typeof setting === "number" ||
               (setting && typeof setting[0] === "number");
    }

    function isOffInConfig(rule) {
        var setting = configFile.rulesConfig[rule];

        return setting === 0 || (setting && setting[0] === 0);
    }

    function isOnInConfig(rule) {
        return !isOffInConfig(rule);
    }

    function isOffInReadme(rule) {
        var line = new RegExp("\\* \\[" + rule + "\\].*").exec(readmeText);

        return (line ? line[0] : "").indexOf("(off by default)") !== -1;
    }

    function isOnInReadme(rule) {
        return !isOffInReadme(rule);
    }

    function hasIdInTitle(docFile, id) {
        var docText = cat(docFile);
        var idInTitleRegExp = new RegExp("^# (.*?) \\(" + id + "\\)");

        return idInTitleRegExp.test(docText);
    }

    ruleFiles.forEach(function (filename) {
        var basename = path.basename(filename, ".js");
        var docFilename = "docs/rules/" + basename + ".md";
        var testFilename = "tests/lib/rules/" + basename + ".js";

        // Verify rule has documentation

        if (!test("-f", docFilename)) {
            console.error("Missing documentation for rule %s", basename);
            errors++;
        } else {
            if (readmeText.indexOf("(" + docFilename + ")") === -1) {
                console.error("Missing link to documentation for rule %s in index", basename);
                errors++;
            }

            // check for proper doc format
            if (!hasIdInTitle(docFilename, basename)) {
                console.error("Missing id in the doc page's title of rule %s", basename);
                errors++;
            }
        }

        // Verify rule has tests

        if (!test("-f", testFilename)) {
            console.error("Missing tests for rule %s", basename);
            errors++;
        }

        // Verify config

        if (!isDefinedInConfig(basename)) {
            console.error("Missing rule entry for %s in index.js", basename);
            errors++;
        }

        if (!hasDefaultSettingInConfig(basename)) {
            console.error("Missing default setting for %s in index.js", basename);
            errors++;
        }

        if (isOffInReadme(basename) && isOnInConfig(basename)) {
            console.error("README states that %s is off by default, but it is enabled in index.js.", basename);
            errors++;
        }

        if (isOffInConfig(basename) && isOnInReadme(basename)) {
            console.error("Missing '(off by default)' for rule %s in README", basename);
            errors++;
        }
    });

    if (errors) {
        exit(1);
    }
};

target.test = function () {
    var errors = 0;

    target.lint();
    target.checkRules();

    echo("Running test suite");

    errors += nodeCLI.exec("istanbul", "cover", MOCHA, "-- -R progress -t 3000 -c", TEST_FILES).code;
    errors += nodeCLI.exec("istanbul", "check-coverage", "--statement 100 --branch 95 --function 100 --lines 100").code;

    if (errors) {
        exit(1);
    }
};

target.changelog = function () {
    echo("Generating changelog");

    // get most recent two tags
    var tags = getVersionTags(),
        rangeTags = tags.slice(tags.length - 2),
        timestamp = dateformat(new Date(), "mmmm d, yyyy"),
        semverRe = /^\* \d\.\d.\d/;

    // output header
    ("### " + rangeTags[1] + " - " + timestamp + "\n").to("CHANGELOG.tmp");

    // get log statements
    var logs = execSilent("git log --pretty=format:\"* %s (%an)\" " + rangeTags.join("..")).split(/\n/g);

    logs = logs.filter(function (line) {
        return line.indexOf("Merge pull request") === -1 &&
               line.indexOf("Merge branch") === -1 &&
               !semverRe.test(line);
    });

    logs.push(""); // to create empty lines
    logs.unshift("");

    // output log statements
    logs.join("\n").toEnd("CHANGELOG.tmp");

    cat("CHANGELOG.tmp", "CHANGELOG.md").to("CHANGELOG.md.tmp");
    rm("CHANGELOG.tmp");
    rm("CHANGELOG.md");
    mv("CHANGELOG.md.tmp", "CHANGELOG.md");
};

target.patch = function () {
    release("patch");
};

target.minor = function () {
    release("minor");
};

target.major = function () {
    release("major");
};
<commit_msg>Build: Ensuring build can run on Windows (fixes #41)<commit_after>/**
 * @fileoverview Build file. Based in large part on functionality found in
 *               eslint's own makefile, with necessary tweaks for building an
 *               eslint plugin instead.
 *
 * @author Casey Visco
 */

"use strict";

/* eslint-env shelljs */

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

require("shelljs/make");

var path = require("path");
var semver = require("semver");
var dateformat = require("dateformat");
var nodeCLI = require("shelljs-nodecli");

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

// Tools
var MOCHA = path.normalize("./node_modules/mocha/bin/_mocha");

// Files
var MAKEFILE = "./makefile.js";
var JS_FILES = find("lib/").filter(fileType("js")).join(" ");
var TEST_FILES = find("tests/lib/").filter(fileType("js")).join(" ");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Generate a function that matches files with a particular extension.
 *
 * @private
 * @param  {String}   extension - the file extension (i.e. "js")
 * @return {Function} function to pass into a filter method
 */
function fileType(extension) {
    return function (filename) {
        return filename.substring(filename.lastIndexOf(".") + 1) === extension;
    };
}

/**
 * Execute a command and return the output instead of printing it to stdout.
 *
 * @private
 * @param  {String} cmd - command to execute
 * @return {String} result of executed command
 */
function execSilent(cmd) {
    return exec(cmd, { silent: true }).output;
}

/**
 * Push supplied `tag` to supplied `list` only if it is a valid semver. This is
 * a reducer function.
 *
 * @private
 * @param  {Array}  list - array of valid semver tags
 * @param  {String} tag  - tag to push if valid
 * @return {Array}  modified `list`
 */
function validSemverTag(list, tag) {
    if (semver.valid(tag)) {
        list.push(tag);
    }

    return list;
}

/**
 * Retrieve a list of semver tags in descending order.
 *
 * @private
 * @return {Array} list of version tags
 */
function getVersionTags() {
    return execSilent("git tag")
        .trim()
        .split("\n")
        .reduce(validSemverTag, [])
        .sort(semver.compare);
}

/**
 * Create a release version, push tags and publish.
 *
 * @private
 * @param  {String} type - type of release to do (patch, minor, major)
 */
function release(type) {
    var newVersion;

    target.test();

    echo("Generating new version");
    newVersion = execSilent("npm version " + type).trim();

    target.changelog();

    // add changelog to commit
    exec("git add CHANGELOG.md");
    exec("git commit --amend --no-edit");

    // replace existing tag
    exec("git tag -f " + newVersion);

    // push all the things
    echo("Publishing to git");
    exec("git push origin master --tags");

    echo("Publishing to npm");
    exec("npm publish");
}

//------------------------------------------------------------------------------
// Tasks
//------------------------------------------------------------------------------

target.lint = function () {
    var errors = 0;

    echo("Linting makefile");
    errors += nodeCLI.exec("eslint", MAKEFILE).code;

    echo("Linting JavaScript files");
    errors += nodeCLI.exec("eslint", JS_FILES).code;

    echo("Linting JavaScript test files");
    errors += nodeCLI.exec("eslint", TEST_FILES).code;

    if (errors) {
        exit(1);
    }
};

/**
 * Verify that each rule is defined in index.js with the correct default
 * setting, has documentation and has tests.
 */
target.checkRules = function () {
    echo("Verifying rules");

    var errors = 0;

    var ruleFiles = find("lib/rules/").filter(fileType("js"));
    var configFile = require("./index");
    var readmeText = cat("./README.md");

    function isDefinedInConfig(rule) {
        return configFile.rules.hasOwnProperty(rule);
    }

    function hasDefaultSettingInConfig(rule) {
        var setting = configFile.rulesConfig[rule];

        return typeof setting === "number" ||
               (setting && typeof setting[0] === "number");
    }

    function isOffInConfig(rule) {
        var setting = configFile.rulesConfig[rule];

        return setting === 0 || (setting && setting[0] === 0);
    }

    function isOnInConfig(rule) {
        return !isOffInConfig(rule);
    }

    function isOffInReadme(rule) {
        var line = new RegExp("\\* \\[" + rule + "\\].*").exec(readmeText);

        return (line ? line[0] : "").indexOf("(off by default)") !== -1;
    }

    function isOnInReadme(rule) {
        return !isOffInReadme(rule);
    }

    function hasIdInTitle(docFile, id) {
        var docText = cat(docFile);
        var idInTitleRegExp = new RegExp("^# (.*?) \\(" + id + "\\)");

        return idInTitleRegExp.test(docText);
    }

    ruleFiles.forEach(function (filename) {
        var basename = path.basename(filename, ".js");
        var docFilename = "docs/rules/" + basename + ".md";
        var testFilename = "tests/lib/rules/" + basename + ".js";

        // Verify rule has documentation

        if (!test("-f", docFilename)) {
            console.error("Missing documentation for rule %s", basename);
            errors++;
        } else {
            if (readmeText.indexOf("(" + docFilename + ")") === -1) {
                console.error("Missing link to documentation for rule %s in index", basename);
                errors++;
            }

            // check for proper doc format
            if (!hasIdInTitle(docFilename, basename)) {
                console.error("Missing id in the doc page's title of rule %s", basename);
                errors++;
            }
        }

        // Verify rule has tests

        if (!test("-f", testFilename)) {
            console.error("Missing tests for rule %s", basename);
            errors++;
        }

        // Verify config

        if (!isDefinedInConfig(basename)) {
            console.error("Missing rule entry for %s in index.js", basename);
            errors++;
        }

        if (!hasDefaultSettingInConfig(basename)) {
            console.error("Missing default setting for %s in index.js", basename);
            errors++;
        }

        if (isOffInReadme(basename) && isOnInConfig(basename)) {
            console.error("README states that %s is off by default, but it is enabled in index.js.", basename);
            errors++;
        }

        if (isOffInConfig(basename) && isOnInReadme(basename)) {
            console.error("Missing '(off by default)' for rule %s in README", basename);
            errors++;
        }
    });

    if (errors) {
        exit(1);
    }
};

target.test = function () {
    var errors = 0;

    target.lint();
    target.checkRules();

    echo("Running test suite");

    errors += nodeCLI.exec("istanbul", "cover", MOCHA, "-- -R progress -t 3000 -c", TEST_FILES).code;
    errors += nodeCLI.exec("istanbul", "check-coverage", "--statement 100 --branch 95 --function 100 --lines 100").code;

    if (errors) {
        exit(1);
    }
};

target.changelog = function () {
    echo("Generating changelog");

    // get most recent two tags
    var tags = getVersionTags(),
        rangeTags = tags.slice(tags.length - 2),
        timestamp = dateformat(new Date(), "mmmm d, yyyy"),
        semverRe = /^\* \d\.\d.\d/;

    // output header
    ("### " + rangeTags[1] + " - " + timestamp + "\n").to("CHANGELOG.tmp");

    // get log statements
    var logs = execSilent("git log --pretty=format:\"* %s (%an)\" " + rangeTags.join("..")).split(/\n/g);

    logs = logs.filter(function (line) {
        return line.indexOf("Merge pull request") === -1 &&
               line.indexOf("Merge branch") === -1 &&
               !semverRe.test(line);
    });

    logs.push(""); // to create empty lines
    logs.unshift("");

    // output log statements
    logs.join("\n").toEnd("CHANGELOG.tmp");

    cat("CHANGELOG.tmp", "CHANGELOG.md").to("CHANGELOG.md.tmp");
    rm("CHANGELOG.tmp");
    rm("CHANGELOG.md");
    mv("CHANGELOG.md.tmp", "CHANGELOG.md");
};

target.patch = function () {
    release("patch");
};

target.minor = function () {
    release("minor");
};

target.major = function () {
    release("major");
};
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { I18nextProvider } from 'react-i18next'
import startI18n from '../tools/startI18n'
import { getTranslation } from '../tools/translationHelpers'
import Title from '../components/Title'

// get language from query parameter or url path
const lang = 'id'

export default class Homepage extends Component {
  static async getInitialProps () {
    const translations = await getTranslation(lang, 'common', 'http://localhost:3000/static/locales/')

    return { translations }
  }

  constructor (props) {
    super(props)

    this.i18n = startI18n(props.translations, lang)
  }

  render (props) {
    return (
      <I18nextProvider i18n={this.i18n}>
        <Title />
      </ I18nextProvider>
    )
  }
}
<commit_msg>Small typo (#2243)<commit_after>import React, { Component } from 'react'
import { I18nextProvider } from 'react-i18next'
import startI18n from '../tools/startI18n'
import { getTranslation } from '../tools/translationHelpers'
import Title from '../components/Title'

// get language from query parameter or url path
const lang = 'id'

export default class Homepage extends Component {
  static async getInitialProps () {
    const translations = await getTranslation(lang, 'common', 'http://localhost:3000/static/locales/')

    return { translations }
  }

  constructor (props) {
    super(props)

    this.i18n = startI18n(props.translations, lang)
  }

  render (props) {
    return (
      <I18nextProvider i18n={this.i18n}>
        <Title />
      </I18nextProvider>
    )
  }
}
<|endoftext|>


<commit_before>var terraformer = require('terraformer'),
  sql = require('sql-parser'),
  _ = require('lodash');

module.exports = {
  // process all query params filters
  filter: function( json, params, callback ){
    if ( params.geometry ){
      this.geometryFilter( json, params, callback );
    } else if ( params.where ){
      this.whereFilter( json, params, callback );
    } else {
      //probably better to parse all params upfront and confirm valid types
      if ( params.returnCountOnly === 'true' || params.returnCountOnly ){

        callback( null, { count: json.features.length });

      } else if ( params.returnIdsOnly === 'true' || params.returnIdsOnly){
        this.getIds( json, params.idField, params, function( err, ids ){
          callback( err, ids );
        });

      } else {

        if (params.orderByFields && params.orderByFields !== ''){
          var fld = params.orderByFields.split(' ');
          var order = params.orderByFields;
          if (fld[fld.length-1] == 'DESC'){
            order = '-' + params.orderByFields;
          }
          json.features = this.orderBy(json.features, params.orderByFields, order);
        }

        if ( ( params.returnGeometry === false || params.returnGeometry == 'false') && (!params.outFields || params.outFields != '*')){
          json.features.forEach(function(f){
            delete f.geometry;
          });
        } else if ( params.outSR && ( params.outSR == '102100' ) ){
          if ( json.spatialReference ){
            json.spatialReference.wkid = params.outSR;
            var coords;
            // project each geometry to merator
            json.features.forEach( function( f ){
              if ( f.geometry.x && f.geometry.y ) {
                coords = new terraformer.Point( [f.geometry.x, f.geometry.y] ).toMercator().coordinates;
                f.geometry.x = coords[0];
                f.geometry.y = coords[1];
              } else if ( f.geometry.rings ){
                f.geometry.rings = new terraformer.Polygon( f.geometry.rings ).toMercator().coordinates;
              } else if ( f.geometry.paths ) {
                f.geometry.paths = new terraformer.LineString( f.geometry.paths ).toMercator().coordinates;
              }
              f.geometry.spatialReference.wkid = params.outSR;
            });
          }
        }

        // checkout for outfields
        if ( params.outFields && params.outFields != '*' ){
          var features = [],
            props,
            outFields = params.outFields.split( ',' );

          json.features.forEach( function( f ){
            props = _.pick(f.attributes || f.properties, outFields);

            var newFeature = { geometry: f.geometry};
            if ( f.properties ) {
              newFeature.properties = props;
            } else {
              newFeature.attributes = props;
            }
            features.push( newFeature );
          });
          json.features = features;
        }

        // before we send back json, process outStats
        if ( params.outStatistics ){
          this.outStatistics( json, params, callback );
        } else {
          callback( null, json );
        }

      }

    }

  },

  calculateStat: function(type, field, features){
    var propName = (features[0].attributes) ? 'attributes' : 'properties';
    var types = {
      'min': function(field, features){
        var min = features[0][propName][field];
        features.forEach(function(f,i){
          if (f[propName][field] < min){
            min = f[propName][field];
          }
        });
        return min;
      },
      'max': function(field, features){
        var max = features[0][propName][field];
        features.forEach(function(f,i){
          if (f[propName][field] > max){
            max = f[propName][field];
          }
        });
        return max;
      },
      'count': function(field, features){
        var count = 0;
        features.forEach(function(f,i){
          if (f[propName][field]){
            count++;
          }
        });
        return count;
      },
      'sum': function(field, features){
        var sum = 0;
        features.forEach(function(f,i){
          if (f[propName][field]){
            sum += parseFloat(f[propName][field]);
          }
        });
        return sum;
      },
      'avg': function(field, features){
        var sum = types.sum(field, features);
        return sum/features.length;
      },
      'stddev': function(field, features){
        var v = types.var( field, features );
        return Math.sqrt(v);
      },
      'var': function(field, features){
        var avg = types.avg( field, features ),
          i = features.length,
          v = 0;

        while( i-- ){
          v += Math.pow( (features[ i ][propName][field] - avg), 2 );
        }
        v /= features.length;
        return v;
      }
    };
    //count | sum | avg | stddev | var
    return types[type](field, features);
  },

  outStatistics: function( json, params, callback ){
    var result = {}, value, self = this;
    try {
      json.fields = [];
      var stats = JSON.parse(params.outStatistics);
      if (stats.length){

        if ( params.groupByFieldsForStatistics ){
          this.buildUniqueFeatures(params, json.features, function( err, uniques ){
            if (err){
              return callback("unable to build unique features with property " + params.groupByFieldsForStatistics, null);
            }
            var statCollection = [];
            var finalJson = {fields:null, features:[]};

            for ( var value in uniques ){
              var statResult = self.buildStats( stats, { features: uniques[ value ] }, params );
              // always has one feature, must add the value of grouped attr
              statResult.features[0].attributes[ params.groupByFieldsForStatistics ] = value;
              finalJson.fields = statResult.fields;
              finalJson.features.push( statResult.features[0] );
            }

            // create a field entry for the grouped attribute
            finalJson.fields.push({
              name: params.groupByFieldsForStatistics,
              type: self.fieldType( finalJson.features[1].attributes[ params.groupByFieldsForStatistics ] ),
              alias: params.groupByFieldsForStatistics
            });

            return callback( null, finalJson );
          });
        } else {
          json = this.buildStats(stats, json, params);
          return callback( null, json );
        }
      } else {
        return callback("'outStatistics' parameter is invalid", null);
      }

    } catch (e){
      return callback("'outStatistics' parameter is invalid", null);
    }

  },

  buildUniqueFeatures: function( params, features, callback ){
    var self = this;
    var propName = (features[0].attributes) ? 'attributes' : 'properties';
    var uniqs = {}, prop;
    try {
      features.forEach(function(feature, i){
        prop = feature[propName][params.groupByFieldsForStatistics];
        if (prop){
          if (!uniqs[ prop ]){
            uniqs[ prop ] = [ feature ];
          } else {
            uniqs[ prop ].push( feature );
          }
        }
      });
      return callback(null, uniqs);
    } catch(e){
      return callback( e, null);
    }
  },

  buildStats: function(stats, json, params){
    var self = this;
    if (!json.fields) {
     json.fields = [];
    }
    var statFeatures = [{attributes:{}}];
    stats.forEach(function(stat, i){
      value = self.calculateStat( stat.statisticType, stat.onStatisticField, json.features );
      statFeatures[ 0 ].attributes[ stat.outStatisticFieldName ] = value;
      json.fields.push({
        name: stat.outStatisticFieldName,
        type: self.fieldType( value ),
        alias: stat.outStatisticFieldName
      });
    });
    json.features = statFeatures;
    return json;
  },

  fieldTypes: {
    'string': 'esriFieldTypeString',
    'integer': 'esriFieldTypeInteger',
    'date': 'esriFieldTypeDate',
    'datetime': 'esriFieldTypeDate',
    'float': 'esriFieldTypeDouble'
  },

  fieldType: function( value ){
    var type = typeof( value );
    if ( type == 'number'){
      type = ( this.isInt( value ) ) ? 'integer' : 'float';
    }
    return this.fieldTypes[ type ];
  },

  // is the value an integer?
  isInt: function( v ){
    return Math.round( v ) == v;
  },

  geometryTypes: {
    esriGeometryPoint: function( geom ){
      var coords = geom.split(',');
      return new terraformer.Point([ coords[0], coords[1] ]);
    },
    esriGeometryMultipoint: function( geom ){
      throw '';
    },
    esriGeometryPolyline: function( geom ){
      // not supported yet
    },
    esriGeometryPolygon: function( geom ){
      // not supported yet
    },
    esriGeometryEnvelope: function( geom ){
      if ( typeof( geom ) == 'object' ) {
        var box = new terraformer.Polygon( [[
          [geom.xmin, geom.ymin],
          [geom.xmin, geom.ymax],
          [geom.xmax, geom.ymax],
          [geom.xmax, geom.ymin],
          [geom.xmin, geom.ymin]
        ]]);
        if (geom.spatialReference && geom.spatialReference.wkid == '102100'){
          return box.toGeographic();
        } else {
          return box;
        }
      } else {
        geom = geom.split(',').map(function(v){ return parseFloat(v); });
        return new terraformer.Polygon([[
          [geom[0], geom[1]],
          [geom[0], geom[3]],
          [geom[2], geom[3]],
          [geom[2], geom[1]],
          [geom[0], geom[1]]
        ]]);
      }
    }
  },

  parseGeometry: function( geom, type ){
    try {
      geom = JSON.parse( geom );
      if ( geom.xmin ){
        return this.geometryTypes[ type ]( geom );
      } else {
        return this.geometryTypes[ type ]( geom );
      }
    } catch( e ){
      return this.geometryTypes[ type ]( geom );
    }
  },

  // NEED to support these:
  //========================================================================================================
    // esriSpatialRelIntersects
    // esriSpatialRelContains
    // esriSpatialRelCrosses
    // esriSpatialRelEnvelopeIntersects
    // esriSpatialRelIndexIntersects
    // esriSpatialRelOverlaps
    // esriSpatialRelTouches
    // esriSpatialRelRelation
  //========================================================================================================
  spatialFilter: {
    esriSpatialRelContains: function( features, geometry ){
      var self = this;
      return _.filter( features, function( f ){
        var featureGeom;
        if ( f.geometry.x && f.geometry.y ){
          featureGeom = new terraformer.Point([ f.geometry.x, f.geometry.y ]);
        } else if ( f.geometry.rings ){
          featureGeom = new terraformer.Polygon( f.geometry.rings );
        } else if ( f.geometry.paths ) {
          featureGeom = new terraformer.LineString( f.geometry.paths );
        }

        if ( featureGeom ) {
          return featureGeom.within( geometry );
        }
      });
    },
    esriSpatialRelWithin: function( features, geometry ){
      return _.filter( features, function( f ){
        var featureGeom;
        if ( f.geometry.x && f.geometry.y ){
          featureGeom = new terraformer.Point([ f.geometry.x, f.geometry.y ]);
        } else if ( f.geometry.rings ){
          featureGeom = new terraformer.Polygon([ f.geometry.rings ]);
        } else if ( f.geometry.paths ) {
          featureGeom = new terraformer.LineString( f.geometry.paths );
        }
        if ( featureGeom ) {
          return featureGeom.within(geometry);
        }
      });
    }
  },

  // subset the features by geometry
  // TODO support more that points
  geometryFilter: function(json, params, callback){

    // Parse the geometry
    var type = params.geometryType || 'esriGeometryEnvelope';
      delete params.geometryType;
    var geometry = this.parseGeometry( params.geometry , type );
      delete params.geometry;

    var spatialRel = params.spatialRel || 'esriSpatialRelContains';
    if ( this.spatialFilter[ spatialRel ] ){
      json.features = this.spatialFilter[spatialRel]( json.features, geometry);
    }

    // recycle the params after we run the geom filter
    this.filter( json, params, callback );
  },

  // process the where filter in the params
  // TODO actually support parsing where clauses
  whereFilter: function( json, params, callback ){
    var where = sql.parse( 'select * from foo where '+ params.where).where;

    var features = [];
    _.each(json.features, function(f){
      var props = f.attributes || f.properties;
      var param = where.conditions.left.value;
      var val = where.conditions.right.value;
      if ( ( this.whereOps[ where.conditions.operation ] && props[ param ] && this.whereOps[ where.conditions.operation ]( props[ param ], val ) ) || param == val ) {
        features.push( f );
      }
    }, this);
    json.features = features;

    delete params.where;
    // recycle the data + params through the filter fn
    this.filter( json, params, callback );
  },

  whereOps:{
    '<': function( param, val ){
      return (param < val);
    },
    '=': function( param, val ){
      return (param == val);
    },
    '==': function( param, val ){
      return (param == val);
    },
    '<=': function( param, val ){
      return (param <= val);
    },
    '>': function( param, val ){
      return (param > val);
    },
    '>=': function( param, val ){
      return (param >= val);
    }
  },

  orderBy: function(features, field, order){
    var dynamicSort = function(property) {
      var sortOrder = 1;
      if(property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
      }
      return function (a,b) {
        var result = (a.attributes[property] < b.attributes[property]) ? -1 : (a.attributes[property] > b.attributes[property]) ? 1 : 0;
        return result * sortOrder;
      };
    };
    features.sort(dynamicSort(order));
    return features;
  },

  // returns only the ids of the features ( when returnIdsOnly=true )
  getIds: function( json, field, params, callback ){
    field = field || 'id';
    var objectIds = [],
      props;

    if (params.orderByFields && params.orderByFields !== ''){
      var fld = params.orderByFields.split(' ');
      var order = params.orderByFields;
      if (fld[fld.length-1] == 'DESC'){
        order = '-' + params.orderByFields;
      }
      json.features = this.orderBy(json.features, params.orderByFields, order);
    }

    json.features.forEach( function(f){
      props = f.attributes || f.properties;
      objectIds.push( props[ field ] );
    });

    callback( null, {
      objectIdField: field,
      objectIds: objectIds
    });
  }

};
<commit_msg>supporting upper case stat types<commit_after>var terraformer = require('terraformer'),
  sql = require('sql-parser'),
  _ = require('lodash');

module.exports = {
  // process all query params filters
  filter: function( json, params, callback ){
    if ( params.geometry ){
      this.geometryFilter( json, params, callback );
    } else if ( params.where ){
      this.whereFilter( json, params, callback );
    } else {
      //probably better to parse all params upfront and confirm valid types
      if ( params.returnCountOnly === 'true' || params.returnCountOnly ){

        callback( null, { count: json.features.length });

      } else if ( params.returnIdsOnly === 'true' || params.returnIdsOnly){
        this.getIds( json, params.idField, params, function( err, ids ){
          callback( err, ids );
        });

      } else {

        if (params.orderByFields && params.orderByFields !== ''){
          var fld = params.orderByFields.split(' ');
          var order = params.orderByFields;
          if (fld[fld.length-1] == 'DESC'){
            order = '-' + params.orderByFields;
          }
          json.features = this.orderBy(json.features, params.orderByFields, order);
        }

        if ( ( params.returnGeometry === false || params.returnGeometry == 'false') && (!params.outFields || params.outFields != '*')){
          json.features.forEach(function(f){
            delete f.geometry;
          });
        } else if ( params.outSR && ( params.outSR == '102100' ) ){
          if ( json.spatialReference ){
            json.spatialReference.wkid = params.outSR;
            var coords;
            // project each geometry to merator
            json.features.forEach( function( f ){
              if ( f.geometry.x && f.geometry.y ) {
                coords = new terraformer.Point( [f.geometry.x, f.geometry.y] ).toMercator().coordinates;
                f.geometry.x = coords[0];
                f.geometry.y = coords[1];
              } else if ( f.geometry.rings ){
                f.geometry.rings = new terraformer.Polygon( f.geometry.rings ).toMercator().coordinates;
              } else if ( f.geometry.paths ) {
                f.geometry.paths = new terraformer.LineString( f.geometry.paths ).toMercator().coordinates;
              }
              f.geometry.spatialReference.wkid = params.outSR;
            });
          }
        }

        // checkout for outfields
        if ( params.outFields && params.outFields != '*' ){
          var features = [],
            props,
            outFields = params.outFields.split( ',' );

          json.features.forEach( function( f ){
            props = _.pick(f.attributes || f.properties, outFields);

            var newFeature = { geometry: f.geometry};
            if ( f.properties ) {
              newFeature.properties = props;
            } else {
              newFeature.attributes = props;
            }
            features.push( newFeature );
          });
          json.features = features;
        }

        // before we send back json, process outStats
        if ( params.outStatistics ){
          this.outStatistics( json, params, callback );
        } else {
          callback( null, json );
        }

      }

    }

  },

  calculateStat: function(type, field, features){
    var propName = (features[0].attributes) ? 'attributes' : 'properties';
    var types = {
      'min': function(field, features){
        var min = features[0][propName][field];
        features.forEach(function(f,i){
          if (f[propName][field] < min){
            min = f[propName][field];
          }
        });
        return min;
      },
      'max': function(field, features){
        var max = features[0][propName][field];
        features.forEach(function(f,i){
          if (f[propName][field] > max){
            max = f[propName][field];
          }
        });
        return max;
      },
      'count': function(field, features){
        var count = 0;
        features.forEach(function(f,i){
          if (f[propName][field]){
            count++;
          }
        });
        return count;
      },
      'sum': function(field, features){
        var sum = 0;
        features.forEach(function(f,i){
          if (f[propName][field]){
            sum += parseFloat(f[propName][field]);
          }
        });
        return sum;
      },
      'avg': function(field, features){
        var sum = types.sum(field, features);
        return sum/features.length;
      },
      'stddev': function(field, features){
        var v = types.var( field, features );
        return Math.sqrt(v);
      },
      'var': function(field, features){
        var avg = types.avg( field, features ),
          i = features.length,
          v = 0;

        while( i-- ){
          v += Math.pow( (features[ i ][propName][field] - avg), 2 );
        }
        v /= features.length;
        return v;
      }
    };
    //count | sum | avg | stddev | var
    return types[type.toLowerCase()](field, features);
  },

  outStatistics: function( json, params, callback ){
    var result = {}, value, self = this;
    try {
      json.fields = [];
      var stats = JSON.parse(params.outStatistics);
      if (stats.length){

        if ( params.groupByFieldsForStatistics ){
          this.buildUniqueFeatures(params, json.features, function( err, uniques ){
            if (err){
              return callback("unable to build unique features with property " + params.groupByFieldsForStatistics, null);
            }
            var statCollection = [];
            var finalJson = {fields:null, features:[]};

            for ( var value in uniques ){
              var statResult = self.buildStats( stats, { features: uniques[ value ] }, params );
              // always has one feature, must add the value of grouped attr
              statResult.features[0].attributes[ params.groupByFieldsForStatistics ] = value;
              finalJson.fields = statResult.fields;
              finalJson.features.push( statResult.features[0] );
            }

            // create a field entry for the grouped attribute
            finalJson.fields.push({
              name: params.groupByFieldsForStatistics,
              type: self.fieldType( finalJson.features[1].attributes[ params.groupByFieldsForStatistics ] ),
              alias: params.groupByFieldsForStatistics
            });

            return callback( null, finalJson );
          });
        } else {
          json = this.buildStats(stats, json, params);
          return callback( null, json );
        }
      } else {
        return callback("'outStatistics' parameter is invalid", null);
      }

    } catch (e){
      return callback("'outStatistics' parameter is invalid", null);
    }

  },

  buildUniqueFeatures: function( params, features, callback ){
    var self = this;
    var propName = (features[0].attributes) ? 'attributes' : 'properties';
    var uniqs = {}, prop;
    try {
      features.forEach(function(feature, i){
        prop = feature[propName][params.groupByFieldsForStatistics];
        if (prop){
          if (!uniqs[ prop ]){
            uniqs[ prop ] = [ feature ];
          } else {
            uniqs[ prop ].push( feature );
          }
        }
      });
      return callback(null, uniqs);
    } catch(e){
      return callback( e, null);
    }
  },

  buildStats: function(stats, json, params){
    var self = this;
    if (!json.fields) {
     json.fields = [];
    }
    var statFeatures = [{attributes:{}}];
    stats.forEach(function(stat, i){
      value = self.calculateStat( stat.statisticType, stat.onStatisticField, json.features );
      statFeatures[ 0 ].attributes[ stat.outStatisticFieldName ] = value;
      json.fields.push({
        name: stat.outStatisticFieldName,
        type: self.fieldType( value ),
        alias: stat.outStatisticFieldName
      });
    });
    json.features = statFeatures;
    return json;
  },

  fieldTypes: {
    'string': 'esriFieldTypeString',
    'integer': 'esriFieldTypeInteger',
    'date': 'esriFieldTypeDate',
    'datetime': 'esriFieldTypeDate',
    'float': 'esriFieldTypeDouble'
  },

  fieldType: function( value ){
    var type = typeof( value );
    if ( type == 'number'){
      type = ( this.isInt( value ) ) ? 'integer' : 'float';
    }
    return this.fieldTypes[ type ];
  },

  // is the value an integer?
  isInt: function( v ){
    return Math.round( v ) == v;
  },

  geometryTypes: {
    esriGeometryPoint: function( geom ){
      var coords = geom.split(',');
      return new terraformer.Point([ coords[0], coords[1] ]);
    },
    esriGeometryMultipoint: function( geom ){
      throw '';
    },
    esriGeometryPolyline: function( geom ){
      // not supported yet
    },
    esriGeometryPolygon: function( geom ){
      // not supported yet
    },
    esriGeometryEnvelope: function( geom ){
      if ( typeof( geom ) == 'object' ) {
        var box = new terraformer.Polygon( [[
          [geom.xmin, geom.ymin],
          [geom.xmin, geom.ymax],
          [geom.xmax, geom.ymax],
          [geom.xmax, geom.ymin],
          [geom.xmin, geom.ymin]
        ]]);
        if (geom.spatialReference && geom.spatialReference.wkid == '102100'){
          return box.toGeographic();
        } else {
          return box;
        }
      } else {
        geom = geom.split(',').map(function(v){ return parseFloat(v); });
        return new terraformer.Polygon([[
          [geom[0], geom[1]],
          [geom[0], geom[3]],
          [geom[2], geom[3]],
          [geom[2], geom[1]],
          [geom[0], geom[1]]
        ]]);
      }
    }
  },

  parseGeometry: function( geom, type ){
    try {
      geom = JSON.parse( geom );
      if ( geom.xmin ){
        return this.geometryTypes[ type ]( geom );
      } else {
        return this.geometryTypes[ type ]( geom );
      }
    } catch( e ){
      return this.geometryTypes[ type ]( geom );
    }
  },

  // NEED to support these:
  //========================================================================================================
    // esriSpatialRelIntersects
    // esriSpatialRelContains
    // esriSpatialRelCrosses
    // esriSpatialRelEnvelopeIntersects
    // esriSpatialRelIndexIntersects
    // esriSpatialRelOverlaps
    // esriSpatialRelTouches
    // esriSpatialRelRelation
  //========================================================================================================
  spatialFilter: {
    esriSpatialRelContains: function( features, geometry ){
      var self = this;
      return _.filter( features, function( f ){
        var featureGeom;
        if ( f.geometry.x && f.geometry.y ){
          featureGeom = new terraformer.Point([ f.geometry.x, f.geometry.y ]);
        } else if ( f.geometry.rings ){
          featureGeom = new terraformer.Polygon( f.geometry.rings );
        } else if ( f.geometry.paths ) {
          featureGeom = new terraformer.LineString( f.geometry.paths );
        }

        if ( featureGeom ) {
          return featureGeom.within( geometry );
        }
      });
    },
    esriSpatialRelWithin: function( features, geometry ){
      return _.filter( features, function( f ){
        var featureGeom;
        if ( f.geometry.x && f.geometry.y ){
          featureGeom = new terraformer.Point([ f.geometry.x, f.geometry.y ]);
        } else if ( f.geometry.rings ){
          featureGeom = new terraformer.Polygon([ f.geometry.rings ]);
        } else if ( f.geometry.paths ) {
          featureGeom = new terraformer.LineString( f.geometry.paths );
        }
        if ( featureGeom ) {
          return featureGeom.within(geometry);
        }
      });
    }
  },

  // subset the features by geometry
  // TODO support more that points
  geometryFilter: function(json, params, callback){

    // Parse the geometry
    var type = params.geometryType || 'esriGeometryEnvelope';
      delete params.geometryType;
    var geometry = this.parseGeometry( params.geometry , type );
      delete params.geometry;

    var spatialRel = params.spatialRel || 'esriSpatialRelContains';
    if ( this.spatialFilter[ spatialRel ] ){
      json.features = this.spatialFilter[spatialRel]( json.features, geometry);
    }

    // recycle the params after we run the geom filter
    this.filter( json, params, callback );
  },

  // process the where filter in the params
  // TODO actually support parsing where clauses
  whereFilter: function( json, params, callback ){
    var where = sql.parse( 'select * from foo where '+ params.where).where;

    var features = [];
    _.each(json.features, function(f){
      var props = f.attributes || f.properties;
      var param = where.conditions.left.value;
      var val = where.conditions.right.value;
      if ( ( this.whereOps[ where.conditions.operation ] && props[ param ] && this.whereOps[ where.conditions.operation ]( props[ param ], val ) ) || param == val ) {
        features.push( f );
      }
    }, this);
    json.features = features;

    delete params.where;
    // recycle the data + params through the filter fn
    this.filter( json, params, callback );
  },

  whereOps:{
    '<': function( param, val ){
      return (param < val);
    },
    '=': function( param, val ){
      return (param == val);
    },
    '==': function( param, val ){
      return (param == val);
    },
    '<=': function( param, val ){
      return (param <= val);
    },
    '>': function( param, val ){
      return (param > val);
    },
    '>=': function( param, val ){
      return (param >= val);
    }
  },

  orderBy: function(features, field, order){
    var dynamicSort = function(property) {
      var sortOrder = 1;
      if(property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
      }
      return function (a,b) {
        var result = (a.attributes[property] < b.attributes[property]) ? -1 : (a.attributes[property] > b.attributes[property]) ? 1 : 0;
        return result * sortOrder;
      };
    };
    features.sort(dynamicSort(order));
    return features;
  },

  // returns only the ids of the features ( when returnIdsOnly=true )
  getIds: function( json, field, params, callback ){
    field = field || 'id';
    var objectIds = [],
      props;

    if (params.orderByFields && params.orderByFields !== ''){
      var fld = params.orderByFields.split(' ');
      var order = params.orderByFields;
      if (fld[fld.length-1] == 'DESC'){
        order = '-' + params.orderByFields;
      }
      json.features = this.orderBy(json.features, params.orderByFields, order);
    }

    json.features.forEach( function(f){
      props = f.attributes || f.properties;
      objectIds.push( props[ field ] );
    });

    callback( null, {
      objectIdField: field,
      objectIds: objectIds
    });
  }

};
<|endoftext|>