<commit_before>/**
 * A class representing a route planner, containing a map and tools to add and remove waypoints to a route
 * @class
 * @param {String} map A css selector identifying the element to contain the map
 * @param {String} controls A css selector identifying the element to contain waypoint controls
 * @param {String} info A css selector identifying the element to display route information
 * @param {String} load A css selector identifying an element of the map to load information from. An empty map will be loaded if this element does not exist
 * @param {Number[]} [mapsize] the size of the map in pixels, given as [width, height]
 */
Planner = function(map, controls, info, load, mapsize)
{
	// Mapsize is optional
	mapsize = mapsize || [640, 480];

	/** JQuery selectors of the elements used by the planner */
	this.elements = {'map': $(map), 'controls': $(controls), 'info': $(info), 'load': $(load)};
	/** The google map object */
	this.map = null;
	/** An array of google map markers */
	this.markers = [];
	/** A google maps directions service instance */
	this.directions = new google.maps.DirectionsService();
	/** A google maps directionsrenderer instance */
	this.route_renderer = null;
	/** a google maps route information instance */
	this.route = null;
	/** Whether the route should start and end at the same point */
	this.round_trip = false;
	/** jQuery selector of a container where the route json is placed for upload */
	this.json_container = null;

	// Setup map size
	this.elements.map.css({'width': mapsize[0] + 'px',
						   'height': mapsize[1] + 'px'});

	this.elements.info.css({'height': mapsize[1] + 'px'});

	this.initialize_map(this.elements.map[0]);
	this.initialize_controls();
	this.update_info();
};

/**
 * Initializes the google map. First tests whether geolocation is available,
 * and if so, centers the map on the user.
 *
Called automatically by constructor.
 * @param {Node} target The html element to contain the map
 */
Planner.prototype.initialize_map = function(target)
{
	// Allow 'this' reference through callbacks
	var planner = this;
	var options = {'center': new google.maps.LatLng(-34.397, 150.644),
				   'zoom': 12};

	// Callback function
	var load_map = function()
	{
		console.log(target);
		planner.map = new google.maps.Map(target, options);
		planner.map_events();
		if (planner.elements.load.length)
			planner.load(planner.elements.load.html());
	};

	// Check if geolocation is supported
	if (navigator.geolocation)
	{
		navigator.geolocation.getCurrentPosition(function(pos)
		{
			// Successful geolocation
			options.center = new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude);
			load_map();
		}, function()
		{
			// Failed geolocation. Permission denied?
			load_map();
		});
	}
	else
		// Geolocation was not supported
		load_map();
};

/**
 * Sets up events for map and markers.
 *
Called automatically by initialize_map.
 */
Planner.prototype.map_events = function()
{
	// Allow 'this' reference through callbacks
	var planner = this;

	// Event to add a waypoint
	google.maps.event.addListener(this.map, 'click', function(ev)
	{
		if (planner.markers.length >= 9)
			return;

		// TODO: Create marker function
		var marker_options = {'position': ev.latLng,
							  'draggable': true};

		var marker = new google.maps.Marker(marker_options);
		planner.markers.push(marker);
		planner.update_route(planner);

		// Event to remove said waypoint
		google.maps.event.addListener(marker, 'rightclick', function(ev)
		{
			marker.setMap();

			var index = planner.markers.indexOf(marker);
			if (index >= 0)
				planner.markers.splice(index, 1);

			planner.update_route(planner);
		});

		// Event on drag
		google.maps.event.addListener(marker, 'dragend', function(ev)
		{
			planner.update_route(planner);
		});
	});
};

/**
 * Updates the route display of the route planner.
 *
 Called automatically by several events.
 */
Planner.prototype.update_route = function()
{
	// Set map icons
	for (var i = 0; i < this.markers.length; i++)
	{
		this.markers[i].setIcon(STATIC_URL + 'img/number_' + (i + 1) + '.png');
		this.markers[i].setMap(this.map);
	}

	// Allow 'this' reference through callbacks
	var planner = this;

	if (this.markers.length >= 2)
	{
		var plan = []
		// Extract positions. This has the nice side effect of copying the array, so markers are unaffected later on.
		for (var i = 0; i < this.markers.length; i++)
		{
			plan.push(this.markers[i].getPosition());
		}

		// If we are round-tripping, end at the last waypoint
		if (this.round_trip)
			plan.push(plan[0]);

		// Extract start and end point
		var destination = plan.reverse().shift();
		var origin = plan.reverse().shift();

		// Plan now contains intermediate waypoints. Wrap these so google maps understands them
		for (var i = 0; i < plan.length; i++)
		{
			// Using stopover true, because it changes route algorithm to allow turning around on the spot
			plan[i] = {'location': plan[i], 'stopover': true};
		}

		var route_options = {'origin': origin,
							 'destination': destination,
							 'waypoints': plan,

							 'travelMode': google.maps.TravelMode.WALKING,
							 'avoidHighways': true,
							 'avoidTolls': true,
							 'optimizeWaypoints': false};

		this.directions.route(route_options, function(result, status)
		{
			if (status == google.maps.DirectionsStatus.OK)
			{
				planner.route = result.routes[0];

				// Update existing route if possible, otherwise create a new route
				if (planner.route_renderer)
				{
					planner.route_renderer.setDirections(result);
				}
				else
				{
					var direction_options = {'directions': result,
											 'draggable': false,   // We use custom draggable markers instead
											 'map': planner.map,
											 'suppressMarkers': true,
											 'preserveViewport': true}

					planner.route_renderer = new google.maps.DirectionsRenderer(direction_options);
				}

				planner.update_info();
			}
			else
			{
				// Creating the route somehow failed
				// TODO: Handle nicely
				alert('Directions request failed: ' + status);
			}
		});
	}
	else
	{
		// Remove previous route
		if (this.route_renderer)
		{
			this.route_renderer.setMap();
			this.route_renderer = null;
		}
		this.update_info();
	}
};

/**
 * Initializes the planner controls
 *
Called automatically by constructor
 */
Planner.prototype.initialize_controls = function()
{
	// Allow 'this' reference through callbacks
	var planner = this;

	$('<button>Clear</button>').appendTo(this.elements.controls).click(function(ev)
	{
		planner.clear();
	});

	$('<button>Reverse</button>').appendTo(this.elements.controls).click(function(ev)
	{
		planner.markers.reverse();
		planner.update_route();
	});

	$('Round-trip?<input type="checkbox"/>').appendTo(this.elements.controls).find('input').change(function(ev)
	{
		planner.round_trip = $(this).is(':checked');
		planner.update_route();
	});

	this.json_container = $('<input type="hidden" name="json" />');
	$('<form action="" method="POST"><input type="text" name="name" value="Route name" /><input type="submit" /></form>').append(this.json_container).append(CSRF).appendTo(this.elements.controls);
};

/**
 * Clears route and markers from the map
 */
Planner.prototype.clear = function()
{
	// Clear all markers, avoiding leaks
	for (var i = 0; i < this.markers.length; i++)
	{
		this.markers[i].setMap();
	}
	this.markers = [];

	// Clear route
	if (this.route_renderer)
		this.route_renderer.setMap();

	this.route_renderer = null;

	this.update_info();
};

/**
 * Updates the route information view
 */
Planner.prototype.update_info = function()
{
	this.elements.info.html('');

	if (!this.route_renderer)
	{
		$('').text("No route entered").appendTo(this.elements.info);
		this.json_container.val('{"waypoints":[]}');
	}
	else
	{
		var distance = 0;
		for (var i = 0; i < this.route.legs.length; i++)
		{
			distance += this.route.legs[i].distance.value;
		}
		$('').text("Route distance: " + distance + 'm').appendTo(this.elements.info);

		var waypoints = [];
		for (var i = 0; i < this.markers.length; i++)
		{
			waypoints.push({'lat': this.markers[i].getPosition().lat(), 'lng': this.markers[i].getPosition().lng()});
		}

		this.json_container.val(JSON.stringify({'waypoints': waypoints,
												'distance': distance})); // TODO: Enter score here
	}
}

/**
 * Loads a route from a json representation
 * @param {JSON} json the json representation to load
 */
Planner.prototype.load = function(json)
{
	var data = JSON.parse(json);
	console.log(data);
	this.elements.controls.find('input[name="name"]').val(data['name']);

	var planner = this;
	for (var i = 0; i < data.waypoints.length; i++)
	{
		// TODO: Create marker function
		var marker_options = {'position': new google.maps.LatLng(data.waypoints[i].lat, data.waypoints[i].lng),
							  'draggable': true};

		var marker = new google.maps.Marker(marker_options);
		planner.markers.push(marker);
		planner.update_route(planner);

		// Event to remove said waypoint
		google.maps.event.addListener(marker, 'rightclick', function(ev)
		{
			marker.setMap();

			var index = planner.markers.indexOf(marker);
			if (index >= 0)
				planner.markers.splice(index, 1);

			planner.update_route(planner);
		});

		// Event on drag
		google.maps.event.addListener(marker, 'dragend', function(ev)
		{
			planner.update_route(planner);
		});
	}

	console.log(this.markers);
	this.update_route();
}
<commit_msg>Created a unified way of adding markers to the planner<commit_after>/**
 * A class representing a route planner, containing a map and tools to add and remove waypoints to a route
 * @class
 * @param {String} map A css selector identifying the element to contain the map
 * @param {String} controls A css selector identifying the element to contain waypoint controls
 * @param {String} info A css selector identifying the element to display route information
 * @param {String} load A css selector identifying an element of the map to load information from. An empty map will be loaded if this element does not exist
 * @param {Number[]} [mapsize] the size of the map in pixels, given as [width, height]
 */
Planner = function(map, controls, info, load, mapsize)
{
	// Mapsize is optional
	mapsize = mapsize || [640, 480];

	/** JQuery selectors of the elements used by the planner */
	this.elements = {'map': $(map), 'controls': $(controls), 'info': $(info), 'load': $(load)};
	/** The google map object */
	this.map = null;
	/** An array of google map markers */
	this.markers = [];
	/** A google maps directions service instance */
	this.directions = new google.maps.DirectionsService();
	/** A google maps directionsrenderer instance */
	this.route_renderer = null;
	/** a google maps route information instance */
	this.route = null;
	/** Whether the route should start and end at the same point */
	this.round_trip = false;
	/** jQuery selector of a container where the route json is placed for upload */
	this.json_container = null;

	// Setup map size
	this.elements.map.css({'width': mapsize[0] + 'px',
						   'height': mapsize[1] + 'px'});

	this.elements.info.css({'height': mapsize[1] + 'px'});

	this.initialize_map(this.elements.map[0]);
	this.initialize_controls();
	this.update_info();
};

/**
 * Initializes the google map. First tests whether geolocation is available,
 * and if so, centers the map on the user.
 *
Called automatically by constructor.
 * @param {Node} target The html element to contain the map
 */
Planner.prototype.initialize_map = function(target)
{
	// Allow 'this' reference through callbacks
	var planner = this;
	var options = {'center': new google.maps.LatLng(-34.397, 150.644),
				   'zoom': 12};

	// Callback function
	var load_map = function()
	{
		console.log(target);
		planner.map = new google.maps.Map(target, options);
		planner.map_events();
		if (planner.elements.load.length)
			planner.load(planner.elements.load.html());
	};

	// Check if geolocation is supported
	if (navigator.geolocation)
	{
		navigator.geolocation.getCurrentPosition(function(pos)
		{
			// Successful geolocation
			options.center = new google.maps.LatLng(pos.coords.latitude, pos.coords.longitude);
			load_map();
		}, function()
		{
			// Failed geolocation. Permission denied?
			load_map();
		});
	}
	else
		// Geolocation was not supported
		load_map();
};

/**
 * Sets up events for map and markers.
 *
Called automatically by initialize_map.
 */
