<commit_before>

var jest = require('jest')
    ,models = require('../../models')
    ,common = require('../common')
    ,async = require('async')
    ,_ = require('underscore');

var CycleTimelineResource = module.exports = jest.Resource.extend({
    init:function(){
        this._super();
        this.allowed_methods = {
            get: {
                list:null
            }
        };
        this.authentication = new common.SessionAuthentication();
        this.filtering = {};
        this.sorting = {};
    },

    get_objects:function(req,filters,sorts,limit,offset,callback)
    {
        var arr = [];
        var cycle_id = req.query.cycle_id;
        async.parallel([
            function(cbk){
                    models.Cycle.findById(cycle_id, function(err, cycle){
                    if(!err){
                        var objs = [];
                        if(cycle){
                            _.each(cycle.admin_updates, function(admin_update){
                                if(admin_update){
                                    var obj = {
                                        type: "admin_update",
                                        text: admin_update.info,
                                        date: admin_update.date
                                    }
                                    objs.push(obj);
                                }
                            })

                            if(cycle.due_date){
                                var obj = {
                                    type: "due_date",
                                    date: cycle.due_date
                                }
                                objs.push(obj);
                            }

                            var obj = {
                                type: "cycle_creation",
                                date: cycle.creation_date
                            }

                            objs.push(obj);

                            var obj = {
                                type: "today",
                                date: Date.now()
                            }

                            objs.push(obj);

                            var discussion = _.find(cycle.discussions, function(discussion){ return discussion.is_main == true});

                            models.Discussion.findById(discussion.discussion)
                                .select({'_id': 1, 'title': 1, 'text_field_preivew': 1, 'image_field_preview': 1, 'creation_date': 1})
                                .exec(function(err, discussion_obj){
                                if(!err && discussion_obj){
                                    discussion_obj = JSON.parse(JSON.stringify(discussion_obj));
                                    discussion_obj.type = "discussion";
                                    discussion_obj.date = discussion_obj.creation_date;
                                    objs.push(discussion_obj);
                                }

                                cbk(err, objs);
                            });
                        }
                    }else
                        cbk(err, objs);
                });
            },

            function(cbk){
                models.Update.find({cycle: cycle_id})
                    .select({'_id': 1, 'title': 1, 'text_field': 1, 'image_field': 1, 'creation_date': 1})
                    .exec(function(err, updates){

                        updates = JSON.parse(JSON.stringify(updates));

                        if(!err){
                            _.each(updates, function(update){
                                update.type = "cycle_update";
                                update.date = update.creation_date;
                            })
                        }

                    cbk(err, updates);
                });
            },

            function(cbk){
                models.Action.find({cycle_id: cycle_id, is_approved: true})
                    .select({'_id': 1, 'title': 1, 'text_field_preivew': 1, 'image_field_preview': 1, 'num_of_going': 1, 'location': 1, 'execution_date': 1})
                    .exec(function(err, actions){
                    if(!err){
                        actions = JSON.parse(JSON.stringify(actions));
                        _.each(actions, function(action){
                            action.type = "action";
                            action.date = action.execution_date.date;
                            action.duration = action.execution_date.duration;
                        })
                    }

                    cbk(err, actions);
                });
            }

        ], function(err, args){

            arr = _.union.apply(_,args);
            _.each(arr, function(item){ item.date = new Date(item.date)})
            arr = _.sortBy(arr, function(item){ return Math.min(item.date);  });

            callback(null,{meta:{total_count: arr.length}, objects: arr});
        });
    }
});
<commit_msg>adding api/cycles/CycleTimeLineResource.js<commit_after>

var jest = require('jest')
    ,models = require('../../models')
    ,common = require('../common')
    ,async = require('async')
    ,_ = require('underscore');

var CycleTimelineResource = module.exports = jest.Resource.extend({
    init:function(){
        this._super();
        this.allowed_methods = {
            get: {
                list:null
            }
        };
        this.authentication = new common.SessionAuthentication();
        this.filtering = {};
        this.sorting = {};
    },

    get_objects:function(req,filters,sorts,limit,offset,callback)
    {
        var arr = [];
        var cycle_id = req.query.cycle_id;
        async.parallel([
            function(cbk){
                    models.Cycle.findById(cycle_id, function(err, cycle){
                    if(!err){
                        var objs = [];
                        if(cycle){
                            _.each(cycle.admin_updates, function(admin_update){
                                if(admin_update){
                                    var obj = {
                                        type: "admin_update",
                                        text: admin_update.info,
                                        date: admin_update.date
                                    }
                                    objs.push(obj);
                                }
                            })

                            if(cycle.due_date){
                                var obj = {
                                    type: "due_date",
                                    date: cycle.due_date
                                }
                                objs.push(obj);
                            }

                            var obj = {
                                type: "cycle_creation",
                                date: cycle.creation_date
                            }

                            objs.push(obj);

                            var obj = {
                                type: "today",
                                date: Date.now()
                            }

                            objs.push(obj);

                            var discussion = _.find(cycle.discussions, function(discussion){ return discussion.is_main == true});

                            models.Discussion.findById(discussion.discussion)
                                .select({'_id': 1, 'title': 1, 'text_field_preivew': 1, 'image_field_preview': 1, 'creation_date': 1})
                                .exec(function(err, discussion_obj){
                                if(!err && discussion_obj){
                                    discussion_obj = JSON.parse(JSON.stringify(discussion_obj));
                                    discussion_obj.type = "discussion";
                                    discussion_obj.date = discussion_obj.creation_date;
                                    objs.push(discussion_obj);
                                }

                                cbk(err, objs);
                            });
                        }
                    }else
                        cbk(err, objs);
                });
            },

            function(cbk){
                models.Update.find({cycle: cycle_id})
                    .select({'_id': 1, 'title': 1, 'text_field': 1, 'image_field': 1, 'creation_date': 1})
                    .exec(function(err, updates){

                        updates = JSON.parse(JSON.stringify(updates));

                        if(!err){
                            _.each(updates, function(update){
                                update.type = "cycle_update";
                                update.date = update.creation_date;
                            })
                        }

                    cbk(err, updates);
                });
            },

            function(cbk){
                models.Action.find({cycle_id: cycle_id, is_approved: true})
                    .select({'_id': 1, 'title': 1, 'text_field_preivew': 1, 'image_field_preview': 1, 'num_of_going': 1, 'location': 1, 'execution_date': 1})
                    .exec(function(err, actions){
                    if(!err){
                        actions = JSON.parse(JSON.stringify(actions));
                        _.each(actions, function(action){
                            action.type = "action";
                            action.date = action.execution_date.date;
                            action.duration = action.execution_date.duration;
                        })
                    }

                    cbk(err, actions);
                });
            }

        ], function(err, args){

            arr = _.union.apply(_,args);
            _.each(arr, function(item){ item.date = new Date(item.date)})
            arr = _.sortBy(arr, function(item){ return Math.min(item.date);  });

            callback(null,{meta:{total_count: arr.length}, objects: arr});
        });
    }
});
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
  Schema = mongoose.Schema;

/**
 * Place Schema
 */
var PlaceSchema = new Schema({
  // name, like "Alpine"
  name: {
    type: String,
    required: true,
    trim: true
  },
  // suffix, like "Beer Company"
  suffix: {
    type: String,
    trim: true
  },
  // sublocation, like "Tasting Room"
  sublocation: {
    type: String,
    trim: true
  },
  // not sure if this could be defaulting to other values
  // like (name +' '+ sublocation).toLowerCase.replace(spaces with -)
  slug: {
	type: String,
	required: true,
	trim: true
  },
  // some Booleans for checkboxes of amenities
  // growler fills
  growlers: {
	type: Boolean,
	default: false
  },
  // family friendly
  fams: {
	type: Boolean,
	default: false
  },
  // dog friendly
  dogs: {
	type: Boolean,
	default: false
  },
  // food trucks
  trucks: {
	type: Boolean,
	default: false
  },
  // food service
  foods: {
	type: Boolean,
	default: false
  },
  // addr = Address 1 & 2 if its there since who cares
  addr: {
    type: String,
    trim: true
  },
  city: {
    type: String,
    trim: true
  },
  state: {
    type: String,
    trim: true
  },
  zip: {
    type: String,
    trim: true
  },
  // does phone number need a set / validator to handle formatting?!
  phone: {
    type: String,
    trim: true
  },
  // does www need a set / validator to store
  // without any "http://www." at the front & / at the end?
  www: {
    type: String,
    trim: true
  },
  // latitude & longitude
  // get converted in 'coords" object Virtual
  lat: {
    type: Number,
    trim: true
  },
  lng: {
    type: Number,
    trim: true
  },
  // social media links & IDs & such...
  twitter_user_name: {
    type: String,
    trim: true
  },
  twitter_user_id: {
    type: String,
    trim: true
  },
  twitter_user_img: {
    type: String,
    trim: true
  },
  instagram_user_name: {
    type: String,
    trim: true
  },
  instagram_user_id: {
    type: String,
    trim: true
  },
  instagram_brewloc_id: {
    type: String,
    trim: true
  },
  facebook_url: {
    type: String,
    trim: true
  },
  facebook_page_id: {
    type: String,
    trim: true
  },
  facebook_brewloc_id: {
    type: String,
    trim: true
  },
  // and then?
});

/**
 * Validations?
 *
ArticleSchema.path('title').validate(function(title) {
  return !!title;
}, 'Title cannot be blank');

ArticleSchema.path('content').validate(function(content) {
  return !!content;
}, 'Content cannot be blank');

/**
 * Virtuals
 */
// for mapping place.coords = { latitude, longitude }
PlaceSchema.virtual( 'coords' ).get(function() {
	return {
		latitude: this.lat,
		longitude: this.lng
	};
});
// "City, State Zip"
PlaceSchema.virtual( 'CSZ' ).get(function() {
	return this.city +', '+ this.city +' '+ this.zip;
});
// get full address in 1 line
PlaceSchema.virtual( 'fullAddr' ).get(function() {
	return this.addr +', '+ this.CSZ;
});
// store a couple versions of fullName combined for easier things?
PlaceSchema.virtual( 'fullName' ).get(function() {
	return this.name + ( this.sublocation !== '' ? (' <small>' + this.sublocation + '</small>') : '' );
});
/**
 * Statics
 */