Planner.prototype.map_events = function()
{
	// Allow 'this' reference through callbacks
	var planner = this;

	// Event to add a waypoint
	google.maps.event.addListener(this.map, 'click', function(ev)
	{
		if (planner.markers.length >= 9)
			return;

		planner.add_marker(ev.latLng)
	});
};

/**
 * Updates the route display of the route planner.
 *
 Called automatically by several events.
 */
Planner.prototype.update_route = function()
{
	// Set map icons
	for (var i = 0; i < this.markers.length; i++)
	{
		this.markers[i].setIcon(STATIC_URL + 'img/number_' + (i + 1) + '.png');
		this.markers[i].setMap(this.map);
	}

	// Allow 'this' reference through callbacks
	var planner = this;

	if (this.markers.length >= 2)
	{
		var plan = []
		// Extract positions. This has the nice side effect of copying the array, so markers are unaffected later on.
		for (var i = 0; i < this.markers.length; i++)
		{
			plan.push(this.markers[i].getPosition());
		}

		// If we are round-tripping, end at the last waypoint
		if (this.round_trip)
			plan.push(plan[0]);

		// Extract start and end point
		var destination = plan.reverse().shift();
		var origin = plan.reverse().shift();

		// Plan now contains intermediate waypoints. Wrap these so google maps understands them
		for (var i = 0; i < plan.length; i++)
		{
			// Using stopover true, because it changes route algorithm to allow turning around on the spot
			plan[i] = {'location': plan[i], 'stopover': true};
		}

		var route_options = {'origin': origin,
							 'destination': destination,
							 'waypoints': plan,

							 'travelMode': google.maps.TravelMode.WALKING,
							 'avoidHighways': true,
							 'avoidTolls': true,
							 'optimizeWaypoints': false};

		this.directions.route(route_options, function(result, status)
		{
			if (status == google.maps.DirectionsStatus.OK)
			{
				planner.route = result.routes[0];

				// Update existing route if possible, otherwise create a new route
				if (planner.route_renderer)
				{
					planner.route_renderer.setDirections(result);
				}
				else
				{
					var direction_options = {'directions': result,
											 'draggable': false,   // We use custom draggable markers instead
											 'map': planner.map,
											 'suppressMarkers': true,
											 'preserveViewport': true}

					planner.route_renderer = new google.maps.DirectionsRenderer(direction_options);
				}

				planner.update_info();
			}
			else
			{
				// Creating the route somehow failed
				// TODO: Handle nicely
				alert('Directions request failed: ' + status);
			}
		});
	}
	else
	{
		// Remove previous route
		if (this.route_renderer)
		{
			this.route_renderer.setMap();
			this.route_renderer = null;
		}
		this.update_info();
	}
};

/**
 * Initializes the planner controls
 *
Called automatically by constructor
 */
Planner.prototype.initialize_controls = function()
{
	// Allow 'this' reference through callbacks
	var planner = this;

	$('<button>Clear</button>').appendTo(this.elements.controls).click(function(ev)
	{
		planner.clear();
	});

	$('<button>Reverse</button>').appendTo(this.elements.controls).click(function(ev)
	{
		planner.markers.reverse();
		planner.update_route();
	});

	$('Round-trip?<input type="checkbox"/>').appendTo(this.elements.controls).find('input').change(function(ev)
	{
		planner.round_trip = $(this).is(':checked');
		planner.update_route();
	});

	this.json_container = $('<input type="hidden" name="json" />');
	$('<form action="" method="POST"><input type="text" name="name" value="Route name" /><input type="submit" /></form>').append(this.json_container).append(CSRF).appendTo(this.elements.controls);
};

/**
 * Clears route and markers from the map
 */
Planner.prototype.clear = function()
{
	// Clear all markers, avoiding leaks
	for (var i = 0; i < this.markers.length; i++)
	{
		this.markers[i].setMap();
	}
	this.markers = [];

	// Clear route
	if (this.route_renderer)
		this.route_renderer.setMap();

	this.route_renderer = null;

	this.update_info();
};

/**
 * Updates the route information view
 */
Planner.prototype.update_info = function()
{
	this.elements.info.html('');

	if (!this.route_renderer)
	{
		$('').text("No route entered").appendTo(this.elements.info);
		this.json_container.val('{"waypoints":[]}');
	}
	else
	{
		var distance = 0;
		for (var i = 0; i < this.route.legs.length; i++)
		{
			distance += this.route.legs[i].distance.value;
		}
		$('').text("Route distance: " + distance + 'm').appendTo(this.elements.info);

		var waypoints = [];
		for (var i = 0; i < this.markers.length; i++)
		{
			waypoints.push({'lat': this.markers[i].getPosition().lat(), 'lng': this.markers[i].getPosition().lng()});
		}

		this.json_container.val(JSON.stringify({'waypoints': waypoints,
												'distance': distance})); // TODO: Enter score here
	}
}

/**
 * Loads a route from a json representation
 * @param {JSON} json the json representation to load
 */
Planner.prototype.load = function(json)
{
	// TODO: Validate json
	var data = JSON.parse(json);
	console.log(data);
	this.elements.controls.find('input[name="name"]').val(data['name']);

	for (var i = 0; i < data.waypoints.length; i++)
		this.add_marker(new google.maps.LatLng(data.waypoints[i].lat, data.waypoints[i].lng))

	console.log(this.markers);
	this.update_route();
}

/*
 * Adds a marker to the map, adding relevant events and updates the directions
 * @param {google.maps.LatLng} position The LatLng object representing the position to add the marker
 */
Planner.prototype.add_marker = function(position)
{
	// Allow 'this' reference through callbacks
	var planner = this;

	var marker_options = {'position': position,
						  'draggable': true};

	var marker = new google.maps.Marker(marker_options);
	this.markers.push(marker);
	this.update_route(planner);

	// Event to remove said waypoint
	google.maps.event.addListener(marker, 'rightclick', function(ev)
	{
		marker.setMap();

		var index = planner.markers.indexOf(marker);
		if (index >= 0)
			planner.markers.splice(index, 1);

		planner.update_route(planner);
	});

	// Event on drag
	google.maps.event.addListener(marker, 'dragend', function(ev)
	{
		planner.update_route(planner);
	});
};
<|endoftext|>


<commit_before>/*global PIXI */
'use strict';

var renderer = new PIXI.WebGLRenderer(800, 600, {resolution: 1, antialias: true });
document.body.appendChild(renderer.view);

var stage = new PIXI.Container();

var camera = new PIXI.Camera3d();
camera.easyPerspective(renderer, 350, 30, 10000);
camera.lookEuler.x = -Math.PI / 5.5;
stage.addChild(camera);

var cards = new PIXI.Container3d();
cards.position.y = -50;
// MAKE CARDS LARGER:
cards.scale.set(1.5,1.5,1.5);
camera.addChild(cards);

// create a new loader
var loader = new PIXI.loaders.Loader();
loader.add('cards', '_assets2/casino/cards.json');
loader.add('table', '_assets2/casino/table.jpg');
loader.load(onAssetsLoaded);

//its for Z-index
camera.enableDisplayList = true;
camera.onZOrder = function(innerOrShadow) {
    innerOrShadow.zOrder = innerOrShadow.position.z;
};

var blurFilter = new PIXI.filters.BlurFilter();
blurFilter.blur = 10;

function CardSprite() {
    PIXI.Container3d.call(this);
    let tex = loader.resources['cards'].textures;

    //shadow will be under card
    this.shadow = new PIXI.Sprite3d(tex["black.png"]);
    this.shadow.anchor.set(0.5);
    this.shadow.scale.set(1.05);
    //TRY IT WITH FILTER:
    // this.shadow.filters = [blurFilter];
    //all shadows are UNDER all cards
    this.shadow.zIndex = 1;
    this.inner = new PIXI.Container3d();
    //cards are above the shadows
    //either they have back, either face
    this.inner.zIndex = 2;

    this.addChild(this.shadow);
    this.addChild(this.inner);

    this.back = new PIXI.Sprite3d(tex["cover.png"]);
    this.back.anchor.set(0.5);
    this.face = new PIXI.Container3d();
    this.code = 0;
    this.showCode = -1;
    this.inner.euler.y = Math.PI;
    this.scale.set(0.2, 0.2, 0.2);
}

CardSprite.prototype = Object.create(PIXI.Container3d.prototype);

CardSprite.prototype.createFace = function() {
    var face = this.face;
    face.removeChildren();
    var tex = loader.resources['cards'].textures;
    var code = this.showCode;
    var num = code & 0xf;
    var suit = code >> 4;
    var sprite = new PIXI.Sprite3d(tex['white.png']);
    var sprite2 = new PIXI.Sprite3d(num > 0 ? tex[suit % 2 + "_" + num + ".png"]: PIXI.Texture.EMPTY);
    var sprite3 = new PIXI.Sprite3d(suit !== 0 ? tex[suit + "_big.png"] : PIXI.Texture.EMPTY);
    var sprite4 = new PIXI.Sprite3d(suit !== 0 ? tex[suit + "_small.png"] : PIXI.Texture.EMPTY);
    sprite2.y = -120;
    sprite2.x = -80;
    sprite3.y = 70;
    sprite3.x = 40;
    sprite4.y = -70;
    sprite4.x = -100;

    sprite.anchor.set(0.5);
    sprite2.anchor.set(0.5);
    sprite3.anchor.set(0.5);
    face.addChild(sprite);
    face.addChild(sprite2);
    face.addChild(sprite3);
    face.addChild(sprite4);
};

CardSprite.prototype.update = function(dt) {
    var inner = this.inner;
    if (this.code > 0 && inner.euler.y > 0) {
        inner.euler.y = Math.max(0, inner.euler.y - dt * 5);
    }
    if (this.code == 0 && inner.euler.y < Math.PI) {
        inner.euler.y = Math.min(Math.PI, inner.euler.y + dt * 5);
    }
    inner.position.z = -Math.sin(inner.euler.y) * this.back.width;

    //assignment is overriden, so its actually calling euler.copy(this.euler)
    this.shadow.euler = inner.euler;

    this.checkFace();
};

CardSprite.prototype.checkFace = function() {
    var inner = this.inner;
    var cc;

    //very naive implementation, i dont even calculate normals :)
    if (inner.euler.y > Math.PI / 2) {
        //user sees the back
        cc = 0;
    } else {
        //user sees the face
        cc = this.showCode || this.code;
    }
    inner.removeChildren();
    if (cc==0) {
        inner.addChild(this.back);
    } else {
        inner.addChild(this.face);
    };

    if (cc !== this.showCode) {
        this.showCode = cc;
        if (cc == 0) {
            if (this.face.children.length > 0) {
                this.face.removeChildren();
            }
        } else {
            this.createFace();
        }
    }
};

function dealHand() {
    cards.removeChildren();
    for (var i=0;i<5;i++) {
        var card = new CardSprite();
        card.position.x = 56 * (i-2);
        if ((Math.random()*3 | 0) == 0) {
            onClick({target: card});
        }
        card.update(0);
        card.interactive = true;
        card.on('mouseup', onClick);
        card.on('touchend', onClick);
        cards.addChild(card);
    }
}

function onClick(event) {
    var target = event.target;
    if (target.code == 0) {
        var num = (Math.random()*13 | 0) + 2;
        var suit = (Math.random()*4 | 0) + 1;
        target.code = suit * 16 + num;
    } else {
        target.code = 0;
    }
}

function onAssetsLoaded() {
    //background must be UNDER camera, it doesnt have z-index or any other bullshit for camera
    stage.addChildAt(new PIXI.Sprite(loader.resources.table.texture), 0);
    dealHand();
    // start animating
    ticker.start();
}

var ticker = PIXI.ticker.shared;
// start it after assets are loaded
ticker.autoStart = false;

ticker.add(function(deltaTime) {
    for (var i=0;i<cards.children.length;i++) {
        cards.children[i].update(deltaTime / 60.0);
    }

    renderer.render(stage);
});
<commit_msg>shadow is not bigger<commit_after>/*global PIXI */
'use strict';

var renderer = new PIXI.WebGLRenderer(800, 600, {resolution: 1, antialias: true });
document.body.appendChild(renderer.view);

var stage = new PIXI.Container();

var camera = new PIXI.Camera3d();
camera.easyPerspective(renderer, 350, 30, 10000);
camera.lookEuler.x = -Math.PI / 5.5;
stage.addChild(camera);

var cards = new PIXI.Container3d();
cards.position.y = -50;
// MAKE CARDS LARGER:
cards.scale.set(1.5,1.5,1.5);
camera.addChild(cards);

// create a new loader
var loader = new PIXI.loaders.Loader();
loader.add('cards', '_assets2/casino/cards.json');
loader.add('table', '_assets2/casino/table.jpg');
loader.load(onAssetsLoaded);

//its for Z-index
camera.enableDisplayList = true;
camera.onZOrder = function(innerOrShadow) {
    innerOrShadow.zOrder = innerOrShadow.position.z;
};

var blurFilter = new PIXI.filters.BlurFilter();
blurFilter.blur = 10;

function CardSprite() {
    PIXI.Container3d.call(this);
    let tex = loader.resources['cards'].textures;

    //shadow will be under card
    this.shadow = new PIXI.Sprite3d(tex["black.png"]);
    this.shadow.anchor.set(0.5);
    this.shadow.scale.set(1.0);
    //TRY IT WITH FILTER:
    // this.shadow.filters = [blurFilter];
    //all shadows are UNDER all cards
    this.shadow.zIndex = 1;
    this.inner = new PIXI.Container3d();
    //cards are above the shadows
    //either they have back, either face
    this.inner.zIndex = 2;

    this.addChild(this.shadow);
    this.addChild(this.inner);

    this.back = new PIXI.Sprite3d(tex["cover.png"]);
    this.back.anchor.set(0.5);
    this.face = new PIXI.Container3d();
    this.code = 0;
    this.showCode = -1;
    this.inner.euler.y = Math.PI;
    this.scale.set(0.2, 0.2, 0.2);
}

CardSprite.prototype = Object.create(PIXI.Container3d.prototype);

CardSprite.prototype.createFace = function() {
    var face = this.face;
    face.removeChildren();
    var tex = loader.resources['cards'].textures;
    var code = this.showCode;
    var num = code & 0xf;
    var suit = code >> 4;
    var sprite = new PIXI.Sprite3d(tex['white.png']);
    var sprite2 = new PIXI.Sprite3d(num > 0 ? tex[suit % 2 + "_" + num + ".png"]: PIXI.Texture.EMPTY);
    var sprite3 = new PIXI.Sprite3d(suit !== 0 ? tex[suit + "_big.png"] : PIXI.Texture.EMPTY);
    var sprite4 = new PIXI.Sprite3d(suit !== 0 ? tex[suit + "_small.png"] : PIXI.Texture.EMPTY);
    sprite2.y = -120;
    sprite2.x = -80;
    sprite3.y = 70;
    sprite3.x = 40;
    sprite4.y = -70;
    sprite4.x = -100;

    sprite.anchor.set(0.5);
    sprite2.anchor.set(0.5);
    sprite3.anchor.set(0.5);
    face.addChild(sprite);
    face.addChild(sprite2);
    face.addChild(sprite3);
    face.addChild(sprite4);
};

CardSprite.prototype.update = function(dt) {
    var inner = this.inner;
    if (this.code > 0 && inner.euler.y > 0) {
        inner.euler.y = Math.max(0, inner.euler.y - dt * 5);
    }
    if (this.code == 0 && inner.euler.y < Math.PI) {
        inner.euler.y = Math.min(Math.PI, inner.euler.y + dt * 5);
    }
    inner.position.z = -Math.sin(inner.euler.y) * this.back.width;

    //assignment is overriden, so its actually calling euler.copy(this.euler)
    this.shadow.euler = inner.euler;

    this.checkFace();
};

CardSprite.prototype.checkFace = function() {
    var inner = this.inner;
    var cc;

    //very naive implementation, i dont even calculate normals :)
    if (inner.euler.y > Math.PI / 2) {
        //user sees the back
        cc = 0;
    } else {
        //user sees the face
        cc = this.showCode || this.code;
    }
    inner.removeChildren();
    if (cc==0) {
        inner.addChild(this.back);
    } else {
        inner.addChild(this.face);
    };

    if (cc !== this.showCode) {
        this.showCode = cc;
        if (cc == 0) {
            if (this.face.children.length > 0) {
                this.face.removeChildren();
            }
        } else {
            this.createFace();
        }
    }
};

function dealHand() {
    cards.removeChildren();
    for (var i=0;i<5;i++) {
        var card = new CardSprite();
        card.position.x = 56 * (i-2);
        if ((Math.random()*3 | 0) == 0) {
            onClick({target: card});
        }
        card.update(0);
        card.interactive = true;
        card.on('mouseup', onClick);
        card.on('touchend', onClick);
        cards.addChild(card);
    }
}

function onClick(event) {
    var target = event.target;
    if (target.code == 0) {
        var num = (Math.random()*13 | 0) + 2;
        var suit = (Math.random()*4 | 0) + 1;
        target.code = suit * 16 + num;
    } else {
        target.code = 0;
    }
}

function onAssetsLoaded() {
    //background must be UNDER camera, it doesnt have z-index or any other bullshit for camera
    stage.addChildAt(new PIXI.Sprite(loader.resources.table.texture), 0);
    dealHand();
    // start animating
    ticker.start();
}

var ticker = PIXI.ticker.shared;
// start it after assets are loaded
ticker.autoStart = false;

ticker.add(function(deltaTime) {
    for (var i=0;i<cards.children.length;i++) {
        cards.children[i].update(deltaTime / 60.0);
    }

    renderer.render(stage);
});
<|endoftext|>