// findOne Place given an id & execute callback
PlaceSchema.statics.load = function(id, cb) {
  this.findOne({
    _id: id
  })
  //.populate('user', 'name username').exec(cb);
  .exec(cb);
};
// findOne Place given a slug & execute callback?
PlaceSchema.statics.findBySlug = function(slug, cb) {
  this.findOne({
    slug: slug //new RegExp( slug, 'i' )
  })
  //.populate('user', 'name username').exec(cb);
  .exec(cb);
};

// compile our model
mongoose.model('Place', PlaceSchema);
<commit_msg>set toObject & toJSON virtuals = true so that they are there on the Angular front end too<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
  Schema = mongoose.Schema;

/**
 * Place Schema
 */
var PlaceSchema = new Schema({
  // name, like "Alpine"
  name: {
    type: String,
    required: true,
    trim: true
  },
  // suffix, like "Beer Company"
  suffix: {
    type: String,
    trim: true
  },
  // sublocation, like "Tasting Room"
  sublocation: {
    type: String,
    trim: true
  },
  // not sure if this could be defaulting to other values
  // like (name +' '+ sublocation).toLowerCase.replace(spaces with -)
  slug: {
	type: String,
	required: true,
	trim: true
  },
  // some Booleans for checkboxes of amenities
  // growler fills
  growlers: {
	type: Boolean,
	default: false
  },
  // family friendly
  fams: {
	type: Boolean,
	default: false
  },
  // dog friendly
  dogs: {
	type: Boolean,
	default: false
  },
  // food trucks
  trucks: {
	type: Boolean,
	default: false
  },
  // food service
  foods: {
	type: Boolean,
	default: false
  },
  // addr = Address 1 & 2 if its there since who cares
  addr: {
    type: String,
    trim: true
  },
  city: {
    type: String,
    trim: true
  },
  state: {
    type: String,
    trim: true
  },
  zip: {
    type: String,
    trim: true
  },
  // does phone number need a set / validator to handle formatting?!
  phone: {
    type: String,
    trim: true
  },
  // does www need a set / validator to store
  // without any "http://www." at the front & / at the end?
  www: {
    type: String,
    trim: true
  },
  // latitude & longitude
  // get converted in 'coords" object Virtual
  lat: {
    type: Number,
    trim: true
  },
  lng: {
    type: Number,
    trim: true
  },
  // social media links & IDs & such...
  twitter_user_name: {
    type: String,
    trim: true
  },
  twitter_user_id: {
    type: String,
    trim: true
  },
  twitter_user_img: {
    type: String,
    trim: true
  },
  instagram_user_name: {
    type: String,
    trim: true
  },
  instagram_user_id: {
    type: String,
    trim: true
  },
  instagram_brewloc_id: {
    type: String,
    trim: true
  },
  facebook_url: {
    type: String,
    trim: true
  },
  facebook_page_id: {
    type: String,
    trim: true
  },
  facebook_brewloc_id: {
    type: String,
    trim: true
  },
  // and then?
}, {
	toObject: {
		virtuals: true
	},
	toJSON: {
		virtuals: true
	}
});

/**
 * Validations?
 *
ArticleSchema.path('title').validate(function(title) {
  return !!title;
}, 'Title cannot be blank');

ArticleSchema.path('content').validate(function(content) {
  return !!content;
}, 'Content cannot be blank');

/**
 * Virtuals
 */
// for mapping place.coords = { latitude, longitude }
PlaceSchema.virtual( 'coords' ).get(function() {
	return {
		latitude: this.lat,
		longitude: this.lng
	};
});
// "City, State Zip"
PlaceSchema.virtual( 'CSZ' ).get(function() {
	return this.city +', '+ this.city +' '+ this.zip;
});
// get full address in 1 line
PlaceSchema.virtual( 'fullAddr' ).get(function() {
	return this.addr +', '+ this.CSZ;
});
// name ("Alpine") + suffix ("Beer Co")
PlaceSchema.virtual( 'fullName' ).get(function() {
	return this.name + ( this.suffix !== '' ? (' ' + this.suffix ) : '' );
});
/**
 * Statics
 */
// findOne Place given an id & execute callback
PlaceSchema.statics.load = function(id, cb) {
  this.findOne({
    _id: id
  })
  //.populate('user', 'name username').exec(cb);
  .exec(cb);
};
// findOne Place given a slug & execute callback?
PlaceSchema.statics.findBySlug = function(slug, cb) {
  this.findOne({
    slug: slug //new RegExp( slug, 'i' )
  })
  //.populate('user', 'name username').exec(cb);
  .exec(cb);
};

// compile our model
mongoose.model('Place', PlaceSchema);
<|endoftext|>


<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.BabylonLoader = function ( manager ) {

	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

};

THREE.BabylonLoader.prototype = {

	constructor: THREE.ObjectLoader,

	load: function ( url, onLoad, onProgress, onError ) {

		var scope = this;

		var loader = new THREE.XHRLoader( scope.manager );
		loader.setCrossOrigin( this.crossOrigin );
		loader.load( url, function ( text ) {

			onLoad( scope.parse( JSON.parse( text ) ) );

		} );

	},

	setCrossOrigin: function ( value ) {

		this.crossOrigin = value;

	},

	parse: function ( json ) {

		var materials = this.parseMaterials( json.materials );
		var scene = this.parseObjects( json, materials );

		return scene;

	},

	parseMaterials: function ( json ) {

		var materials = {};

		for ( var i = 0, l = json.length; i < l; i ++ ) {

			var data = json[ i ];

			var material = new THREE.MeshPhongMaterial();
			material.name = data.name;
			material.ambient.fromArray( data.ambient );
			material.color.fromArray( data.diffuse );
			material.emissive.fromArray( data.emissive );
			material.specular.fromArray( data.specular );
			material.shininess = data.specularPower;
			material.opacity = data.alpha;

			materials[ data.id ] = material;

		}

		return materials;

	},

	parseObjects: function ( json, materials ) {

		var objects = {};
		var scene = new THREE.Scene();

		var lights = json.lights;

		for ( var i = 0, l = lights.length; i < l; i ++ ) {

			var data = lights[ i ];

			var light;

			switch ( data.type ) {

				case 0:
					light = new THREE.PointLight();
					break;

				case 1:
					light = new THREE.DirectionalLight();
					break;

				case 2:
					light = new BABYLON.SpotLight();
					break;

				case 3:
					light = new BABYLON.HemisphericLight();
					break;
			}

			light.name = data.name;
			light.position.fromArray( data.data );
			light.color.fromArray( data.diffuse );
			if ( data.intensity ) light.intensity = data.intensity;

			objects[ data.id ] = light;

			scene.add( light );

		}

		var meshes = json.meshes;

		for ( var i = 0, l = meshes.length; i < l; i ++ ) {

			var data = meshes[ i ];

			var object;

			if ( data.indices !== null  ) {

				var geometry = new THREE.BufferGeometry();

				geometry.attributes.index = {
					itemSize: 1,
					array: new Uint16Array( data.indices )
				};

				var strideSize = 0;
				var positions = [];
				var normals = [];

				switch ( data.uvCount ) {

					case 0:
						strideSize = 6; // [3, 3]
						break;
					case 1:
						strideSize = 8; // [3, 3, 2]
						break;
					case 2:
						strideSize = 10; // [3, 3, 2, 2]
						break;

				}

				var vertices = data.vertices;

				for ( var j = 0, jl = vertices.length; j < jl; j += strideSize ) {

					positions.push(
						vertices[ j ],
						vertices[ j + 1 ],
						- vertices[ j + 2 ]
					);

					normals.push(
						vertices[ j + 3 ],
						vertices[ j + 4 ],
						- vertices[ j + 5 ]
					);
				}

				geometry.attributes.position = {
					itemSize: 3,
					array: new Float32Array( positions )
				};

				geometry.attributes.normal = {
					itemSize: 3,
					array: new Float32Array( normals )
				};

				var subMeshes = data.subMeshes;

				if ( subMeshes !== null ) {

					for ( var j = 0, jl = subMeshes.length; j < jl; j ++ ) {

						var subMesh = subMeshes[ j ];

						geometry.offsets.push( {
							start: subMesh.indexStart,
							index: 0,
							count: subMesh.indexCount
						} );

					}

				} else {

					geometry.offsets.push( {
						start: 0,
						index: 0,
						count: data.indices.length
					} );

				}

				var material = materials[ data.materialId ];

				object = new THREE.Mesh( geometry, material );

			} else {

				object = new THREE.Object3D();

			}

			object.name = data.name;
			object.position.set( data.position[ 0 ], data.position[ 1 ], - data.position[ 2 ] );
			object.rotation.fromArray( data.rotation );
			object.scale.fromArray( data.scaling );

			if ( data.parentId !== undefined && data.parentId !== '' ) {

				objects[ data.parentId ].add( object );

			} else {

				scene.add( object );

			}

			objects[ data.id ] = object;

		}

		return scene;

	}

};
<commit_msg>BabylonLoader: Inverting z for lights too.<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 */

THREE.BabylonLoader = function ( manager ) {

	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;

};