<commit_before>export let serviceName = 'notifications';
export default /*@ngInject*/ function notifications(
  $cordovaBadge,
  $cordovaLocalNotification,
  $rootScope,
  $window,
  $log,
  $timeout,
  $q,
  devices,
  cryptography,
  identity,
  navigation,
  state,
  R
) {
  let contacts;
  let activeWebNotifications = {};
  // cordovaLocalNotification uses number IDs
  // notificationId is just channelId, and has the form toc-{32hex-chars}
  let getCordovaNotificationId = (notificationId) => {
    let lastEightHex = notificationId.substr(notificationId.length - 8);
    return parseInt(lastEightHex, 16);
  };

  let getNotificationMessage = function getNotificationMessage(notificationId) {
    let channelId = notificationId;
    let channelCursor = state.cloud.channels.select([channelId]);

    let notificationMessage;
    if (channelCursor.get(['inviteStatus']) === 'received') {
      return 'New invite received!';
    }

    let messageIdCursor = channelCursor.select(['latestMessageId']);
    let messageId = messageIdCursor.get();
    if (!messageId) {
      return '';
    }

    return state.cloud.messages.get([
      channelId, messageId, 'messageInfo', 'content'
    ]);
  };

  let notifyCordova = function notifyCordova(notificationInfo) {
    let channelId = notificationInfo.id;
    let channelCursor = state.cloud.channels.select([channelId]);
    let contactId = channelCursor.get(['channelInfo', 'contactIds'])[0];
    let contactCursor = state.cloud.contacts.select(contactId);
    let contactInfo = contactCursor.get('userInfo');

    let icon = identity.getAvatar(contactInfo);
    let title = contactInfo.displayName || 'Anonymous';
    let text = getNotificationMessage(notificationInfo.id);

    let cordovaNotificationInfo = {
      id: notificationInfo.cordovaNotificationId,
      title,
      text,
      icon,
      sound: 'res://platform_default',
      smallIcon: 'res://icon.png',
      data: notificationInfo
    };

    return $cordovaLocalNotification.schedule(cordovaNotificationInfo);
  };

  let notifyWeb = function notifyWeb(notificationInfo) {
    if (!$window.Notification) {
      return $q.when();
    }
    let channelId = notificationInfo.id;
    let channelCursor = state.cloud.channels.select([channelId]);
    let contactId = channelCursor.get(['channelInfo', 'contactIds'])[0];
    let contactCursor = state.cloud.contacts.select(contactId);
    let contactInfo = contactCursor.get('userInfo');

    let webNotificationOptions = {
      body: getNotificationMessage(notificationInfo.id),
      icon: identity.getAvatar(contactInfo),
      tag: notificationInfo.id
    };

    let notificationTitle = contactInfo.displayName || 'Anonymous';

    activeWebNotifications[notificationInfo.id] =
      new Notification(notificationTitle, webNotificationOptions);

    let notificationInstance = activeWebNotifications[notificationInfo.id];
    notificationInstance.addEventListener('click',
      () => handleNotificationClick(notificationInfo.id));

    $timeout(() => {
      notificationInstance.close();
    }, 5000, false);

    return $q.when();
  };

  let notify = function notify(notificationId) {
    let cordovaNotificationId = getCordovaNotificationId(notificationId);

    let notifyNative = (notificationInfo) => {
      if (devices.isInForeground()) {
        return $q.when();
      }
      // apparently, web notificaions api works in cordova for android.
      // TODO: check with iOS
      // return devices.isCordovaApp() ?
      //   notifyCordova(notificationInfo) :
      //   notifyWeb(notificationInfo);
      return notifyWeb(notificationInfo);
    };

    let notificationCursor = state.cloud.notifications.select([notificationId]);

    let notificationInfo = {
      id: notificationId,
      cordovaNotificationId
    };

    return notifyNative(notificationInfo)
      .then(() => state.save(
        notificationCursor, ['notificationInfo'], notificationInfo
      ))
      .then(() => state.save(notificationCursor, ['dismissed'], false));
  };

  let notifySystem = function notifySystem(message) {
    let systemMessageCursor = state.memory.notifications.select(['system']);

    return cryptography.getRandomBase64(2)
      .then((systemMessageId) => state.save(
        systemMessageCursor,
        ['notificationInfo'],
        { id: systemMessageId, message }
      ));
  };

  let notifyGenericError = function notifyGenericError(error) {
    $log.error(error);

    return notifySystem(
      'Something went wrong. ' +
      'Please contact the developer for further troubleshooting.'
    );
  };

  let dismissCordova = function dismissCordova(notificationInfo) {
    return $cordovaLocalNotification
      .clear(notificationInfo.cordovaNotificationId)
      // needed to avoid notifications poping up on next device startup
      .then(() => $cordovaLocalNotification
        .cancel(notificationInfo.cordovaNotificationId));
  };

  let dismissWeb = function dismissWeb(notificationInfo) {
    if (!$window.Notification) {
      return $q.when();
    }
    if (activeWebNotifications[notificationInfo.id] === undefined) {
      return $q.when();
    }

    activeWebNotifications[notificationInfo.id].close();
    return $q.when();
  };

  let dismiss = function dismiss(notificationId) {
    let cordovaNotificationId = getCordovaNotificationId(notificationId);

    if (isDismissed(notificationId)) {
      return $q.when();
    }

    let dismissNative = (notificationInfo) => {
      // return devices.isCordovaApp() ?
      //   dismissCordova(notificationInfo) :
      //   dismissWeb(notificationInfo);
      return dismissWeb(notificationInfo);
    };

    let notificationCursor = state.cloud.notifications.select([notificationId]);

    let notificationInfo = {
      id: notificationId,
      cordovaNotificationId
    };

    return dismissNative(notificationInfo)
      .then(() => state.save(notificationCursor, ['dismissed'], true));
  };

  let isDismissed = function isDismissed(notificationId) {
    return state.cloud.notifications.get([notificationId, 'dismissed']);
  };

  let handleNotificationClick = function handleNotificationClick(channelId) {
    let channelCursor = state.cloud.channels.select([channelId]);

    if (channelCursor.get(['inviteStatus'])  === 'received') {
      return contacts.showAcceptInviteDialog(channelId);
    }

    return dismiss(channelId)
      .then(() => navigation.navigate(channelId))
      .then(() => state.save(channelCursor, ['viewingLatest'], true));
  };

  $rootScope.$on('$cordovaLocalNotification:click', (event, notification) => {
    let viewId = JSON.parse(notification.data).id;

    if (viewId === 'home') {
      return navigation.navigate(viewId);
    }

    let channelId = viewId;
    return handleNotificationClick(channelId);
  });

  let initialize = function initialize(contactsService) {
    contacts = contactsService;

    if ($window.Notification) {
      $window.Notification.requestPermission();
    }

    if (devices.isCordovaApp() && $window.cordova.plugins.backgroundMode) {
      // enables background operation
      // de.appplant.cordova.plugin.background-mode required
      // $window.cordova.plugins.backgroundMode.setDefaults({ silent: true });
      $window.cordova.plugins.backgroundMode.setDefaults({
        title: 'Toc Messenger',
        ticker: 'Toc Messenger is running in background',
        text: 'Online'
      });

      $window.cordova.plugins.backgroundMode.enable();
    }

    // excluding android because it shows up as a notification instead of badge
    if (devices.isCordovaApp() && !devices.isAndroidApp() &&
      $window.cordova.plugins.notification) {
      let notificationsCursor = state.cloud.notifications;
      let updateNotificationBadge = () => {
        let notificationCount = R.pipe(
          R.values,
          R.reject(R.prop('dismissed'))
        )(notificationsCursor.get()).length;

        $cordovaBadge.get()
          .then((badgeCount) => {
            if (badgeCount === notificationCount) {
              return;
            }

            return $cordovaBadge.set(notificationCount);
          })
          .catch($log.error);
      };

      state.addListener(notificationsCursor, updateNotificationBadge, null);
    }

    return $q.when();
  };

  return {
    isDismissed,
    notify,
    notifySystem,
    notifyGenericError,
    dismiss,
    initialize
  };
}
<commit_msg>Disable notification clear on timeout<commit_after>export let serviceName = 'notifications';
export default /*@ngInject*/ function notifications(
  $cordovaBadge,
  $cordovaLocalNotification,
  $rootScope,
  $window,
  $log,
  $timeout,
  $q,
  devices,
  cryptography,
  identity,
  navigation,
  state,
  R
) {
  let contacts;
  let activeWebNotifications = {};
  // cordovaLocalNotification uses number IDs
  // notificationId is just channelId, and has the form toc-{32hex-chars}
  let getCordovaNotificationId = (notificationId) => {
    let lastEightHex = notificationId.substr(notificationId.length - 8);
    return parseInt(lastEightHex, 16);
  };

  let getNotificationMessage = function getNotificationMessage(notificationId) {
    let channelId = notificationId;
    let channelCursor = state.cloud.channels.select([channelId]);

    let notificationMessage;
    if (channelCursor.get(['inviteStatus']) === 'received') {
      return 'New invite received!';
    }

    let messageIdCursor = channelCursor.select(['latestMessageId']);
    let messageId = messageIdCursor.get();
    if (!messageId) {
      return '';
    }

    return state.cloud.messages.get([
      channelId, messageId, 'messageInfo', 'content'
    ]);
  };

  let notifyCordova = function notifyCordova(notificationInfo) {
    let channelId = notificationInfo.id;
    let channelCursor = state.cloud.channels.select([channelId]);
    let contactId = channelCursor.get(['channelInfo', 'contactIds'])[0];
    let contactCursor = state.cloud.contacts.select(contactId);
    let contactInfo = contactCursor.get('userInfo');

    let icon = identity.getAvatar(contactInfo);
    let title = contactInfo.displayName || 'Anonymous';
    let text = getNotificationMessage(notificationInfo.id);

    let cordovaNotificationInfo = {
      id: notificationInfo.cordovaNotificationId,
      title,
      text,
      icon,
      sound: 'res://platform_default',
      smallIcon: 'res://icon.png',
      data: notificationInfo
    };

    return $cordovaLocalNotification.schedule(cordovaNotificationInfo);
  };

  let notifyWeb = function notifyWeb(notificationInfo) {
    if (!$window.Notification) {
      return $q.when();
    }
    let channelId = notificationInfo.id;
    let channelCursor = state.cloud.channels.select([channelId]);
    let contactId = channelCursor.get(['channelInfo', 'contactIds'])[0];
    let contactCursor = state.cloud.contacts.select(contactId);
    let contactInfo = contactCursor.get('userInfo');

    let webNotificationOptions = {
      body: getNotificationMessage(notificationInfo.id),
      icon: identity.getAvatar(contactInfo),
      tag: notificationInfo.id
    };

    let notificationTitle = contactInfo.displayName || 'Anonymous';

    activeWebNotifications[notificationInfo.id] =
      new Notification(notificationTitle, webNotificationOptions);

    let notificationInstance = activeWebNotifications[notificationInfo.id];
    notificationInstance.addEventListener('click',
      () => handleNotificationClick(notificationInfo.id));

    // $timeout(() => {
    //   notificationInstance.close();
    // }, 5000, false);

    return $q.when();
  };

  let notify = function notify(notificationId) {
    let cordovaNotificationId = getCordovaNotificationId(notificationId);

    let notifyNative = (notificationInfo) => {
      if (devices.isInForeground()) {
        return $q.when();
      }
      // apparently, web notificaions api works in cordova for android.
      // TODO: check with iOS
      // return devices.isCordovaApp() ?
      //   notifyCordova(notificationInfo) :
      //   notifyWeb(notificationInfo);
      return notifyWeb(notificationInfo);
    };

    let notificationCursor = state.cloud.notifications.select([notificationId]);

    let notificationInfo = {
      id: notificationId,
      cordovaNotificationId
    };

    return notifyNative(notificationInfo)
      .then(() => state.save(
        notificationCursor, ['notificationInfo'], notificationInfo
      ))
      .then(() => state.save(notificationCursor, ['dismissed'], false));
  };

  let notifySystem = function notifySystem(message) {
    let systemMessageCursor = state.memory.notifications.select(['system']);

    return cryptography.getRandomBase64(2)
      .then((systemMessageId) => state.save(
        systemMessageCursor,
        ['notificationInfo'],
        { id: systemMessageId, message }
      ));
  };

  let notifyGenericError = function notifyGenericError(error) {
    $log.error(error);

    return notifySystem(
      'Something went wrong. ' +
      'Please contact the developer for further troubleshooting.'
    );
  };

  let dismissCordova = function dismissCordova(notificationInfo) {
    return $cordovaLocalNotification
      .clear(notificationInfo.cordovaNotificationId)
      // needed to avoid notifications poping up on next device startup
      .then(() => $cordovaLocalNotification
        .cancel(notificationInfo.cordovaNotificationId));
  };

  let dismissWeb = function dismissWeb(notificationInfo) {
    if (!$window.Notification) {
      return $q.when();
    }
    if (activeWebNotifications[notificationInfo.id] === undefined) {
      return $q.when();
    }

    activeWebNotifications[notificationInfo.id].close();
    return $q.when();
  };

  let dismiss = function dismiss(notificationId) {
    let cordovaNotificationId = getCordovaNotificationId(notificationId);

    if (isDismissed(notificationId)) {
      return $q.when();
    }

    let dismissNative = (notificationInfo) => {
      // return devices.isCordovaApp() ?
      //   dismissCordova(notificationInfo) :
      //   dismissWeb(notificationInfo);
      return dismissWeb(notificationInfo);
    };

    let notificationCursor = state.cloud.notifications.select([notificationId]);

    let notificationInfo = {
      id: notificationId,
      cordovaNotificationId
    };

    return dismissNative(notificationInfo)
      .then(() => state.save(notificationCursor, ['dismissed'], true));
  };

  let isDismissed = function isDismissed(notificationId) {
    return state.cloud.notifications.get([notificationId, 'dismissed']);
  };

  let handleNotificationClick = function handleNotificationClick(channelId) {
    let channelCursor = state.cloud.channels.select([channelId]);

    if (channelCursor.get(['inviteStatus'])  === 'received') {
      return contacts.showAcceptInviteDialog(channelId);
    }

    return dismiss(channelId)
      .then(() => navigation.navigate(channelId))
      .then(() => state.save(channelCursor, ['viewingLatest'], true));
  };

  $rootScope.$on('$cordovaLocalNotification:click', (event, notification) => {
    let viewId = JSON.parse(notification.data).id;

    if (viewId === 'home') {
      return navigation.navigate(viewId);
    }

    let channelId = viewId;
    return handleNotificationClick(channelId);
  });

  let initialize = function initialize(contactsService) {
    contacts = contactsService;

    if ($window.Notification) {
      $window.Notification.requestPermission();
    }

    if (devices.isCordovaApp() && $window.cordova.plugins.backgroundMode) {
      // enables background operation
      // de.appplant.cordova.plugin.background-mode required
      // $window.cordova.plugins.backgroundMode.setDefaults({ silent: true });
      $window.cordova.plugins.backgroundMode.setDefaults({
        title: 'Toc Messenger',
        ticker: 'Toc Messenger is running in background',
        text: 'Online'
      });

      $window.cordova.plugins.backgroundMode.enable();
    }

    // excluding android because it shows up as a notification instead of badge
    if (devices.isCordovaApp() && !devices.isAndroidApp() &&
      $window.cordova.plugins.notification) {
      let notificationsCursor = state.cloud.notifications;
      let updateNotificationBadge = () => {
        let notificationCount = R.pipe(
          R.values,
          R.reject(R.prop('dismissed'))
        )(notificationsCursor.get()).length;

        $cordovaBadge.get()
          .then((badgeCount) => {
            if (badgeCount === notificationCount) {
              return;
            }

            return $cordovaBadge.set(notificationCount);
          })
          .catch($log.error);
      };

      state.addListener(notificationsCursor, updateNotificationBadge, null);
    }

    return $q.when();
  };

  return {
    isDismissed,
    notify,
    notifySystem,
    notifyGenericError,
    dismiss,
    initialize
  };
}
<|endoftext|>


<commit_before>/*
 * Converts JSON data received from the server to flat array of values.
 */
function jsonToValuesArray(jsonData) {
  var errMsgs = [];
  for (var key in jsonData) {
  	var values = jsonData[key];
    $.each(values, function (idx, val) {
      errMsgs.push(val);
	});
  }
  return errMsgs;
}

/*
 * Calls callback function on AJAX success (because built-in functions don't
 * work!)
 */
$.fn.onAjaxComplete = function (cb) {
  $(this)
  .on('ajax:success', function () {
	cb();
  })
  .on('ajax:error', function () {
	cb();
  });
}

// Number of all tutorial steps
var TUTORIAL_STEPS_CNT = 26;

/**
 * Initializes tutorial steps for the current page.
 * NOTE: You can specify steps manually in JS with steps parameter (preferred
 * way), or hardcode them in HTML
 * NOTE: If some steps edit page, then this function needs to be called several
 * times for the same page, but for different steps. The same goes if the page
 * has discontinuous tutorial steps. In such cases, use steps branching, e.g.:
 * @example
 * var tutorialData = Cookies.get('tutorial_data');
 * if (tutorialData) {
 *   tutorialData = JSON.parse(tutorialData);
 *   var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
 *
 *   if (stepNum >= 6 && stepNum <= 7) {
 *     ...
 *   } else if ...
 * NOTE: If an element the popup is pointing at is of lesser horizontal length
 * than the popup itself, then it will not be positioned correctly if it's
 * position is top or bottom, so set/change the step's position to either left
 * or right (and don't use any custom styling!), e.g.:
 * @example
 * var steps = [
 *   {
 *     ...
 *     position: 'right'
 *   },
 *   {
 *   ...
 * ];
 * NOTE: If only one page step is needed, then make pageFirstStepN ==
 * pageLastStepN (both represent the one and only step number)
 *
 * @param  {number} pageFirstStepN Page's first step number
 * @param  {number} pageLastStepN Page's last step number
 * @param  {string} nextPagePath Next page absolute path
 * @param {function} beforeCb Callback called before the tutorial starts. Mainly
 *  used for setting 'pointer-events: none' on the elements the page's steps
 *  highlight.
 * @param {function} endCb Callback called after the tutorial ends. Mainly used
 *  for setting 'pointer-events: auto' on the elements the page's steps
 *  highlight.
 * @param {object} steps Optional JSON containing introJs steps. They can be
 *  specified here, or hardcoded in HTML.
 */
function initPageTutorialSteps(pageFirstStepN, pageLastStepN, nextPagePath,
                               beforeCb, endCb, steps) {
  var tutorialData = Cookies.get('tutorial_data');
  if (tutorialData) {
    tutorialData = JSON.parse(tutorialData);
    var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
    if (isNaN(stepNum)) {
      // Cookies data initialization
      stepNum = 1;
      Cookies.set('current_tutorial_step', stepNum);
      tutorialData[0].backPagesPaths = [];
      Cookies.set('tutorial_data', tutorialData);
    }
    var thisPagePath = window.location.pathname;
    beforeCb();

    // Initialize tutorial for the current pages' steps
    var isLastStep = pageLastStepN == TUTORIAL_STEPS_CNT;
    var doneLabel = isLastStep ? 'Start using sciNote' : 'End tutorial';
    if (_.isUndefined(steps)) {
      // This approach (tutorial data separately in ERB) shouldn't be used,
      // because it's buggy and inconvenient
      introJs()
       .setOptions({
         overlayOpacity: '0.2',
         prevLabel: 'Back',
         nextLabel: 'Next',
         skipLabel: 'End tutorial',
         doneLabel: doneLabel,
         showBullets: false,
         showStepNumbers: false,
         exitOnOverlayClick: false,
         exitOnEsc: false,
         disableInteraction: true,
         keyboardNavigation: false,
         tooltipClass: 'custom next-page-link'
       })
       .goToStep(stepNum - (pageFirstStepN - 1))
       .onexit(function() {
         Cookies.remove('tutorial_data');
         Cookies.remove('current_tutorial_step');
         location.reload();
       })
       .oncomplete(function() {
         Cookies.remove('tutorial_data');
         Cookies.remove('current_tutorial_step');
         location.reload();
       })
       .start();
    } else {
      if (!isLastStep) {
        // Add extra fake step, so that next button on last step of current page
        // gets focused. Also, if current page has only one step, this adds back
        // and next buttons to the popup.
        steps.push({});
      }

      introJs()
       .setOptions({
         overlayOpacity: '0.2',
         prevLabel: 'Back',
         nextLabel: 'Next',
         skipLabel: 'End tutorial',
         doneLabel: doneLabel,
         showBullets: false,
         showStepNumbers: false,
         exitOnOverlayClick: false,
         exitOnEsc: false,
         disableInteraction: true,
         keyboardNavigation: false,
         tooltipClass: 'custom next-page-link',
         steps: steps
       })
       .goToStep(stepNum - (pageFirstStepN - 1))
       .onexit(function() {
         Cookies.remove('tutorial_data');
         Cookies.remove('current_tutorial_step');
         location.reload();
       })
       .oncomplete(function() {
         Cookies.remove('tutorial_data');
         Cookies.remove('current_tutorial_step');
         location.reload();
       })
       .start();
    }

    // Page navigation when coming to this page from previous/next page
    $(function() {
      if (stepNum === pageFirstStepN && stepNum > 1) {
        $('.introjs-prevbutton').removeClass('introjs-disabled');
      } else if (stepNum === pageLastStepN && stepNum < TUTORIAL_STEPS_CNT) {
        $('.introjs-nextbutton').removeClass('introjs-disabled');
      }
    });

    // Page navigation when already on this page

    $('.introjs-skipbutton').click(function() {
      Cookies.remove('tutorial_data');
      Cookies.remove('current_tutorial_step');

      endCb();
    });

    $('.introjs-prevbutton').click(function() {
      if (stepNum > 1) {
        Cookies.set('current_tutorial_step', --stepNum);

        if (stepNum === pageFirstStepN && stepNum > 1) {
          $('.introjs-prevbutton').removeClass('introjs-disabled');
        } else if (stepNum < pageFirstStepN) {
          // Go to previous page;

          var prevPagePath = tutorialData[0].backPagesPaths.pop();
          Cookies.set('tutorial_data', tutorialData);
          $('.introjs-prevbutton').attr('href', prevPagePath);
          introJs().exit();
          endCb();
        }
      }
    });

    $('.introjs-nextbutton').click(function() {
      if (stepNum < TUTORIAL_STEPS_CNT) {
        Cookies.set('current_tutorial_step', ++stepNum);

        if (stepNum === pageLastStepN && stepNum < TUTORIAL_STEPS_CNT) {
          $('.introjs-nextbutton').removeClass('introjs-disabled');
        } else if (stepNum > pageLastStepN) {
          // Go to next page

          tutorialData[0].backPagesPaths.push(thisPagePath);
          Cookies.set('tutorial_data', tutorialData);
          $('.introjs-nextbutton').attr('href', nextPagePath);
          introJs().exit();
          endCb();
        }
      }
    });
  }
}
<commit_msg>Removed some unneeded intro.js tutorial code.<commit_after>/*
 * Converts JSON data received from the server to flat array of values.
 */
function jsonToValuesArray(jsonData) {
  var errMsgs = [];
  for (var key in jsonData) {
  	var values = jsonData[key];
    $.each(values, function (idx, val) {
      errMsgs.push(val);
	});
  }
  return errMsgs;
}

/*
 * Calls callback function on AJAX success (because built-in functions don't
 * work!)
 */
$.fn.onAjaxComplete = function (cb) {
  $(this)
  .on('ajax:success', function () {
	cb();
  })
  .on('ajax:error', function () {
	cb();
  });
}

// Number of all tutorial steps
var TUTORIAL_STEPS_CNT = 26;

/**
 * Initializes tutorial steps for the current page.
 * NOTE: You can specify steps manually in JS with steps parameter (preferred
 * way), or hardcode them in HTML
 * NOTE: If some steps edit page, then this function needs to be called several
 * times for the same page, but for different steps. The same goes if the page
 * has discontinuous tutorial steps. In such cases, use steps branching, e.g.:
 * @example
 * var tutorialData = Cookies.get('tutorial_data');
 * if (tutorialData) {
 *   tutorialData = JSON.parse(tutorialData);
 *   var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
 *
 *   if (stepNum >= 6 && stepNum <= 7) {
 *     ...
 *   } else if ...
 * NOTE: If an element the popup is pointing at is of lesser horizontal length
 * than the popup itself, then it will not be positioned correctly if it's
 * position is top or bottom, so set/change the step's position to either left
 * or right (and don't use any custom styling!), e.g.:
 * @example
 * var steps = [
 *   {
 *     ...
 *     position: 'right'
 *   },
 *   {
 *   ...
 * ];
 * NOTE: If only one page step is needed, then make pageFirstStepN ==
 * pageLastStepN (both represent the one and only step number)
 *
 * @param  {number} pageFirstStepN Page's first step number
 * @param  {number} pageLastStepN Page's last step number
 * @param  {string} nextPagePath Next page absolute path
 * @param {function} beforeCb Callback called before the tutorial starts. Mainly
 *  used for setting 'pointer-events: none' on the elements the page's steps
 *  highlight.
 * @param {function} endCb Callback called after the tutorial ends. Mainly used
 *  for setting 'pointer-events: auto' on the elements the page's steps
 *  highlight.
 * @param {object} steps JSON containing intro.js steps.
 */
function initPageTutorialSteps(pageFirstStepN, pageLastStepN, nextPagePath,
                               beforeCb, endCb, steps) {
  var tutorialData = Cookies.get('tutorial_data');
  if (tutorialData) {
    tutorialData = JSON.parse(tutorialData);
    var stepNum = parseInt(Cookies.get('current_tutorial_step'), 10);
    if (isNaN(stepNum)) {
      // Cookies data initialization
      stepNum = 1;
      Cookies.set('current_tutorial_step', stepNum);
      tutorialData[0].backPagesPaths = [];
      Cookies.set('tutorial_data', tutorialData);
    }
    var thisPagePath = window.location.pathname;
    beforeCb();

    // Initialize tutorial for the current pages' steps

    var doneLabel;
    if (pageLastStepN == TUTORIAL_STEPS_CNT) {
      doneLabel = 'Start using sciNote';
    } else {
      doneLabel = 'End tutorial';
      // Add extra fake step, so that next button on last step of current page
      // gets focused. Also, if current page has only one step, this adds back
      // and next buttons to the popup.
      steps.push({});
    }
    introJs()
     .setOptions({
       overlayOpacity: '0.2',
       prevLabel: 'Back',
       nextLabel: 'Next',
       skipLabel: 'End tutorial',
       doneLabel: doneLabel,
       showBullets: false,
       showStepNumbers: false,
       exitOnOverlayClick: false,
       exitOnEsc: false,
       disableInteraction: true,
       keyboardNavigation: false,
       tooltipClass: 'custom next-page-link',
       steps: steps
     })
     .goToStep(stepNum - (pageFirstStepN - 1))
     .onexit(function() {
       Cookies.remove('tutorial_data');
       Cookies.remove('current_tutorial_step');
       location.reload();
     })
     .oncomplete(function() {
       Cookies.remove('tutorial_data');
       Cookies.remove('current_tutorial_step');
       location.reload();
     })
     .start();

    // Page navigation when coming to this page from previous or from next page
    $(function() {
      if (stepNum === pageFirstStepN && stepNum > 1) {
        $('.introjs-prevbutton').removeClass('introjs-disabled');
      } else if (stepNum === pageLastStepN && stepNum < TUTORIAL_STEPS_CNT) {
        $('.introjs-nextbutton').removeClass('introjs-disabled');
      }
    });

    // Page navigation when already on this page

    $('.introjs-skipbutton').click(function() {
      Cookies.remove('current_tutorial_step');
      Cookies.remove('tutorial_data');

      endCb();
    });

    $('.introjs-prevbutton').click(function() {
      if (stepNum > 1) {
        Cookies.set('current_tutorial_step', --stepNum);

        if (stepNum === pageFirstStepN && stepNum > 1) {
          $('.introjs-prevbutton').removeClass('introjs-disabled');
        } else if (stepNum < pageFirstStepN) {
          // Go to previous page;

          var prevPagePath = tutorialData[0].backPagesPaths.pop();
          Cookies.set('tutorial_data', tutorialData);
          $('.introjs-prevbutton').attr('href', prevPagePath);
          introJs().exit();
          endCb();
        }
      }
    });

    $('.introjs-nextbutton').click(function() {
      if (stepNum < TUTORIAL_STEPS_CNT) {
        Cookies.set('current_tutorial_step', ++stepNum);

        if (stepNum === pageLastStepN && stepNum < TUTORIAL_STEPS_CNT) {
          $('.introjs-nextbutton').removeClass('introjs-disabled');
        } else if (stepNum > pageLastStepN) {
          // Go to next page

          tutorialData[0].backPagesPaths.push(thisPagePath);
          Cookies.set('tutorial_data', tutorialData);
          $('.introjs-nextbutton').attr('href', nextPagePath);
          introJs().exit();
          endCb();
        }
      }
    });
  }
}
<|endoftext|>


<commit_before>var secrets = require('../../config/secrets');