THREE.BabylonLoader.prototype = {

	constructor: THREE.ObjectLoader,

	load: function ( url, onLoad, onProgress, onError ) {

		var scope = this;

		var loader = new THREE.XHRLoader( scope.manager );
		loader.setCrossOrigin( this.crossOrigin );
		loader.load( url, function ( text ) {

			onLoad( scope.parse( JSON.parse( text ) ) );

		} );

	},

	setCrossOrigin: function ( value ) {

		this.crossOrigin = value;

	},

	parse: function ( json ) {

		var materials = this.parseMaterials( json.materials );
		var scene = this.parseObjects( json, materials );

		return scene;

	},

	parseMaterials: function ( json ) {

		var materials = {};

		for ( var i = 0, l = json.length; i < l; i ++ ) {

			var data = json[ i ];

			var material = new THREE.MeshPhongMaterial();
			material.name = data.name;
			material.ambient.fromArray( data.ambient );
			material.color.fromArray( data.diffuse );
			material.emissive.fromArray( data.emissive );
			material.specular.fromArray( data.specular );
			material.shininess = data.specularPower;
			material.opacity = data.alpha;

			materials[ data.id ] = material;

		}

		return materials;

	},

	parseObjects: function ( json, materials ) {

		var objects = {};
		var scene = new THREE.Scene();

		var lights = json.lights;

		for ( var i = 0, l = lights.length; i < l; i ++ ) {

			var data = lights[ i ];

			var light;

			switch ( data.type ) {

				case 0:
					light = new THREE.PointLight();
					break;

				case 1:
					light = new THREE.DirectionalLight();
					break;

				case 2:
					light = new BABYLON.SpotLight();
					break;

				case 3:
					light = new BABYLON.HemisphericLight();
					break;
			}

			light.name = data.name;
			light.position.set( data.data[ 0 ], data.data[ 1 ], - data.data[ 2 ] );
			light.color.fromArray( data.diffuse );
			if ( data.intensity ) light.intensity = data.intensity;

			objects[ data.id ] = light;

			scene.add( light );

		}

		var meshes = json.meshes;

		for ( var i = 0, l = meshes.length; i < l; i ++ ) {

			var data = meshes[ i ];

			var object;

			if ( data.indices !== null  ) {

				var geometry = new THREE.BufferGeometry();

				geometry.attributes.index = {
					itemSize: 1,
					array: new Uint16Array( data.indices )
				};

				var strideSize = 0;
				var positions = [];
				var normals = [];

				switch ( data.uvCount ) {

					case 0:
						strideSize = 6; // [3, 3]
						break;
					case 1:
						strideSize = 8; // [3, 3, 2]
						break;
					case 2:
						strideSize = 10; // [3, 3, 2, 2]
						break;

				}

				var vertices = data.vertices;

				for ( var j = 0, jl = vertices.length; j < jl; j += strideSize ) {

					positions.push(
						vertices[ j ],
						vertices[ j + 1 ],
						- vertices[ j + 2 ]
					);

					normals.push(
						vertices[ j + 3 ],
						vertices[ j + 4 ],
						- vertices[ j + 5 ]
					);
				}

				geometry.attributes.position = {
					itemSize: 3,
					array: new Float32Array( positions )
				};

				geometry.attributes.normal = {
					itemSize: 3,
					array: new Float32Array( normals )
				};

				var subMeshes = data.subMeshes;

				if ( subMeshes !== null ) {

					for ( var j = 0, jl = subMeshes.length; j < jl; j ++ ) {

						var subMesh = subMeshes[ j ];

						geometry.offsets.push( {
							start: subMesh.indexStart,
							index: 0,
							count: subMesh.indexCount
						} );

					}

				} else {

					geometry.offsets.push( {
						start: 0,
						index: 0,
						count: data.indices.length
					} );

				}

				var material = materials[ data.materialId ];

				object = new THREE.Mesh( geometry, material );

			} else {

				object = new THREE.Object3D();

			}

			object.name = data.name;
			object.position.set( data.position[ 0 ], data.position[ 1 ], - data.position[ 2 ] );
			object.rotation.fromArray( data.rotation );
			object.scale.fromArray( data.scaling );

			if ( data.parentId !== undefined && data.parentId !== '' ) {

				objects[ data.parentId ].add( object );

			} else {

				scene.add( object );

			}

			objects[ data.id ] = object;

		}

		return scene;

	}

};
<|endoftext|>


<commit_before>import test from 'ava'
import { transformFileSync } from '@babel/core'
import { readFileSync, symlink, unlink } from 'fs'
import { resolve as r } from 'path'
import prettier from 'prettier'
import treeshake from '../treeshake'
import rewrite from '../rewrite'

const SPECS = [
  {
    title: 'should transform VEUI components to help treeshaking',
    name: 'treeshake',
    plugins: [treeshake, [treeshake, { alias: 'veui-next' }, 'veui-next']]
  },
  {
    title: 'should rewrite package name to given alias',
    name: 'rewrite',
    plugins: [[rewrite, { alias: 'veui-next' }]]
  }
]

SPECS.forEach(({ title, name, plugins }) => {
  test(title, t => {
    return prepare()
      .then(() => {
        let { code } = transformFileSync(
          r(__dirname, `./fixtures/${name}/source.js`),
          {
            babelrc: false,
            plugins
          }
        )

        let expect = readFileSync(
          r(__dirname, `./fixtures/${name}/expected.js`),
          'utf8'
        )
        expect = expect.replace(/\r\n/gm, '\n')
        code = code.replace(/\r\n/gm, '\n')

        t.is(format(code), format(expect))
      })
      .then(tearDown)
  })
})

function format (source) {
  return prettier.format(source, { parser: 'babel' })
}

function prepare () {
  return new Promise(resolve => {
    symlink(
      r(__dirname, '../node_modules/veui'),
      r(__dirname, '../node_modules/veui-next'),
      resolve
    )
  })
}

function tearDown () {
  return new Promise(resolve => {
    unlink(r(__dirname, '../node_modules/veui-next'), resolve)
  })
}
<commit_msg>test: use a semaphore to prevent racing conditions<commit_after>import test from 'ava'
import { transformFileSync } from '@babel/core'
import { readFileSync, symlink, unlink } from 'fs'
import { resolve as r } from 'path'
import prettier from 'prettier'
import treeshake from '../treeshake'
import rewrite from '../rewrite'

const SPECS = [
  {
    title: 'should transform VEUI components to help treeshaking',
    name: 'treeshake',
    plugins: [treeshake, [treeshake, { alias: 'veui-next' }, 'veui-next']]
  },
  {
    title: 'should rewrite package name to given alias',
    name: 'rewrite',
    plugins: [[rewrite, { alias: 'veui-next' }]]
  }
]

let running = 0

SPECS.forEach(({ title, name, plugins }) => {
  test(title, t => {
    return prepare()
      .then(() => {
        let { code } = transformFileSync(
          r(__dirname, `./fixtures/${name}/source.js`),
          {
            babelrc: false,
            plugins
          }
        )

        let expect = readFileSync(
          r(__dirname, `./fixtures/${name}/expected.js`),
          'utf8'
        )
        expect = expect.replace(/\r\n/gm, '\n')
        code = code.replace(/\r\n/gm, '\n')

        t.is(format(code), format(expect))
      })
      .then(tearDown)
  })
})

function format (source) {
  return prettier.format(source, { parser: 'babel' })
}

function prepare () {
  running++
  return new Promise(resolve => {
    if (running === 1) {
      symlink(
        r(__dirname, '../node_modules/veui'),
        r(__dirname, '../node_modules/veui-next'),
        resolve
      )
      return
    }
    resolve()
  })
}

function tearDown () {
  running--

  return new Promise(resolve => {
    if (running === 0) {
      unlink(r(__dirname, '../node_modules/veui-next'), resolve)
      return
    }
    resolve()
  })
}
<|endoftext|>


<commit_before>import debugModule from "debug";
const debug = debugModule("debugger:evm:sagas");

import { put, takeEvery, select } from "redux-saga/effects";
import { prefixName, keccak256 } from "lib/helpers";

import { TICK } from "lib/trace/actions";
import * as actions from "../actions";

import evm from "../selectors";

import * as trace from "lib/trace/sagas";

/**
 * Adds EVM bytecode context
 *
 * @return {string} ID (0x-prefixed keccak of binary)
 */
export function* addContext(context) {
  //get context hash if context doesn't already have it
  const contextHash =
    context.context || keccak256({ type: "string", value: context.binary });
  //NOTE: we take hash as *string*, not as bytes, because the binary may
  //contain link references!

  debug("context %O", context);
  yield put(actions.addContext({ ...context, context: contextHash }));

  return contextHash;
}

/**
 * Adds to codex known deployed instance of binary at address
 * (not to list of affected instances)
 *
 * @param {string} binary - may be undefined (e.g. precompiles)
 * @return {string} ID (0x-prefixed keccak of binary)
 */
export function* addInstance(address, binary) {
  const search = yield select(evm.info.binaries.search);
  const context = search(binary);

  //now, whether we needed a new context or not, add the instance
  yield put(actions.addInstance(address, context, binary));

  return context;
}

/**
 * Adds known deployed instance of binary at address
 * to list of affected instances, *not* to codex
 *
 * @param {string} binary - may be undefined (e.g. precompiles)
 * @return {string} ID (0x-prefixed keccak of binary)
 */
export function* addAffectedInstance(address, binary) {
  const search = yield select(evm.info.binaries.search);
  const context = search(binary);

  //now, whether we needed a new context or not, add the instance
  yield put(actions.addAffectedInstance(address, context, binary));

  return context;
}

//goes through all instances (codex & affected) and re-adds them with their new
//context (used if new contexts have been added -- something
//that currently only happens when adding external compilations)
export function* refreshInstances() {
  const instances = yield select(evm.current.codex.instances);
  const affectedInstances = yield select(evm.transaction.displayInstances);
  for (let [address, { binary }] of Object.entries(instances)) {
    const search = yield select(evm.info.binaries.search);
    const context = search(binary);
    yield put(actions.addInstance(address, context, binary));
  }
  for (let [address, { binary }] of Object.entries(affectedInstances)) {
    const search = yield select(evm.info.binaries.search);
    const context = search(binary);
    yield put(actions.addAffectedInstance(address, context, binary));
  }
}

export function* begin({
  address,
  binary,
  data,
  storageAddress,
  status,
  sender,
  value,
  gasprice,
  block
}) {
  yield put(actions.saveGlobals(sender, gasprice, block));
  yield put(actions.saveStatus(status));
  debug("codex: %O", yield select(evm.current.codex));
  if (address) {
    yield put(actions.call(address, data, storageAddress, sender, value));
  } else {
    yield put(actions.create(binary, storageAddress, sender, value));
  }
}

function* tickSaga() {
  debug("got TICK");

  yield* callstackAndCodexSaga();
  yield* trace.signalTickSagaCompletion();
}

//NOTE: We don't account here for multiple simultaneous returns.
//Such a case is *vanishingly* unlikely to come up in real code
//so it's simply not worth the trouble.  Such a case will screw
//up the debugger pretty good as a result.
//(...but I might go back and do it later. :P )

export function* callstackAndCodexSaga() {
  if (yield select(evm.current.step.isExceptionalHalting)) {
    //let's handle this case first so we can be sure everything else is *not*
    //an exceptional halt
    debug("exceptional halt!");

    yield put(actions.fail());
  } else if (yield select(evm.current.step.isInstantCallOrCreate)) {
    // if there is no binary (e.g. for precompiles or externally owned
    // accounts), or if the call fails instantly (callstack overflow or not
    // enough ether), there will be no trace steps for the called code, and so
    // we shouldn't tell the debugger that we're entering another execution
    // context
    // (so we do nothing)
  } else if (yield select(evm.current.step.isCall)) {
    debug("got call");

    let address = yield select(evm.current.step.callAddress);
    let data = yield select(evm.current.step.callData);

    debug("calling address %s", address);

    if (yield select(evm.current.step.isDelegateCallStrict)) {
      //if delegating, leave storageAddress, sender, and value the same
      let { storageAddress, sender, value } = yield select(evm.current.call);
      yield put(actions.call(address, data, storageAddress, sender, value));
    } else {
      //this branch covers CALL, CALLCODE, and STATICCALL
      let currentCall = yield select(evm.current.call);
      let storageAddress = (yield select(evm.current.step.isDelegateCallBroad))
        ? currentCall.storageAddress //for CALLCODE
        : address;
      let sender = currentCall.storageAddress; //not the code address!
      let value = yield select(evm.current.step.callValue); //0 if static
      yield put(actions.call(address, data, storageAddress, sender, value));
    }
  } else if (yield select(evm.current.step.isCreate)) {
    debug("got create");
    let binary = yield select(evm.current.step.createBinary);
    let createdAddress = yield select(evm.current.step.createdAddress);
    let value = yield select(evm.current.step.createValue);
    let sender = (yield select(evm.current.call)).storageAddress;
    //not the code address!

    yield put(actions.create(binary, createdAddress, sender, value));
    //as above, storageAddress handles when calling from a creation call
  } else if (yield select(evm.current.step.isNormalHalting)) {
    debug("got return");

    let { binary, storageAddress } = yield select(evm.current.call);

    if (binary) {
      //if we're returning from a successful creation call, let's log the
      //result
      let returnedBinary = yield select(evm.current.step.returnValue);
      let search = yield select(evm.info.binaries.search);
      let returnedContext = search(returnedBinary);
      yield put(
        actions.returnCreate(storageAddress, returnedBinary, returnedContext)
      );
    } else {
      yield put(actions.returnCall());
    }
  } else if (yield select(evm.current.step.isStore)) {
    let storageAddress = (yield select(evm.current.call)).storageAddress;
    let slot = yield select(evm.current.step.storageAffected);
    let storedValue = yield select(evm.current.step.valueStored);
    yield put(actions.store(storageAddress, slot, storedValue));
  } else if (yield select(evm.current.step.isLoad)) {
    let storageAddress = (yield select(evm.current.call)).storageAddress;
    let slot = yield select(evm.current.step.storageAffected);
    let loadedValue = yield select(evm.current.step.valueLoaded);
    yield put(actions.load(storageAddress, slot, loadedValue));
  }
}

export function* reset() {
  let initialCall = yield select(evm.transaction.initialCall);
  yield put(actions.reset());
  yield put(initialCall);
}

export function* unload() {
  yield put(actions.unloadTransaction());
}

export function* saga() {
  yield takeEvery(TICK, tickSaga);
}

export default prefixName("evm", saga);
<commit_msg>Fix outdated selector name<commit_after>import debugModule from "debug";
const debug = debugModule("debugger:evm:sagas");

import { put, takeEvery, select } from "redux-saga/effects";
import { prefixName, keccak256 } from "lib/helpers";

import { TICK } from "lib/trace/actions";
import * as actions from "../actions";

import evm from "../selectors";

import * as trace from "lib/trace/sagas";

/**
 * Adds EVM bytecode context
 *
 * @return {string} ID (0x-prefixed keccak of binary)
 */
export function* addContext(context) {
  //get context hash if context doesn't already have it
  const contextHash =
    context.context || keccak256({ type: "string", value: context.binary });
  //NOTE: we take hash as *string*, not as bytes, because the binary may
  //contain link references!

  debug("context %O", context);
  yield put(actions.addContext({ ...context, context: contextHash }));

  return contextHash;
}

/**
 * Adds to codex known deployed instance of binary at address
 * (not to list of affected instances)
 *
 * @param {string} binary - may be undefined (e.g. precompiles)
 * @return {string} ID (0x-prefixed keccak of binary)
 */
export function* addInstance(address, binary) {
  const search = yield select(evm.info.binaries.search);
  const context = search(binary);

  //now, whether we needed a new context or not, add the instance
  yield put(actions.addInstance(address, context, binary));

  return context;
}

/**
 * Adds known deployed instance of binary at address
 * to list of affected instances, *not* to codex
 *
 * @param {string} binary - may be undefined (e.g. precompiles)
 * @return {string} ID (0x-prefixed keccak of binary)
 */
export function* addAffectedInstance(address, binary) {
  const search = yield select(evm.info.binaries.search);
  const context = search(binary);

  //now, whether we needed a new context or not, add the instance
  yield put(actions.addAffectedInstance(address, context, binary));

  return context;
}

//goes through all instances (codex & affected) and re-adds them with their new
//context (used if new contexts have been added -- something
//that currently only happens when adding external compilations)
export function* refreshInstances() {
  const instances = yield select(evm.current.codex.instances);
  const affectedInstances = yield select(evm.transaction.affectedInstances);
  for (let [address, { binary }] of Object.entries(instances)) {
    const search = yield select(evm.info.binaries.search);
    const context = search(binary);
    yield put(actions.addInstance(address, context, binary));
  }
  for (let [address, { binary }] of Object.entries(affectedInstances)) {
    const search = yield select(evm.info.binaries.search);
    const context = search(binary);
    yield put(actions.addAffectedInstance(address, context, binary));
  }
}

export function* begin({
  address,
  binary,
  data,
  storageAddress,
  status,
  sender,
  value,
  gasprice,
  block
}) {
  yield put(actions.saveGlobals(sender, gasprice, block));
  yield put(actions.saveStatus(status));
  debug("codex: %O", yield select(evm.current.codex));
  if (address) {
    yield put(actions.call(address, data, storageAddress, sender, value));
  } else {
    yield put(actions.create(binary, storageAddress, sender, value));
  }
}

function* tickSaga() {
  debug("got TICK");

  yield* callstackAndCodexSaga();
  yield* trace.signalTickSagaCompletion();
}

//NOTE: We don't account here for multiple simultaneous returns.
//Such a case is *vanishingly* unlikely to come up in real code
//so it's simply not worth the trouble.  Such a case will screw
//up the debugger pretty good as a result.
//(...but I might go back and do it later. :P )

export function* callstackAndCodexSaga() {
  if (yield select(evm.current.step.isExceptionalHalting)) {
    //let's handle this case first so we can be sure everything else is *not*
    //an exceptional halt
    debug("exceptional halt!");

    yield put(actions.fail());
  } else if (yield select(evm.current.step.isInstantCallOrCreate)) {
    // if there is no binary (e.g. for precompiles or externally owned
    // accounts), or if the call fails instantly (callstack overflow or not
    // enough ether), there will be no trace steps for the called code, and so
    // we shouldn't tell the debugger that we're entering another execution
    // context
    // (so we do nothing)
  } else if (yield select(evm.current.step.isCall)) {
    debug("got call");

    let address = yield select(evm.current.step.callAddress);
    let data = yield select(evm.current.step.callData);

    debug("calling address %s", address);

    if (yield select(evm.current.step.isDelegateCallStrict)) {
      //if delegating, leave storageAddress, sender, and value the same
      let { storageAddress, sender, value } = yield select(evm.current.call);
      yield put(actions.call(address, data, storageAddress, sender, value));
    } else {
      //this branch covers CALL, CALLCODE, and STATICCALL
      let currentCall = yield select(evm.current.call);
      let storageAddress = (yield select(evm.current.step.isDelegateCallBroad))
        ? currentCall.storageAddress //for CALLCODE
        : address;
      let sender = currentCall.storageAddress; //not the code address!
      let value = yield select(evm.current.step.callValue); //0 if static
      yield put(actions.call(address, data, storageAddress, sender, value));
    }
  } else if (yield select(evm.current.step.isCreate)) {
    debug("got create");
    let binary = yield select(evm.current.step.createBinary);
    let createdAddress = yield select(evm.current.step.createdAddress);
    let value = yield select(evm.current.step.createValue);
    let sender = (yield select(evm.current.call)).storageAddress;
    //not the code address!

    yield put(actions.create(binary, createdAddress, sender, value));
    //as above, storageAddress handles when calling from a creation call
  } else if (yield select(evm.current.step.isNormalHalting)) {
    debug("got return");

    let { binary, storageAddress } = yield select(evm.current.call);

    if (binary) {
      //if we're returning from a successful creation call, let's log the
      //result
      let returnedBinary = yield select(evm.current.step.returnValue);
      let search = yield select(evm.info.binaries.search);
      let returnedContext = search(returnedBinary);
      yield put(
        actions.returnCreate(storageAddress, returnedBinary, returnedContext)
      );
    } else {
      yield put(actions.returnCall());
    }
  } else if (yield select(evm.current.step.isStore)) {
    let storageAddress = (yield select(evm.current.call)).storageAddress;
    let slot = yield select(evm.current.step.storageAffected);
    let storedValue = yield select(evm.current.step.valueStored);
    yield put(actions.store(storageAddress, slot, storedValue));
  } else if (yield select(evm.current.step.isLoad)) {
    let storageAddress = (yield select(evm.current.call)).storageAddress;
    let slot = yield select(evm.current.step.storageAffected);
    let loadedValue = yield select(evm.current.step.valueLoaded);
    yield put(actions.load(storageAddress, slot, loadedValue));
  }
}

export function* reset() {
  let initialCall = yield select(evm.transaction.initialCall);
  yield put(actions.reset());
  yield put(initialCall);
}

export function* unload() {
  yield put(actions.unloadTransaction());
}

export function* saga() {
  yield takeEvery(TICK, tickSaga);
}