module.exports = {
  db: {
    connector: 'mongodb',
    connectionTimeout: 10000,
    url: secrets.db,
    useNewUrlParser: true,
    allowExtendedOperators: true
  },
  mail: {
    connector: 'mail',
    transport: {
      type: 'ses',
      accessKeyId: process.env.SES_ID,
      secretAccessKey: process.env.SES_SECRET
    }
  }
};
<commit_msg>fix(api): use mongodb+srv url protocol (#38831)<commit_after>var secrets = require('../../config/secrets');

module.exports = {
  db: {
    connector: 'mongodb',
    protocol: 'mongodb+srv',
    connectionTimeout: 10000,
    url: secrets.db,
    useNewUrlParser: true,
    allowExtendedOperators: true
  },
  mail: {
    connector: 'mail',
    transport: {
      type: 'ses',
      accessKeyId: process.env.SES_ID,
      secretAccessKey: process.env.SES_SECRET
    }
  }
};
<|endoftext|>


<commit_before>//= require fileupload/tmpl
//= require fileupload/jquery.iframe-transport
//= require fileupload/jquery.fileupload.js
//= require fileupload/jquery.fileupload-process.js
//= require fileupload/jquery.fileupload-validate.js
//= require fileupload/jquery.fileupload-ui.js
//
/*
 * jQuery File Upload Plugin JS Example
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

(function( $ ){
  'use strict';

  $.fn.extend({
    sufiaUploader: function( options ) {
      // Initialize our jQuery File Upload widget.
      // TODO: get these values from configuration.
      this.fileupload({
        // xhrFields: {withCredentials: true},              // to send cross-domain cookies
        // acceptFileTypes: /(\.|\/)(png|mov|jpe?g|pdf)$/i, // not a strong check, just a regex on the filename
        // limitMultiFileUploadSize: 500000000, // bytes
        limitConcurrentUploads: 6,
        maxNumberOfFiles: 100,
        maxFileSize: 500000000, // bytes, i.e. 500 MB
        autoUpload: true,
        url: Routes.sufia_uploads_path({trailing_slash: true}),
        type: 'POST'
      })
      .bind('fileuploadadded', function (e, data) {
        $(e.currentTarget).find('button.cancel').removeClass('hidden');
      });
    }
  });
})(jQuery);
<commit_msg>Increase JS max upload size<commit_after>//= require fileupload/tmpl
//= require fileupload/jquery.iframe-transport
//= require fileupload/jquery.fileupload.js
//= require fileupload/jquery.fileupload-process.js
//= require fileupload/jquery.fileupload-validate.js
//= require fileupload/jquery.fileupload-ui.js
//
/*
 * jQuery File Upload Plugin JS Example
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

(function( $ ){
  'use strict';

  $.fn.extend({
    sufiaUploader: function( options ) {
      // Initialize our jQuery File Upload widget.
      // TODO: get these values from configuration.
      this.fileupload({
        // xhrFields: {withCredentials: true},              // to send cross-domain cookies
        // acceptFileTypes: /(\.|\/)(png|mov|jpe?g|pdf)$/i, // not a strong check, just a regex on the filename
        // limitMultiFileUploadSize: 500000000, // bytes
        limitConcurrentUploads: 6,
        maxNumberOfFiles: 100,
        maxFileSize: 1073741824, // bytes, 1GB
        autoUpload: true,
        url: Routes.sufia_uploads_path({trailing_slash: true}),
        type: 'POST'
      })
      .bind('fileuploadadded', function (e, data) {
        $(e.currentTarget).find('button.cancel').removeClass('hidden');
      });
    }
  });
})(jQuery);
<|endoftext|>


<commit_before>module.exports = function(request, response, next) {
  function success(content) {
    response.statusCode = 200;
    response.setHeader('Content-Type', 'application/json');
    response.end(content);
  }
  function error(status, content) {
    response.statusCode = status;
    response.setHeader('Content-Type', 'application/json');
    response.end(content);
  }

  // endpoint for simple authentication with grant_type "password"
  if (request.url === '/token' && request.method === 'POST') {
    if (request.body.grant_type === 'password') {
      if (request.body.username === 'letme' && request.body.password === 'in') {
        success('{ "access_token": "secret token!" }');
      } else {
        error(400, '{ "error": "invalid_grant" }');
      }
    } else {
      error(400, '{ "error": "unsupported_grant_type" }');
    }

  // endpoint for token revokation
  } else if (request.url === '/revoke' && request.method === 'POST') {
    if (request.body.token_type_hint === 'access_token' || request.body.token_type_hint === 'refresh_token') {
      success('');
    } else {
      error(400, '{ "error": "unsupported_token_type" }');
    }

  // endpoint that will always cause an authorization error
  } else if (request.url === '/auth-error' && request.method === 'GET') {
    error(401, '{ "error": "your token has expired" }');

  // authentication endpoint that also supports token refreshing via the "refresh_token" grant type
  } else if (request.url === '/v2/token' && request.method === 'POST') {
    if (request.body.grant_type === 'password') {
      if (request.body.username === 'letme' && request.body.password === 'in') {
        success('{ "access_token": "secret token!", "refresh_token": "secret refresh token!", "expires_in": 15 }');
      } else {
        error(400, '{ "error": "invalid_grant" }');
      }
    } else if (request.body.grant_type === 'refresh_token') {
      if (request.body.refresh_token === 'secret refresh token!') {
        success('{ "access_token": "' + Math.random().toString(36).substring(10) + '" }');
      } else {
        error(400, '{ "error": "invalid_grant" }');
      }
    } else {
      error(400, '{ "error": "unsupported_grant_type" }');
    }

  // authentication endpoint that includes an authenticated account in the response
  } else if (request.url === '/v3/token' && request.method === 'POST') {
    if (request.body.grant_type === 'password') {
      if (request.body.username === 'letme' && request.body.password === 'in') {
        success('{ "access_token": "secret token!", "account_id": 1 }');
      } else {
        error(400, '{ "error": "invalid_grant" }');
      }
    } else {
      error(400, '{ "error": "unsupported_grant_type" }');
    }

  // endpoint that returns the data for the Ember Data Account model
  } else if (request.url === '/accounts/1' && request.method === 'GET') {
    if (request.headers.authorization === 'Bearer secret token!') {
      success('{ "account": { "id": 1, "login": "letme", "name": "Some Person"} }');
    } else {
      error(401, '{}');
    }

  // endpoint that returns the data for the Ember Data Post model
  } else if (request.url === '/posts' && request.method === 'GET') {
    if (request.headers.authorization === 'Bearer secret token!') {
      success('{ "posts": [{ "id": 1, "title": "first post", "body": "some content"}, { "id": 2, "title": "2nd post", "body": "some other body" }] }');
    } else {
      error(401, '{}');
    }

  // custom authentication endpoint with a completely non-standard interface
  } else if (request.url === '/v4/session') {
    if (request.method === 'POST') {
      if (request.body.session.identification === 'letme' && request.body.session.password === 'in') {
        success('{ "session": { "token": "secret token!" } }');
      } else {
        error(422, '{ "error": "invalid credentials" }');
      }
    // callback that will be invoked when the user logs out
    } else if (request.method === 'DELETE') {
      success('');
    } else {
      next();
    }
  } else if (request.url === '/v4/data') {
    if (request.headers.authorization === 'Token: secret token!') {
      success('{ "some": "data" }');
    } else {
      error(401, '{}');
    }

  // endpoint for the devise example
  } else if (request.url === '/v5/users/sign_in') {
    if (request.method === 'POST') {
      if (request.body.user.email === 'letme' && request.body.user.password === 'in') {
        success('{ "user_email": "letme", "user_token": "secret token!" }');
      } else {
        error(422, '');
      }
    } else {
      next();
    }
  } else if (request.url === '/v5/data') {
    if (request.headers.authorization === 'Token user_token="secret token!", user_email="letme"') {
      success('{ "some": "data" }');
    } else {
      error(401, '{}');
    }

  } else {
    next();
  }
};
<commit_msg>fixed devise example<commit_after>module.exports = function(request, response, next) {
  function success(content) {
    response.statusCode = 200;
    response.setHeader('Content-Type', 'application/json');
    response.end(content);
  }
  function error(status, content) {
    response.statusCode = status;
    response.setHeader('Content-Type', 'application/json');
    response.end(content);
  }

  // endpoint for simple authentication with grant_type "password"
  if (request.url === '/token' && request.method === 'POST') {
    if (request.body.grant_type === 'password') {
      if (request.body.username === 'letme' && request.body.password === 'in') {
        success('{ "access_token": "secret token!" }');
      } else {
        error(400, '{ "error": "invalid_grant" }');
      }
    } else {
      error(400, '{ "error": "unsupported_grant_type" }');
    }

  // endpoint for token revokation
  } else if (request.url === '/revoke' && request.method === 'POST') {
    if (request.body.token_type_hint === 'access_token' || request.body.token_type_hint === 'refresh_token') {
      success('');
    } else {
      error(400, '{ "error": "unsupported_token_type" }');
    }

  // endpoint that will always cause an authorization error
  } else if (request.url === '/auth-error' && request.method === 'GET') {
    error(401, '{ "error": "your token has expired" }');

  // authentication endpoint that also supports token refreshing via the "refresh_token" grant type
  } else if (request.url === '/v2/token' && request.method === 'POST') {
    if (request.body.grant_type === 'password') {
      if (request.body.username === 'letme' && request.body.password === 'in') {
        success('{ "access_token": "secret token!", "refresh_token": "secret refresh token!", "expires_in": 15 }');
      } else {
        error(400, '{ "error": "invalid_grant" }');
      }
    } else if (request.body.grant_type === 'refresh_token') {
      if (request.body.refresh_token === 'secret refresh token!') {
        success('{ "access_token": "' + Math.random().toString(36).substring(10) + '" }');
      } else {
        error(400, '{ "error": "invalid_grant" }');
      }
    } else {
      error(400, '{ "error": "unsupported_grant_type" }');
    }

  // authentication endpoint that includes an authenticated account in the response
  } else if (request.url === '/v3/token' && request.method === 'POST') {
    if (request.body.grant_type === 'password') {
      if (request.body.username === 'letme' && request.body.password === 'in') {
        success('{ "access_token": "secret token!", "account_id": 1 }');
      } else {
        error(400, '{ "error": "invalid_grant" }');
      }
    } else {
      error(400, '{ "error": "unsupported_grant_type" }');
    }

  // endpoint that returns the data for the Ember Data Account model
  } else if (request.url === '/accounts/1' && request.method === 'GET') {
    if (request.headers.authorization === 'Bearer secret token!') {
      success('{ "account": { "id": 1, "login": "letme", "name": "Some Person"} }');
    } else {
      error(401, '{}');
    }

  // endpoint that returns the data for the Ember Data Post model
  } else if (request.url === '/posts' && request.method === 'GET') {
    if (request.headers.authorization === 'Bearer secret token!') {
      success('{ "posts": [{ "id": 1, "title": "first post", "body": "some content"}, { "id": 2, "title": "2nd post", "body": "some other body" }] }');
    } else {
      error(401, '{}');
    }

  // custom authentication endpoint with a completely non-standard interface
  } else if (request.url === '/v4/session') {
    if (request.method === 'POST') {
      if (request.body.session.identification === 'letme' && request.body.session.password === 'in') {
        success('{ "session": { "token": "secret token!" } }');
      } else {
        error(422, '{ "error": "invalid credentials" }');
      }
    // callback that will be invoked when the user logs out
    } else if (request.method === 'DELETE') {
      success('');
    } else {
      next();
    }
  } else if (request.url === '/v4/data') {
    if (request.headers.authorization === 'Token: secret token!') {
      success('{ "some": "data" }');
    } else {
      error(401, '{}');
    }

  // endpoint for the devise example
  } else if (request.url === '/v5/users/sign_in') {
    if (request.method === 'POST') {
      if (request.body.user.user_email === 'letme' && request.body.user.password === 'in') {
        success('{ "user_email": "letme", "token": "secret token!" }');
      } else {
        error(422, '');
      }
    } else {
      next();
    }
  } else if (request.url === '/v5/data') {
    if (request.headers.authorization === 'Token token="secret token!", user_email="letme"') {
      success('{ "some": "data" }');
    } else {
      error(401, '{}');
    }

  } else {
    next();
  }
};
<|endoftext|>


<commit_before>function submitTip(event) {
    event.preventDefault()
    var url = $(event.target).attr('action')
    var data = $(event.target).serialize()

    $.ajax({
        dataType: "json",
        type: "POST",
        url: url,
        data: data
    }).done(function(data) {
        search_infoWindow.close()
        markerPair = addMarker(data)
        openMarker(markerPair.marker, markerPair.infowindow)
        $('#tip-list').prepend(renderTipNavPartial(data))
    }).fail(function(data) {
        error_text = data.responseJSON.place_id[0]
        $(event.target).find('input[name="commit"]').addClass('hidden')
        $(event.target).append('
' + error_text + '
')
    })
}

function loadMarkers(data) {
    for (var i = data.length - 1; i >= 0; i--) {
        addMarker(data[i])
    }
}

function openMarker(marker, infowindow) {
    event.preventDefault();
    closeAllInfoWindows();

    if (infowindow == undefined) {
        place_id = $(event.target).data('place_id')
        obj = findMarker(place_id)
        infoWindows[obj.index].open(map, obj.mark)
    } else {
        infowindow.open(map, marker);
    }

}

function findMarker(place_id) {
    for (var i = markers.length - 1; i >= 0; i--) {
        if (markers[i].place_id == place_id) {
            return {
                mark: markers[i],
                index: i
            }
        }
    }
}

function updateTipModal(event) {
    event.preventDefault();
    var form = $(event.target)
    var url = form.attr('action')
    var data = $(this).serialize()

    $.ajax({
        type: "PATCH",
        url: url,
        data: data
    }).done(function(data) {
        $('#modal-html').html(data)
        rating = $('#tip_rating').val()
        comment = $('#modal-html').find('textarea').text()
        if (comment === "") {
            $('#info-box-comment i').addClass('hidden')
        } else {
            $('#info-box-comment i').removeClass('hidden')
        }
        $('#info-box-rating span').text(rating)
        $('.large-info-updated').show().fadeOut(3000)
    })
}

$(document).on('click', '#map-info-box', function(event) {
    var linkTarget = $(event.target).closest('.gm-style-iw').find('a');
    var url = linkTarget.attr('name')
    $.ajax({
        type: "GET",
        url: url
    }).done(function(data) {
        $('#modal-html').html(data)
        linkTarget.modal();
    })
})

$(document).on('click', '.user input', function() {
    console.log('worked')
    $('#tip_rating option[value=' + $(this).val() + ']').attr('selected', 'selected').parent().focus()
})
<commit_msg>Update tip_controller.js<commit_after>function submitTip(event) {
    event.preventDefault()
    var url = $(event.target).attr('action')
    var data = $(event.target).serialize()

    $.ajax({
        dataType: "json",
        type: "POST",
        url: url,
        data: data
    }).done(function(data) {
        search_infoWindow.close()
        markerPair = addMarker(data)
        openMarker(markerPair.marker, markerPair.infowindow)
        $('#tip-list').prepend(renderTipNavPartial(data))
    }).fail(function(data) {
        error_text = data.responseJSON.place_id[0]
        $(event.target).find('input[name="commit"]').addClass('hidden')
        $(event.target).append('
' + error_text + '
')
    })
}

function loadMarkers(data) {
    for (var i = data.length - 1; i >= 0; i--) {
        addMarker(data[i])
    }
}

function openMarker(marker, infowindow) {
    event.preventDefault();
    closeAllInfoWindows();

    if (infowindow == undefined) {
        place_id = $(event.target).data('place_id')
        obj = findMarker(place_id)
        infoWindows[obj.index].open(map, obj.mark)
    } else {
        infowindow.open(map, marker);
    }

}

function findMarker(place_id) {
    for (var i = markers.length - 1; i >= 0; i--) {
        if (markers[i].place_id == place_id) {
            return {
                mark: markers[i],
                index: i
            }
        }
    }
}

function updateTipModal(event) {
    event.preventDefault();
    var form = $(event.target)
    var url = form.attr('action')
    var data = $(this).serialize()

    $.ajax({
        type: "PATCH",
        url: url,
        data: data
    }).done(function(data) {
        $('#modal-html').html(data)
        rating = $('#tip_rating').val()
        comment = $('#modal-html').find('textarea').text()
        if (comment === "") {
            $('#info-box-comment i').addClass('hidden')
        } else {
            $('#info-box-comment i').removeClass('hidden')
        }
        $('#info-box-rating span').text(rating)
        $('.large-info-updated').show().fadeOut(3000)
    })
}

$(document).on('click', '#map-info-box', function(event) {
    var linkTarget = $(event.target).closest('.gm-style-iw').find('a');
    var url = linkTarget.attr('name')
    $.ajax({
        type: "GET",
        url: url
    }).done(function(data) {
        $('#modal-html').html(data)
        linkTarget.modal();
    })
})

$(document).on('click', '.user input', function() {
    $('#tip_rating option[value=' + $(this).val() + ']').attr('selected', 'selected').parent().focus()
})
<|endoftext|>


<commit_before>'use strict';

const boundsToMapLocation = (inputCoords) => {
  const WORLD_DIM = { height: 256, width: 256 };
  var ZOOM_MAX = 21;

  // height is 32rem in RRA css
  const mapHeight = 32 * Number(window.getComputedStyle(document.body)
                         .getPropertyValue('font-size').replace(/px/, ''));
  // width is container width minus lesser of 33.33% and 400px in iD css
  const containerWidth = document.querySelector('#app-container').offsetWidth;
  const mapSidebarWidth = containerWidth * 0.33 < 400 ? Math.round(containerWidth * 0.33) : 400;

  const mapDimensions = {
    width: containerWidth - mapSidebarWidth,
    height: mapHeight
  };

  const bounds = {
    southwest: {
      lng: inputCoords[0],
      lat: inputCoords[1]},
    northeast: {
      lng: inputCoords[2],
      lat: inputCoords[3]}
  };

  const latRad = (lat) => {
    const sin = Math.sin(lat * Math.PI / 180);
    const radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
    return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
  };

  const getZoom = (mapPx, worldPx, fraction) => {
    return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2);
  };

  const ne = bounds.northeast;
  const sw = bounds.southwest;

  const latFraction = (latRad(ne.lat) - latRad(sw.lat)) / Math.PI;

  const lngDiff = ne.lng - sw.lng;
  const lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;

  const latZoom = getZoom(mapDimensions.height, WORLD_DIM.height, latFraction);
  const lngZoom = getZoom(mapDimensions.width, WORLD_DIM.width, lngFraction);

  const zoom = Math.min(latZoom, lngZoom, ZOOM_MAX);

  const center = {
    lng: ((bounds.southwest.lng + bounds.northeast.lng) / 2).toFixed(4),
    lat: ((bounds.southwest.lat + bounds.northeast.lat) / 2).toFixed(4)
  };

  return {zoom: zoom, center: center};
};

export default boundsToMapLocation;
<commit_msg>Round zoom-level up to the next 1/4<commit_after>'use strict';

const boundsToMapLocation = (inputCoords) => {
  const WORLD_DIM = { height: 256, width: 256 };
  var ZOOM_MAX = 21;

  // height is 32rem in RRA css
  const mapHeight = 32 * Number(window.getComputedStyle(document.body)
                         .getPropertyValue('font-size').replace(/px/, ''));
  // width is container width minus lesser of 33.33% and 400px in iD css
  const containerWidth = document.querySelector('#app-container').offsetWidth;
  const mapSidebarWidth = containerWidth * 0.33 < 400 ? Math.round(containerWidth * 0.33) : 400;

  const mapDimensions = {
    width: containerWidth - mapSidebarWidth,
    height: mapHeight
  };

  const bounds = {
    southwest: {
      lng: inputCoords[0],
      lat: inputCoords[1]},
    northeast: {
      lng: inputCoords[2],
      lat: inputCoords[3]}
  };

  const latRad = (lat) => {
    const sin = Math.sin(lat * Math.PI / 180);
    const radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
    return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
  };

  const getZoom = (mapPx, worldPx, fraction) => {
    return Math.ceil((Math.log(mapPx / worldPx / fraction) / Math.LN2) / 0.25) * 0.25;
  };

  const ne = bounds.northeast;
  const sw = bounds.southwest;

  const latFraction = (latRad(ne.lat) - latRad(sw.lat)) / Math.PI;

  const lngDiff = ne.lng - sw.lng;
  const lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;

  const latZoom = getZoom(mapDimensions.height, WORLD_DIM.height, latFraction);
  const lngZoom = getZoom(mapDimensions.width, WORLD_DIM.width, lngFraction);

  const zoom = Math.min(latZoom, lngZoom, ZOOM_MAX);

  const center = {
    lng: ((bounds.southwest.lng + bounds.northeast.lng) / 2).toFixed(4),
    lat: ((bounds.southwest.lat + bounds.northeast.lat) / 2).toFixed(4)
  };

  return {zoom: zoom, center: center};
};

export default boundsToMapLocation;
<|endoftext|>


<commit_before>(function ($) {

    /*
     * Handle input. Call public functions and initializers
     */

    $.fn.cuentabancaria = function (data) {
        var _this = $(this);
        var plugin = _this.data('cuentabancaria');

        /*Inicializado ?*/
        if (!plugin) {

            plugin = new $.cuentabancaria(this, data);

            _this.data('cuentabancaria', plugin);

            return plugin;
            /*Si ya fue inizializado regresamos el plugin*/
        } else {
            return plugin;
        }

    };

    /*
     * Plugin Constructor
     */

    $.cuentabancaria = function (container, options) {

        var plugin = this;

        /*
         * Important Components
         */
        var $container = $(container);

        var settings;
        var $table;

        var defaults = {
        };

        /*
         * Private methods
         */

        /*
         * Public methods
         */

        plugin.init = function () {

            settings = plugin.settings = $.extend({}, defaults, options);

        }

        plugin.list = function () {

            var table = $container.find('#datatable');
            $.ajax({
                url: '/application/json/datatable/lang_es.json',
                dataType: 'json',
                success: function (data) {
                    table.dataTable({
                        "language": data,
                        "order": [],
                    });
                },
            });
            //ELIMINAR CUENTA BANCARIA
            $('.delete_modal').click(function () {
                var id = $(this).closest('tr').attr('id');
                var tmpl = [
                    // tabindex is required for focus
                    '
',
                    '
',
                    '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>',
                    '
ADVERTENCIA
',
                    '
',
                    '<form method="post" action="/catalogo/cuentabancaria/eliminar/' + id + '">',
                    '
',
                    '
¿Estas seguro que deseas eliminar el registro seleccionado?
',
                    '
',
                    '
',
                    'Cancelar',
                    '<button class="btn btn-danger" type="submit">Eliminar</button>',
                    '
',
                    '</form>',
                    '
'
                ].join('');
                $(tmpl).modal();
            });
        }

        plugin.new = function () {
            var validarcuenta = function () {
                var cuenta = $('input[name=cuentabancaria_nocuenta]').val();
                var banco = $('input[name=cuentabancaria_banco]').val();
                if (banco != '' && cuenta != '') {
                    var $this = $(this);
                    $this.removeClass('valid');
                    $('input[name=cuentabancaria_banco]').removeClass('valid');
                    $.ajax({
                        url: "/flujoefectivo/cuentabancaria/validarcuenta",
                        dataType: "json",
                        data: {cuenta: cuenta,banco: banco},
                        success: function (exist) {
                            if (exist) {
                                alert('La cuenta "' + cuenta + '" ya fue utilizada en el banco "' + banco + '"');
                                $('input[name=cuentabancaria_nocuenta]').val('');
                                $('input[name=cuentabancaria_banco]').val('');
                            } else {
                                $('input[name=cuentabancaria_nocuenta]').addClass('valid');
                                $('input[name=cuentabancaria_banco]').addClass('valid');
                            }

                        },
                    });

                }
            }
            $('input[name=cuentabancaria_nocuenta]').numeric();
            $('input[name=cuentabancaria_balance]').numeric();

            $('input[name=cuentabancaria_nocuenta]').on('blur', function () {
                validarcuenta();
            });

            $('input[name=cuentabancaria_banco]').on('blur', function () {
                validarcuenta();
            });
        }

        plugin.edit = function () {
            var validarcuenta = function () {
                var cuenta = $('input[name=cuentabancaria_nocuenta]').val();
                var banco = $('input[name=cuentabancaria_banco]').val();
                if (banco != '' && cuenta != '') {
                    var $this = $(this);
                    $this.removeClass('valid');
                    $('input[name=cuentabancaria_banco]').removeClass('valid');
                    $.ajax({
                        url: "/flujoefectivo/cuentabancaria/validarcuenta",
                        dataType: "json",
                        data: {cuenta: cuenta,banco: banco,edit: true,id: $('input[name=idcuentabancaria]').val()},
                        success: function (exist) {
                            console.log(exist);
                            if (exist) {
                                alert('La cuenta "' + cuenta + '" ya fue utilizada en el banco "' + banco + '"');
                                $('input[name=cuentabancaria_nocuenta]').val('');
                                $('input[name=cuentabancaria_banco]').val('');
                            } else {
                                $('input[name=cuentabancaria_nocuenta]').addClass('valid');
                                $('input[name=cuentabancaria_banco]').addClass('valid');
                            }

                        },
                    });

                }
            }
            $('input[name=cuentabancaria_nocuenta]').numeric();
            $('input[name=cuentabancaria_balance]').numeric();

            $('input[name=cuentabancaria_nocuenta]').on('blur', function () {
                validarcuenta();
            });

            $('input[name=cuentabancaria_banco]').on('blur', function () {
                validarcuenta();
            });
        }

        /*
         * Plugin initializing
         */

        plugin.init();

    }
})(jQuery);