export default prefixName("evm", saga);
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Cinderella Girls gallery enhance
// @namespace    http://github.com/Yoctillion
// @version      1.2
// @author       Yoctillion
// @description  Unlock cards and download
// @include      http://sp.pf.mbga.jp/12008305/?guid=ON&*url=http%3A%2F%2F125.6.169.35%2Fidolmaster%2Fidol_gallery%2Fidol_detail%2F*
// @require      https://raw.githubusercontent.com/Stuk/jszip/master/dist/jszip.min.js
// @require      https://raw.githubusercontent.com/eligrey/FileSaver.js/master/FileSaver.min.js
// @updateURL    https://raw.githubusercontent.com/Yoctillion/CinderellaGirlsGalleryEnhance/master/gallery-enhance.user.js
// @downloadURL  https://raw.githubusercontent.com/Yoctillion/CinderellaGirlsGalleryEnhance/master/gallery-enhance.user.js
// @license      https://github.com/Yoctillion/CinderellaGirlsGalleryEnhance/blob/master/LICENSE
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// ==/UserScript==

(function() {
    const resources = {
        "en": {
            buttonText:  "Save all images",
            downloading: "Downloading... {0}/{1}/{2}",
            packaing:    "Packaging...",
            finished:    "Finished",
            failed:      ", {0} failed"
        },
        "zh-CN": {
            buttonText:  "保存全部图片",
            downloading: "下载中…… {0}/{1}/{2}",
            packaing:    "打包中……",
            finished:    "已完成",
            failed:      "，其中 {0} 失败"
        }
    };

    let resource = resources[navigator.language];
    if (resource === undefined) resource = resources["en"];

    const ImageType = {
        large:    "l",
        m2:       "m2",
        xs:       "xs",
        quest:    "quest",
        noframe:  "l_noframe",
        premium:  "l_premium"
    };

    let cardNames = [];
    let idolName;

    String.prototype.format = function() {
        var args = arguments;
        return this.replace(/{(\d+)}/g, function(match, number) {
            return args[number] !== undefined
                ? args[number]
                : match;
        });
    };

    function updateIdol(idol) {
        let images = idol.images;
        for (let i = 0; i < idol.detail_list.length; i++) {
            let detail = unsafeWindow.idol.detail_list[i];
            cardNames[i] = detail.data.card_name;
            detail.is_exist_archive = true;
            detail.archive.normal = "1";

            let rarity = parseInt(detail.data.rarity);

            let card = new Card(detail.data.hash_card_id);
            if (rarity >= 5) { // SR / SR+
                detail.show_card_type = "l";
                detail.archive.premium = "1";

                // no way to get sign id

                if (!images.l[i])          images.l[i]          = card.getSignUrl(ImageType.large);
                if (!images.l_premium[i])  images.l_premium[i]  = card.getPremiumUrl(ImageType.large);
                if (!images.l_nosign[i])   images.l_nosign[i]   = card.getNoSignUrl(ImageType.large);
                if (!images.l_nosign_p[i]) images.l_nosign_p[i] = card.getNoSignPremiumUrl(ImageType.large);
                if (!images.l_noframe[i])  images.l_noframe[i]  = card.getLargeNoFrameUrl();
            } else {    // N / N+ / R / R+
                if (!images.l[i]) images.l[i] = card.getCardUrl(ImageType.large);
            }
            if (!images.m2[i])    images.m2[i]    = card.getCardUrl(ImageType.m2);
            if (!images.quest[i]) images.quest[i] = card.getCardUrl(ImageType.quest);
        }
    }

    class Card {
        constructor(hashId) {
            this.baseUrl = "http://sp.pf-img-a.mbga.jp/12008305/?guid=ON&url=http%3A%2F%2F125.6.169.35%2Fidolmaster%2F";
            this.hashId  = hashId;
        }

        getCardUrl(size) {
            return this.getNoSignUrl(size);
        }

        getSignUrl(size) {
            return this.getImageUrl("card_sign_b", size);
        }

        getNoSignUrl(size) {
            return this.getImageUrl("card", size);
        }

        getPremiumUrl(size) {
            return this.getImageUrl("card_sign_p", size);
        }

        getNoSignPremiumUrl(size) {
            return this.getImageUrl("card_sign_no_p", size);
        }

        getLargeNoFrameUrl() {
            return this.getImageUrl("card", "l_noframe");
        }

        getImageUrl(type, size) {
            return this.baseUrl + "image_sp%2F" + type + "%2F" + size + "%2F" + this.hashId + ".jpg";
            // no way to get version
        }

        getSignJSUrl(id) {
            return baseUrl + "js%2Fcjs%2Fpremium%2Fsign_effect_" + id + ".js";
            // no way to get version
        }
    }

    // manage tasks
    // ugly and unstable
    class Task {
        constructor() {
            this.completed = false;
            this.tasks = [];
            this.parent = null;
            this.onCompleted = null;
        }

        complete() {
            if (this.tasks.length === 0) {
                this.completed = true;
            }

            if (this.parent) {
                this.parent.notify(this);
            }
        }

        isCompleted() {
            if (this.tasks.length === 0) {
                return this.completed;
            }

            for (let t of this.tasks) {
                if (!t.isCompleted()) {
                    return false;
                }
            }
            return true;
        }

        wait(task) {
            task.parent = this;
            this.tasks.push(task);
        }

        notify(task) {
            if (this.isCompleted()) {
                if (this.onCompleted) {
                    this.onCompleted();
                }
                this.complete();
            }
        }
    }

    function download(zip, url, path, counter) {
        let task = new Task();

        GM_xmlhttpRequest({
            method:       "GET",
            url:          url,
            responseType: "blob",
            onload:
            resp => {
                if (resp.status === 200) {
                    zip.file(path, resp.response, {binary: true});
                    console.log(path + " completed");
                    counter.success += 1;
                }
                else {
                    console.log(path + " fail: " + url + " " + resp.status + " " + resp.statusText);
                    counter.fail += 1;
                }

                task.complete();
                counter.update();
            }
        });
        return task;
    }

    function downloadAll(zip, urls, folder, counter, cardNames) {
        let task = new Task();
        let indexes = Object.keys(urls);
        for (let i of indexes) {
            if (urls[i]) {
                task.wait(download(zip, urls[i], folder + "/" + cardNames[i] + ".jpg", counter));
            }
        }
        return task;
    }

    function insertAfter(newNode, referenceNode) {
        referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
    }

    function count(arr) {
        let result = 0;
        for (let x of arr) {
            if (x) {
                result++;
            }
        }
        return result;
    }

    let idol    = unsafeWindow.idol;

    let images  = idol.images;
    let large   = images[ImageType.large];
    let premium = images[ImageType.premium];
    let noframe = images[ImageType.noframe];

    idolName  = idol.detail_list[0].data.real_name;

    // remove class
    const notExistClass = "not_exist";
    let not_exists = document.getElementsByClassName(notExistClass);
    while(not_exists.length > 0) {
        not_exists[0].innerHTML = "";
        not_exists[0].classList.remove(notExistClass);
    }

    updateIdol(idol);

    // download button
    let btn = document.createElement("button");

    // style
    btn.className = "grayButton300";
    btn.setAttribute("style", "position: relative;");

    btn.innerHTML = resource.buttonText;

    btn.onclick = () => {
        class Counter {
            constructor() {
                this.success = 0;
                this.fail    = 0;
                this.total   = 0;
            }

            update() {
                btn.innerHTML = resource.downloading.format(this.success, this.fail, this.total);
            }
        }

        btn.disabled = true;

        let counter = new Counter();
        counter.total = count(large, "l") + count(premium, "p") + count(noframe, "n");
        counter.update();

        let zip = new JSZip();

        let task = new Task();
        task.wait(downloadAll(zip, large, "large", counter, cardNames));
        task.wait(downloadAll(zip, premium, "premium", counter, cardNames));
        task.wait(downloadAll(zip, noframe, "noframe", counter, cardNames));

        task.onCompleted = () => {
            btn.innerHTML = resource.packaing;

            zip.generateAsync({type:"blob"})
                .then(content => {
                    saveAs(content, idolName + ".zip");

                    btn.innerHTML = resource.finished + (counter.fail > 0 ? resource.failed.format(counter.fail) : "");

                    setTimeout(() => {
                        btn.disabled = false;
                        btn.innerHTML = resource.buttonText;
                    }, 3000);
                });
        };
    };

    let naviIcon = document.getElementsByClassName("icon_navi")[0];
    insertAfter(btn, naviIcon);
})();
<commit_msg>Version<commit_after>// ==UserScript==
// @name         Cinderella Girls gallery enhance
// @namespace    http://github.com/Yoctillion
// @version      1.3
// @author       Yoctillion
// @description  Unlock cards and download
// @include      http://sp.pf.mbga.jp/12008305/?guid=ON&*url=http%3A%2F%2F125.6.169.35%2Fidolmaster%2Fidol_gallery%2Fidol_detail%2F*
// @require      https://raw.githubusercontent.com/Stuk/jszip/master/dist/jszip.min.js
// @require      https://raw.githubusercontent.com/eligrey/FileSaver.js/master/FileSaver.min.js
// @updateURL    https://raw.githubusercontent.com/Yoctillion/CinderellaGirlsGalleryEnhance/master/gallery-enhance.user.js
// @downloadURL  https://raw.githubusercontent.com/Yoctillion/CinderellaGirlsGalleryEnhance/master/gallery-enhance.user.js
// @license      https://github.com/Yoctillion/CinderellaGirlsGalleryEnhance/blob/master/LICENSE
// @grant        GM_xmlhttpRequest
// @run-at       document-end
// ==/UserScript==

(function() {
    const resources = {
        "en": {
            buttonText:  "Save all images",
            downloading: "Downloading... {0}/{1}/{2}",
            packaing:    "Packaging...",
            finished:    "Finished",
            failed:      ", {0} failed"
        },
        "zh-CN": {
            buttonText:  "保存全部图片",
            downloading: "下载中…… {0}/{1}/{2}",
            packaing:    "打包中……",
            finished:    "已完成",
            failed:      "，其中 {0} 失败"
        }
    };

    let resource = resources[navigator.language];
    if (resource === undefined) resource = resources["en"];

    const ImageType = {
        large:    "l",
        m2:       "m2",
        xs:       "xs",
        quest:    "quest",
        noframe:  "l_noframe",
        premium:  "l_premium"
    };

    let cardNames = [];
    let idolName;

    String.prototype.format = function() {
        var args = arguments;
        return this.replace(/{(\d+)}/g, function(match, number) {
            return args[number] !== undefined
                ? args[number]
                : match;
        });
    };

    function updateIdol(idol) {
        let images = idol.images;
        for (let i = 0; i < idol.detail_list.length; i++) {
            let detail = unsafeWindow.idol.detail_list[i];
            cardNames[i] = detail.data.card_name;
            detail.is_exist_archive = true;
            detail.archive.normal = "1";

            let rarity = parseInt(detail.data.rarity);

            let card = new Card(detail.data.hash_card_id);
            if (rarity >= 5) { // SR / SR+
                detail.show_card_type = "l";
                detail.archive.premium = "1";

                // no way to get sign id

                if (!images.l[i])          images.l[i]          = card.getSignUrl(ImageType.large);
                if (!images.l_premium[i])  images.l_premium[i]  = card.getPremiumUrl(ImageType.large);
                if (!images.l_nosign[i])   images.l_nosign[i]   = card.getNoSignUrl(ImageType.large);
                if (!images.l_nosign_p[i]) images.l_nosign_p[i] = card.getNoSignPremiumUrl(ImageType.large);
                if (!images.l_noframe[i])  images.l_noframe[i]  = card.getLargeNoFrameUrl();
            } else {    // N / N+ / R / R+
                if (!images.l[i]) images.l[i] = card.getCardUrl(ImageType.large);
            }
            if (!images.m2[i])    images.m2[i]    = card.getCardUrl(ImageType.m2);
            if (!images.quest[i]) images.quest[i] = card.getCardUrl(ImageType.quest);
        }
    }

    class Card {
        constructor(hashId) {
            this.baseUrl = "http://sp.pf-img-a.mbga.jp/12008305/?guid=ON&url=http%3A%2F%2F125.6.169.35%2Fidolmaster%2F";
            this.hashId  = hashId;
        }

        getCardUrl(size) {
            return this.getNoSignUrl(size);
        }

        getSignUrl(size) {
            return this.getImageUrl("card_sign_b", size);
        }

        getNoSignUrl(size) {
            return this.getImageUrl("card", size);
        }

        getPremiumUrl(size) {
            return this.getImageUrl("card_sign_p", size);
        }

        getNoSignPremiumUrl(size) {
            return this.getImageUrl("card_sign_no_p", size);
        }

        getLargeNoFrameUrl() {
            return this.getImageUrl("card", "l_noframe");
        }

        getImageUrl(type, size) {
            return this.baseUrl + "image_sp%2F" + type + "%2F" + size + "%2F" + this.hashId + ".jpg";
            // no way to get version
        }

        getSignJSUrl(id) {
            return baseUrl + "js%2Fcjs%2Fpremium%2Fsign_effect_" + id + ".js";
            // no way to get version
        }
    }

    // manage tasks
    // ugly and unstable
    class Task {
        constructor() {
            this.completed = false;
            this.tasks = [];
            this.parent = null;
            this.onCompleted = null;
        }

        complete() {
            if (this.tasks.length === 0) {
                this.completed = true;
            }

            if (this.parent) {
                this.parent.notify(this);
            }
        }

        isCompleted() {
            if (this.tasks.length === 0) {
                return this.completed;
            }

            for (let t of this.tasks) {
                if (!t.isCompleted()) {
                    return false;
                }
            }
            return true;
        }

        wait(task) {
            task.parent = this;
            this.tasks.push(task);
        }

        notify(task) {
            if (this.isCompleted()) {
                if (this.onCompleted) {
                    this.onCompleted();
                }
                this.complete();
            }
        }
    }

    function download(zip, url, path, counter) {
        let task = new Task();

        GM_xmlhttpRequest({
            method:       "GET",
            url:          url,
            responseType: "blob",
            onload:
            resp => {
                if (resp.status === 200) {
                    zip.file(path, resp.response, {binary: true});
                    console.log(path + " completed");
                    counter.success += 1;
                }
                else {
                    console.log(path + " fail: " + url + " " + resp.status + " " + resp.statusText);
                    counter.fail += 1;
                }

                task.complete();
                counter.update();
            }
        });
        return task;
    }

    function downloadAll(zip, urls, folder, counter, cardNames) {
        let task = new Task();
        let indexes = Object.keys(urls);
        for (let i of indexes) {
            if (urls[i]) {
                task.wait(download(zip, urls[i], folder + "/" + cardNames[i] + ".jpg", counter));
            }
        }
        return task;
    }

    function insertAfter(newNode, referenceNode) {
        referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
    }

    function count(arr) {
        let result = 0;
        for (let x of arr) {
            if (x) {
                result++;
            }
        }
        return result;
    }

    let idol    = unsafeWindow.idol;

    let images  = idol.images;
    let large   = images[ImageType.large];
    let premium = images[ImageType.premium];
    let noframe = images[ImageType.noframe];

    idolName  = idol.detail_list[0].data.real_name;

    // remove class
    const notExistClass = "not_exist";
    let not_exists = document.getElementsByClassName(notExistClass);
    while(not_exists.length > 0) {
        not_exists[0].innerHTML = "";
        not_exists[0].classList.remove(notExistClass);
    }

    updateIdol(idol);

    // download button
    let btn = document.createElement("button");

    // style
    btn.className = "grayButton300";
    btn.setAttribute("style", "position: relative;");

    btn.innerHTML = resource.buttonText;

    btn.onclick = () => {
        class Counter {
            constructor() {
                this.success = 0;
                this.fail    = 0;
                this.total   = 0;
            }

            update() {
                btn.innerHTML = resource.downloading.format(this.success, this.fail, this.total);
            }
        }

        btn.disabled = true;

        let counter = new Counter();
        counter.total = count(large, "l") + count(premium, "p") + count(noframe, "n");
        counter.update();

        let zip = new JSZip();

        let task = new Task();
        task.wait(downloadAll(zip, large, "large", counter, cardNames));
        task.wait(downloadAll(zip, premium, "premium", counter, cardNames));
        task.wait(downloadAll(zip, noframe, "noframe", counter, cardNames));

        task.onCompleted = () => {
            btn.innerHTML = resource.packaing;

            zip.generateAsync({type:"blob"})
                .then(content => {
                    saveAs(content, idolName + ".zip");

                    btn.innerHTML = resource.finished + (counter.fail > 0 ? resource.failed.format(counter.fail) : "");

                    setTimeout(() => {
                        btn.disabled = false;
                        btn.innerHTML = resource.buttonText;
                    }, 3000);
                });
        };
    };

    let naviIcon = document.getElementsByClassName("icon_navi")[0];
    insertAfter(btn, naviIcon);
})();
<|endoftext|>


<commit_before>// Beaver: A beaver in the game.

var Class = require("classe");
var log = require(__basedir + "/gameplay/log");
var GameObject = require("./gameObject");

//<<-- Creer-Merge: requires -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

//<<-- /Creer-Merge: requires -->>

// @class Beaver: A beaver in the game.
var Beaver = Class(GameObject, {
    /**
     * Initializes Beavers.
     *
     * @param {Object} data - a simple mapping passed in to the constructor with whatever you sent with it. GameSettings are in here by key/value as well.
     */
    init: function(data) {
        GameObject.init.apply(this, arguments);

        /**
         * The number of actions remaining for the beaver this turn.
         *
         * @type {number}
         */
        this.actions = this.actions || 0;

        /**
         * The number of branches this beaver is holding.
         *
         * @type {number}
         */
        this.branches = this.branches || 0;

        /**
         * Number of turns this beaver is distracted for (0 means not distracted).
         *
         * @type {number}
         */
        this.distracted = this.distracted || 0;

        /**
         * The number of fish this beaver is holding.
         *
         * @type {number}
         */
        this.fish = this.fish || 0;

        /**
         * How much health this beaver has left.
         *
         * @type {number}
         */
        this.health = this.health || 0;

        /**
         * The Job this beaver was recruited to do.
         *
         * @type {Job}
         */
        this.job = this.job || null;

        /**
         * How many moves this beaver has left this turn.
         *
         * @type {number}
         */
        this.moves = this.moves || 0;

        /**
         * The Player that owns and can control this beaver.
         *
         * @type {Player}
         */
        this.owner = this.owner || null;

        /**
         * The tile this beaver is on.
         *
         * @type {Tile}
         */
        this.tile = this.tile || null;

        //<<-- Creer-Merge: init -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        for(var key of this.job) {
            this[key] = this.job[key];
        }

        //<<-- /Creer-Merge: init -->>
    },

    gameObjectName: "Beaver",

    /**
     * Attacks another adjacent beaver.
     *
     * @param {Player} player - the player that called this.
     * @param {Tile} tile - The tile of the beaver you want to attack.
     * @param {function} asyncReturn - if you nest orders in this function you must return that value via this function in the order's callback.
     * @returns {boolean} True if successfully attacked, false otherwise.
     */
    attack: function(player, tile, asyncReturn) {
        // <<-- Creer-Merge: attack -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // Developer: Put your game logic for the Beaver's attack function here
        return false;

        // <<-- /Creer-Merge: attack -->>
    },

    /**
     * Builds a lodge on the Beavers current tile.
     *
     * @param {Player} player - the player that called this.
     * @param {function} asyncReturn - if you nest orders in this function you must return that value via this function in the order's callback.
     * @returns {boolean} True if successfully built a lodge, false otherwise.
     */
    buildLodge: function(player, asyncReturn) {
        // <<-- Creer-Merge: buildLodge -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // Developer: Put your game logic for the Beaver's buildLodge function here
        return false;

        // <<-- /Creer-Merge: buildLodge -->>
    },

    /**
     * Drops some of the given resource on the beaver's tile. Fish dropped in water disappear instantly, and fish dropped on land die one per tile per turn.
     *
     * @param {Player} player - the player that called this.
     * @param {string} resource - The type of resource to drop ('branch' or 'fish').
     * @param {number} amount - The amount of the resource to drop, numbers <= 0 will drop all of that type.
     * @param {function} asyncReturn - if you nest orders in this function you must return that value via this function in the order's callback.
     * @returns {boolean} True if successfully dropped the resource, false otherwise.
     */
    drop: function(player, resource, amount, asyncReturn) {
        // <<-- Creer-Merge: drop -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // Developer: Put your game logic for the Beaver's drop function here
        return false;

        // <<-- /Creer-Merge: drop -->>
    },

    /**
     * Harvests the branches or fish from a Spawner on an adjacent tile.
     *
     * @param {Player} player - the player that called this.
     * @param {Tile} tile - The tile you want to harvest.
     * @param {function} asyncReturn - if you nest orders in this function you must return that value via this function in the order's callback.
     * @returns {boolean} True if successfully harvested, false otherwise.
     */
    harvest: function(player, tile, asyncReturn) {
        // <<-- Creer-Merge: harvest -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // Developer: Put your game logic for the Beaver's harvest function here
        return false;

        // <<-- /Creer-Merge: harvest -->>
    },

    /**
     * Moves this beaver from its current tile to an adjacent tile.
     *
     * @param {Player} player - the player that called this.
     * @param {Tile} tile - The tile this beaver should move to. Costs 2 moves normally, 3 if moving upstream, and 1 if moving downstream.
     * @param {function} asyncReturn - if you nest orders in this function you must return that value via this function in the order's callback.
     * @returns {boolean} True if the move worked, false otherwise.
     */
    move: function(player, tile, asyncReturn) {
        // <<-- Creer-Merge: move -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // Developer: Put your game logic for the Beaver's move function here
        return false;

        // <<-- /Creer-Merge: move -->>
    },

    /**
     * Picks up some branches or fish on the beaver's tile.
     *
     * @param {Player} player - the player that called this.
     * @param {string} resource - The type of resource to pickup ('branch' or 'fish').
     * @param {number} amount - The amount of the resource to drop, numbers <= 0 will pickup all of that type.
     * @param {function} asyncReturn - if you nest orders in this function you must return that value via this function in the order's callback.
     * @returns {boolean} True if successfully picked up a resource, false otherwise.
     */
    pickup: function(player, resource, amount, asyncReturn) {
        // <<-- Creer-Merge: pickup -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // Developer: Put your game logic for the Beaver's pickup function here
        return false;

        // <<-- /Creer-Merge: pickup -->>
    },

    //<<-- Creer-Merge: added-functions -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

    // You can add additional functions here. These functions will not be directly callable by client AIs

    //<<-- /Creer-Merge: added-functions -->>

});