<commit_msg>aersa<commit_after>(function ($) {

    /*
     * Handle input. Call public functions and initializers
     */

    $.fn.cuentabancaria = function (data) {
        var _this = $(this);
        var plugin = _this.data('cuentabancaria');

        /*Inicializado ?*/
        if (!plugin) {

            plugin = new $.cuentabancaria(this, data);

            _this.data('cuentabancaria', plugin);

            return plugin;
            /*Si ya fue inizializado regresamos el plugin*/
        } else {
            return plugin;
        }

    };

    /*
     * Plugin Constructor
     */

    $.cuentabancaria = function (container, options) {

        var plugin = this;

        /*
         * Important Components
         */
        var $container = $(container);

        var settings;
        var $table;

        var defaults = {
        };

        /*
         * Private methods
         */

        /*
         * Public methods
         */

        plugin.init = function () {

            settings = plugin.settings = $.extend({}, defaults, options);

        }

        plugin.list = function () {

            var table = $container.find('#datatable');
            $.ajax({
                url: '/application/json/datatable/lang_es.json',
                dataType: 'json',
                success: function (data) {
                    table.dataTable({
                        "language": data,
                        "order": [],
                    });
                },
            });
            //ELIMINAR CUENTA BANCARIA
            $('.delete_modal').click(function () {
                var id = $(this).closest('tr').attr('id');
                var tmpl = [
                    // tabindex is required for focus
                    '
',
                    '
',
                    '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>',
                    '
ADVERTENCIA
',
                    '
',
                    '<form method="post" action="/flujoefectivo/cuentabancaria/eliminar/' + id + '">',
                    '
',
                    '
¿Estas seguro que deseas eliminar el registro seleccionado?
',
                    '
',
                    '
',
                    'Cancelar',
                    '<button class="btn btn-danger" type="submit">Eliminar</button>',
                    '
',
                    '</form>',
                    '
'
                ].join('');
                $(tmpl).modal();
            });
        }

        plugin.new = function () {
            var validarcuenta = function () {
                var cuenta = $('input[name=cuentabancaria_nocuenta]').val();
                var banco = $('input[name=cuentabancaria_banco]').val();
                if (banco != '' && cuenta != '') {
                    var $this = $(this);
                    $this.removeClass('valid');
                    $('input[name=cuentabancaria_banco]').removeClass('valid');
                    $.ajax({
                        url: "/flujoefectivo/cuentabancaria/validarcuenta",
                        dataType: "json",
                        data: {cuenta: cuenta,banco: banco},
                        success: function (exist) {
                            if (exist) {
                                alert('La cuenta "' + cuenta + '" ya fue utilizada en el banco "' + banco + '"');
                                $('input[name=cuentabancaria_nocuenta]').val('');
                                $('input[name=cuentabancaria_banco]').val('');
                            } else {
                                $('input[name=cuentabancaria_nocuenta]').addClass('valid');
                                $('input[name=cuentabancaria_banco]').addClass('valid');
                            }

                        },
                    });

                }
            }
            $('input[name=cuentabancaria_nocuenta]').numeric();
            $('input[name=cuentabancaria_balance]').numeric();

            $('input[name=cuentabancaria_nocuenta]').on('blur', function () {
                validarcuenta();
            });

            $('input[name=cuentabancaria_banco]').on('blur', function () {
                validarcuenta();
            });
        }

        plugin.edit = function () {
            var validarcuenta = function () {
                var cuenta = $('input[name=cuentabancaria_nocuenta]').val();
                var banco = $('input[name=cuentabancaria_banco]').val();
                if (banco != '' && cuenta != '') {
                    var $this = $(this);
                    $this.removeClass('valid');
                    $('input[name=cuentabancaria_banco]').removeClass('valid');
                    $.ajax({
                        url: "/flujoefectivo/cuentabancaria/validarcuenta",
                        dataType: "json",
                        data: {cuenta: cuenta,banco: banco,edit: true,id: $('input[name=idcuentabancaria]').val()},
                        success: function (exist) {
                            console.log(exist);
                            if (exist) {
                                alert('La cuenta "' + cuenta + '" ya fue utilizada en el banco "' + banco + '"');
                                $('input[name=cuentabancaria_nocuenta]').val('');
                                $('input[name=cuentabancaria_banco]').val('');
                            } else {
                                $('input[name=cuentabancaria_nocuenta]').addClass('valid');
                                $('input[name=cuentabancaria_banco]').addClass('valid');
                            }

                        },
                    });

                }
            }
            $('input[name=cuentabancaria_nocuenta]').numeric();
            $('input[name=cuentabancaria_balance]').numeric();

            $('input[name=cuentabancaria_nocuenta]').on('blur', function () {
                validarcuenta();
            });

            $('input[name=cuentabancaria_banco]').on('blur', function () {
                validarcuenta();
            });
        }

        /*
         * Plugin initializing
         */

        plugin.init();

    }
})(jQuery);