module.exports = Beaver;
<commit_msg>Started working on the initial checks for beaverDrop<commit_after>// Beaver: A beaver in the game.

var Class = require("classe");
var log = require(__basedir + "/gameplay/log");
var GameObject = require("./gameObject");

//<<-- Creer-Merge: requires -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

//<<-- /Creer-Merge: requires -->>

// @class Beaver: A beaver in the game.
var Beaver = Class(GameObject, {
    /**
     * Initializes Beavers.
     *
     * @param {Object} data - a simple mapping passed in to the constructor with whatever you sent with it. GameSettings are in here by key/value as well.
     */
    init: function(data) {
        GameObject.init.apply(this, arguments);

        /**
         * The number of actions remaining for the beaver this turn.
         *
         * @type {number}
         */
        this.actions = this.actions || 0;

        /**
         * The number of branches this beaver is holding.
         *
         * @type {number}
         */
        this.branches = this.branches || 0;

        /**
         * Number of turns this beaver is distracted for (0 means not distracted).
         *
         * @type {number}
         */
        this.distracted = this.distracted || 0;

        /**
         * The number of fish this beaver is holding.
         *
         * @type {number}
         */
        this.fish = this.fish || 0;

        /**
         * How much health this beaver has left.
         *
         * @type {number}
         */
        this.health = this.health || 0;

        /**
         * The Job this beaver was recruited to do.
         *
         * @type {Job}
         */
        this.job = this.job || null;

        /**
         * How many moves this beaver has left this turn.
         *
         * @type {number}
         */
        this.moves = this.moves || 0;

        /**
         * The Player that owns and can control this beaver.
         *
         * @type {Player}
         */
        this.owner = this.owner || null;

        /**
         * The tile this beaver is on.
         *
         * @type {Tile}
         */
        this.tile = this.tile || null;

        //<<-- Creer-Merge: init -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        for(var key of this.job) {
            this[key] = this.job[key];
        }

        //<<-- /Creer-Merge: init -->>
    },

    gameObjectName: "Beaver",

    /**
     * Attacks another adjacent beaver.
     *
     * @param {Player} player - the player that called this.
     * @param {Tile} tile - The tile of the beaver you want to attack.
     * @param {function} asyncReturn - if you nest orders in this function you must return that value via this function in the order's callback.
     * @returns {boolean} True if successfully attacked, false otherwise.
     */
    attack: function(player, tile, asyncReturn) {
        // <<-- Creer-Merge: attack -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // Developer: Put your game logic for the Beaver's attack function here
        return false;

        // <<-- /Creer-Merge: attack -->>
    },

    /**
     * Builds a lodge on the Beavers current tile.
     *
     * @param {Player} player - the player that called this.
     * @param {function} asyncReturn - if you nest orders in this function you must return that value via this function in the order's callback.
     * @returns {boolean} True if successfully built a lodge, false otherwise.
     */
    buildLodge: function(player, asyncReturn) {
        // <<-- Creer-Merge: buildLodge -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // Developer: Put your game logic for the Beaver's buildLodge function here
        return false;

        // <<-- /Creer-Merge: buildLodge -->>
    },

    /**
     * Drops some of the given resource on the beaver's tile. Fish dropped in water disappear instantly, and fish dropped on land die one per tile per turn.
     *
     * @param {Player} player - the player that called this.
     * @param {string} resource - The type of resource to drop ('branch' or 'fish').
     * @param {number} amount - The amount of the resource to drop, numbers <= 0 will drop all of that type.
     * @param {function} asyncReturn - if you nest orders in this function you must return that value via this function in the order's callback.
     * @returns {boolean} True if successfully dropped the resource, false otherwise.
     */
    drop: function(player, resource, amount, asyncReturn) {
        // <<-- Creer-Merge: drop -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        /* CHECKS:
        * -------------------------------------------------
        * Its the current player's turn
        * The current player is controlling their own units
        * The beaver isn't dead
        * That the beaver isn't distracted
        * The beaver isn't trying to drop something it doesn't have
        */

        let reason;

        if(!player || player !== this.game.currentPlayer) {
            reason = `${player} it is not your turn.`;
        }
        else if(this.owner !== player) {
            reason = `${this} is not owned by you.`;
        }
        else if(this.health <= 0) {
            reason = `${this} is dead.`;
        }
        else if(this.distracted) {
            reason = `${this} is distracted.`;
        }
        else if(this.resource === "fish" && amount > this.fish) {
            reason = "{this} does not have {amount} fish to drop.";
        }
        else if(this.resource === "branch" && amount > this.branch) {
            reason = "{this} does not have {amount} branch(es) to drop.";
        }

        if(reason) {
            return this.game.logicError(false, reason);
        }

        return true;
        // <<-- /Creer-Merge: drop -->>
    },

    /**
     * Harvests the branches or fish from a Spawner on an adjacent tile.
     *
     * @param {Player} player - the player that called this.
     * @param {Tile} tile - The tile you want to harvest.
     * @param {function} asyncReturn - if you nest orders in this function you must return that value via this function in the order's callback.
     * @returns {boolean} True if successfully harvested, false otherwise.
     */
    harvest: function(player, tile, asyncReturn) {
        // <<-- Creer-Merge: harvest -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // Developer: Put your game logic for the Beaver's harvest function here
        return false;

        // <<-- /Creer-Merge: harvest -->>
    },

    /**
     * Moves this beaver from its current tile to an adjacent tile.
     *
     * @param {Player} player - the player that called this.
     * @param {Tile} tile - The tile this beaver should move to. Costs 2 moves normally, 3 if moving upstream, and 1 if moving downstream.
     * @param {function} asyncReturn - if you nest orders in this function you must return that value via this function in the order's callback.
     * @returns {boolean} True if the move worked, false otherwise.
     */
    move: function(player, tile, asyncReturn) {
        // <<-- Creer-Merge: move -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // Developer: Put your game logic for the Beaver's move function here
        return false;

        // <<-- /Creer-Merge: move -->>
    },

    /**
     * Picks up some branches or fish on the beaver's tile.
     *
     * @param {Player} player - the player that called this.
     * @param {string} resource - The type of resource to pickup ('branch' or 'fish').
     * @param {number} amount - The amount of the resource to drop, numbers <= 0 will pickup all of that type.
     * @param {function} asyncReturn - if you nest orders in this function you must return that value via this function in the order's callback.
     * @returns {boolean} True if successfully picked up a resource, false otherwise.
     */
    pickup: function(player, resource, amount, asyncReturn) {
        // <<-- Creer-Merge: pickup -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

        // Developer: Put your game logic for the Beaver's pickup function here
        return false;

        // <<-- /Creer-Merge: pickup -->>
    },

    //<<-- Creer-Merge: added-functions -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

    // You can add additional functions here. These functions will not be directly callable by client AIs

    //<<-- /Creer-Merge: added-functions -->>

});

module.exports = Beaver;
<|endoftext|>


<commit_before>// Util functions

function equalPointArray(a1, a2) {
  // check n x 2 arrays for equality
  if (a1.length !== a2.length) {
    return false;
  }
  var i = a1.length;
  while (i--) {
    if (a1[i][0] !== a2[i][0] || a1[i][1] !== a2[i][1]) {
      return false;
    }
  }
  return true;
}

function numberAppendString(num, string) {
  if (num > 0) {
    return num + string;
  }
  return "";
}

function secondsToStringFR(seconds) {
  var numyears = Math.floor(seconds / 31536000);
  var numdays = Math.floor((seconds % 31536000) / 86400);
  var numhours = Math.floor(((seconds % 31536000) % 86400) / 3600);
  var numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60);
  var numseconds = (((seconds % 31536000) % 86400) % 3600) % 60;

  var sentence = "";
  sentence = sentence + numberAppendString(numyears, " ans ");
  sentence = sentence + numberAppendString(numdays, " jours ");
  sentence = sentence + numberAppendString(numhours, " heures ");
  sentence = sentence + numberAppendString(numminutes, " minutes ");

  return sentence;
}

function metersToKmString(meters) {
  return Math.round( (meters / 1000) * 10 ) / 10;
}

var Point = function(lat, lon, rank, kind) {
  this.lat = lat;
  this.lon = lon;
  this.rank = rank;
  this.kind = kind;
  this.arrayValue = function() {
    return [this.lat, this.lon];
  };
};

var TripDrawing = function() {
  var self = this;

  self.init = function(aRouting, aPointArray) {
    self.totalDistance = 0.0;
    self.totalTime = 0.0;
    // this is also defined in the model
    self.maxRank = 99;
    // trip is passing by those points
    self.points = [];
    // to check for changes
    self.lastPoints = [];
    self.routing = aRouting;
    if (self.hasAFirstLatLon(aPointArray)) {
      self.points = aPointArray;
      self.renderRouting();
    }
    self.observeGeonameChanges();
    self.manageCocoonEvents();
    self.observeAndGetRouteInfo();
  };

  self.observeGeonameChanges = function() {
    $(".trip_points_lon input").change(self.reorderSteps);
  };

  self.manageCocoonEvents = function() {
    $("#steps")
      .on("cocoon:after-insert", function(e, el) {
        $(el).find(".trip_points_lon input:first").change(self.reorderSteps);
        self.reorderSteps();
      })
      .on("cocoon:after-remove", function(e, el) {
        self.reorderSteps();
      })
      .on("sortupdate", function(e, el) {
        self.reorderSteps();
      });
  };

  self.updateOrCreatePoint = function() {
    var lon = parseFloat($(this).val());
    var lat = parseFloat($(this).parent().siblings(
      ".trip_points_lat:first").find("input:first").val());
    if (!isNaN(lon) && !isNaN(lat)) {
      var kind = $(this).parent().siblings(
        ".trip_points_kind:first").find("input:first").val();
      var rank = parseInt($(this).parent().siblings(
        ".trip_points_rank:first").find("input:first").val());
      var point = new Point(lat, lon, rank, kind);
      self.points[point.rank] = point.arrayValue();
    }
  };

  self.reorderSteps = function() {
    // Reset coordinates array
    self.points = [];
    // Explore start/end DOM and get coordinates
    $("#city_from").find(".trip_points_lon input:first").each(
      self.updateOrCreatePoint);
    $("#city_to").find(".trip_points_lon input:first").each(
      self.updateOrCreatePoint);
    // Explore DOM steps
    $("#steps .nested-fields").each(function(index, value) {
      var newIndex = parseInt(index) + 1;
      // Rename labels, update rank
      $(this).find(".step-nb:first").text(newIndex);
      $(this).find(".trip_points_rank input:first").val(newIndex);
      // Make coordinates list
      $(this).find(".trip_points_lon input:first").each(self.updateOrCreatePoint);
    });
    self.renderRouting();
  };

  // rendering and routing methods

  self.renderRouting = function() {
    // Check for changes before contacting osrm
    if (!self.equalArrays()) {
      // Keep copy of waypoints
      self.lastPoints = self.points.slice(0);
      self.routing.setWaypoints(self.cloneAndTrimArray());
      self.routing.route();
    }
  };

  self.observeAndGetRouteInfo = function() {
    self.routing.on("routesfound", function (e) {
      self.totalDistance = e.routes[0].summary.totalDistance;
      self.totalTime = e.routes[0].summary.totalTime;
      $("#trip_total_distance").val(self.totalDistance);
      $("#trip_total_time").val(self.totalTime);
      $("#distance_and_time").text(metersToKmString(self.totalDistance)
        + " km - " + secondsToStringFR(self.totalTime));
    });
  };

  // more or less "private" methods

  self.cloneAndTrimArray = function() {
    var copiedArray = self.points.slice(0);
    return copiedArray.filter(function(val) { return val !== null; });
  };

  self.equalArrays = function() {
    // make copies
    var a1 = self.points.slice(0);
    var a2 = self.lastPoints.slice(0);
    // trim copied sparse arrays
    a1 = a1.filter(function(val) { return val !== null; });
    a2 = a2.filter(function(val) { return val !== null; });
    // compare values
    return equalPointArray(a1, a2);
  };

  self.hasAFirstLatLon = function(anArray) {
    var result = true;
    if (anArray[0][0] === 0) { result = false; }
    if (anArray[0][1] === 0) { result = false; }
    return result;
  };

};
<commit_msg>more cyclomatic complexity reduction<commit_after>// Util functions

function equalDouble(d1, d2) {
  // cheeck equality between two (1 x 2) arrays
  return (d1[0] === d2[0] && d1[1] === d2[1]);
}

function equalPointArray(a1, a2) {
  // check n x 2 arrays for equality
  if (a1.length == a2.length) {
    var i = a1.length;
    while (i--) {
      if (!equalDouble(a1[i], a2[i])) {
        return false;
      }
    }
    return true;
  }
  return false;
}

function numberAppendString(num, string) {
  if (num > 0) {
    return num + string;
  }
  return "";
}

function secondsToStringFR(seconds) {
  var numyears = Math.floor(seconds / 31536000);
  var numdays = Math.floor((seconds % 31536000) / 86400);
  var numhours = Math.floor(((seconds % 31536000) % 86400) / 3600);
  var numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60);
  var numseconds = (((seconds % 31536000) % 86400) % 3600) % 60;

  var sentence = "";
  sentence = sentence + numberAppendString(numyears, " ans ");
  sentence = sentence + numberAppendString(numdays, " jours ");
  sentence = sentence + numberAppendString(numhours, " heures ");
  sentence = sentence + numberAppendString(numminutes, " minutes ");

  return sentence;
}

function metersToKmString(meters) {
  return Math.round( (meters / 1000) * 10 ) / 10;
}

var Point = function(lat, lon, rank, kind) {
  this.lat = lat;
  this.lon = lon;
  this.rank = rank;
  this.kind = kind;
  this.arrayValue = function() {
    return [this.lat, this.lon];
  };
};

var TripDrawing = function() {
  var self = this;

  self.init = function(aRouting, aPointArray) {
    self.totalDistance = 0.0;
    self.totalTime = 0.0;
    // this is also defined in the model
    self.maxRank = 99;
    // trip is passing by those points
    self.points = [];
    // to check for changes
    self.lastPoints = [];
    self.routing = aRouting;
    if (self.hasAFirstLatLon(aPointArray)) {
      self.points = aPointArray;
      self.renderRouting();
    }
    self.observeGeonameChanges();
    self.manageCocoonEvents();
    self.observeAndGetRouteInfo();
  };

  self.observeGeonameChanges = function() {
    $(".trip_points_lon input").change(self.reorderSteps);
  };

  self.manageCocoonEvents = function() {
    $("#steps")
      .on("cocoon:after-insert", function(e, el) {
        $(el).find(".trip_points_lon input:first").change(self.reorderSteps);
        self.reorderSteps();
      })
      .on("cocoon:after-remove", function(e, el) {
        self.reorderSteps();
      })
      .on("sortupdate", function(e, el) {
        self.reorderSteps();
      });
  };

  self.updateOrCreatePoint = function() {
    var lon = parseFloat($(this).val());
    var lat = parseFloat($(this).parent().siblings(
      ".trip_points_lat:first").find("input:first").val());
    if (!isNaN(lon) && !isNaN(lat)) {
      var kind = $(this).parent().siblings(
        ".trip_points_kind:first").find("input:first").val();
      var rank = parseInt($(this).parent().siblings(
        ".trip_points_rank:first").find("input:first").val());
      var point = new Point(lat, lon, rank, kind);
      self.points[point.rank] = point.arrayValue();
    }
  };

  self.reorderSteps = function() {
    // Reset coordinates array
    self.points = [];
    // Explore start/end DOM and get coordinates
    $("#city_from").find(".trip_points_lon input:first").each(
      self.updateOrCreatePoint);
    $("#city_to").find(".trip_points_lon input:first").each(
      self.updateOrCreatePoint);
    // Explore DOM steps
    $("#steps .nested-fields").each(function(index, value) {
      var newIndex = parseInt(index) + 1;
      // Rename labels, update rank
      $(this).find(".step-nb:first").text(newIndex);
      $(this).find(".trip_points_rank input:first").val(newIndex);
      // Make coordinates list
      $(this).find(".trip_points_lon input:first").each(self.updateOrCreatePoint);
    });
    self.renderRouting();
  };

  // rendering and routing methods

  self.renderRouting = function() {
    // Check for changes before contacting osrm
    if (!self.equalArrays()) {
      // Keep copy of waypoints
      self.lastPoints = self.points.slice(0);
      self.routing.setWaypoints(self.cloneAndTrimArray());
      self.routing.route();
    }
  };

  self.observeAndGetRouteInfo = function() {
    self.routing.on("routesfound", function (e) {
      self.totalDistance = e.routes[0].summary.totalDistance;
      self.totalTime = e.routes[0].summary.totalTime;
      $("#trip_total_distance").val(self.totalDistance);
      $("#trip_total_time").val(self.totalTime);
      $("#distance_and_time").text(metersToKmString(self.totalDistance)
        + " km - " + secondsToStringFR(self.totalTime));
    });
  };

  // more or less "private" methods

  self.cloneAndTrimArray = function() {
    var copiedArray = self.points.slice(0);
    return copiedArray.filter(function(val) { return val !== null; });
  };

  self.equalArrays = function() {
    // make copies
    var a1 = self.points.slice(0);
    var a2 = self.lastPoints.slice(0);
    // trim copied sparse arrays
    a1 = a1.filter(function(val) { return val !== null; });
    a2 = a2.filter(function(val) { return val !== null; });
    // compare values
    return equalPointArray(a1, a2);
  };

  self.hasAFirstLatLon = function(anArray) {
    var result = true;
    if (anArray[0][0] === 0) { result = false; }
    if (anArray[0][1] === 0) { result = false; }
    return result;
  };

};
<|endoftext|>