<|endoftext|>


<commit_before>/**
 *
 * LanguageSelector
 *
 */

import React from 'react'
import PropTypes from 'prop-types'
import SelectField from 'material-ui/SelectField'
import MenuItem from 'material-ui/MenuItem'
import languages from 'data/languages'
import { injectIntl, intlShape } from 'react-intl'
import messages from './messages'

const LanguageSelector = ({
  onChange,
  intl,
  value,
  shortOption,
  showToolTip,
  labelColor
}) => {
  const { formatMessage } = intl
  const handleChange = (event, index, value) => {
    onChange(value)
  }

  return (
    <SelectField
      style={{ textAlign: 'left', width: '330px', maxWidth: '98%' }}
      maxHeight={400}
      value={value}
      labelStyle={labelColor ? { color: labelColor } : {}}
      onChange={handleChange}
      floatingLabelText={showToolTip && 'Choose your language'}
    >
      {languages.map((language) =>
        shortOption ? (
          <MenuItem
            key={language.code}
            value={language.code}
            primaryText={formatMessage(messages[language.code])}
          />
        ) : (
            <MenuItem
              key={language.code}
              value={language.code}
              primaryText={`${language.text} - ${formatMessage(
                messages[language.code]
              )}`}
            />
          )
      )}
    </SelectField>
  )
}

LanguageSelector.propTypes = {
  intl: intlShape.isRequired,
  onChange: PropTypes.func,
  value: PropTypes.string,
  shortOption: PropTypes.bool,
  showToolTip: PropTypes.bool,
  labelColor: PropTypes.string,
}

export default injectIntl(LanguageSelector)
<commit_msg>fix horizontal scroll<commit_after>/**
 *
 * LanguageSelector
 *
 */

import React from 'react'
import PropTypes from 'prop-types'
import SelectField from 'material-ui/SelectField'
import MenuItem from 'material-ui/MenuItem'
import languages from 'data/languages'
import { injectIntl, intlShape } from 'react-intl'
import messages from './messages'

const LanguageSelector = ({
  onChange,
  intl,
  value,
  shortOption,
  showToolTip,
  labelColor
}) => {
  const { formatMessage } = intl
  const handleChange = (event, index, value) => {
    onChange(value)
  }

  return (
    <SelectField
      style={{ textAlign: 'left', width: '370px' }}
      maxHeight={400}
      value={value}
      labelStyle={labelColor ? { color: labelColor } : {}}
      onChange={handleChange}
      floatingLabelText={showToolTip && 'Choose your language'}
    >
      {languages.map((language) =>
        shortOption ? (
          <MenuItem
            key={language.code}
            value={language.code}
            primaryText={formatMessage(messages[language.code])}
          />
        ) : (
            <MenuItem
              key={language.code}
              value={language.code}
              primaryText={`${language.text} - ${formatMessage(
                messages[language.code]
              )}`}
            />
          )
      )}
    </SelectField>
  )
}

LanguageSelector.propTypes = {
  intl: intlShape.isRequired,
  onChange: PropTypes.func,
  value: PropTypes.string,
  shortOption: PropTypes.bool,
  showToolTip: PropTypes.bool,
  labelColor: PropTypes.string,
}

export default injectIntl(LanguageSelector)
<|endoftext|>


<commit_before>import gulp from 'gulp';
import $ from './gulp-load-plugins';

export function copy (src, dest, taskName = 'missing task name') {
	$.fancyLog(`----> //** Copying files [${taskName}]`);
	return $.pump([
		gulp.src(src),
		gulp.dest(dest)
	]);
};

export function copyNewer (src, dest, taskName = 'missing task name', newerDest) {
	$.fancyLog(`----> //** Copying files [${taskName}]`);
	return $.pump([
		gulp.src(src),
		$.newer(newerDest),
		gulp.dest(dest)
	]);
};

export function copyNewerRename (src, dest, taskName = 'missing task name', newerDest, ifConditions, renameConfig) {
	$.fancyLog(`----> //** Copying files [${taskName}]`);
	return $.pump([
		gulp.src(src),
		$.newer(newerDest),
		$.if(ifConditions, $.rename(renameConfig)),
		gulp.dest(dest)
	]);
};<commit_msg>- remove unnecessary semicolon<commit_after>import gulp from 'gulp';
import $ from './gulp-load-plugins';

export function copy (src, dest, taskName = 'missing task name') {
	$.fancyLog(`----> //** Copying files [${taskName}]`);
	return $.pump([
		gulp.src(src),
		gulp.dest(dest)
	]);
}

export function copyNewer (src, dest, taskName = 'missing task name', newerDest) {
	$.fancyLog(`----> //** Copying files [${taskName}]`);
	return $.pump([
		gulp.src(src),
		$.newer(newerDest),
		gulp.dest(dest)
	]);
}

export function copyNewerRename (src, dest, taskName = 'missing task name', newerDest, ifConditions, renameConfig) {
	$.fancyLog(`----> //** Copying files [${taskName}]`);
	return $.pump([
		gulp.src(src),
		$.newer(newerDest),
		$.if(ifConditions, $.rename(renameConfig)),
		gulp.dest(dest)
	]);
}<|endoftext|>