<commit_before>var mapzen = mapzen || {};
mapzen.whosonfirst = mapzen.whosonfirst || {};

mapzen.whosonfirst.properties = (function(){

    var self = {

	'render': function(props){

	    var possible_wof = [
		'wof.belongsto',
		'wof.parent_id', 'wof.children',
		'wof.breaches',
		'wof.supersedes',
		'wof.superseded_by',
		// TO DO : please to write js.whosonfirst.placetypes...
		'wof.hierarchy.continent_id', 'wof.hierarchy.country_id', 'wof.hierarchy.region_id',
		'wof.hierarchy.county_id', 'wof.hierarchy.locality_id', 'wof.hierarchy.neighbourhood_id',
		'wof.hierarchy.campus_id', 'wof.hierarchy.venue_id'
	    ];

	    var text_callbacks = {
		'wof.id': mapzen.whosonfirst.yesnofix.render_code,
		'wof.placetype': self.render_placetype,
		'wof.concordances.gn:id': self.render_geonames_id,
		'wof.concordances.gp:id': self.render_woedb_id,
		'wof.concordances.woe:id': self.render_woedb_id,
		'wof.concordances.tgn:id': self.render_tgn_id,
		'wof.lastmodified': mapzen.whosonfirst.yesnofix.render_timestamp,
		'wof.megacity': self.render_megacity,
		'wof.tags': self.render_wof_tags,
		'wof.name': self.render_wof_name,
		'sg.city': self.render_simplegeo_city,
		'sg.postcode': self.render_simplegeo_postcode,
		'sg.tags': self.render_simplegeo_tags,
		'sg.classifier': self.render_simplegeo_classifiers,
	    };

	    var text_renderers = function(d, ctx){

		if ((possible_wof.indexOf(ctx) != -1) && (d > 0)){
		    return self.render_wof_id;
		}

		else if (ctx.match(/^name-/)){
		    return self.render_wof_name;
		}

		else if (ctx.match(/^sg-classifiers-/)){
		    return self.render_simplegeo_classifiers;
		}

		else if (text_callbacks[ctx]){
		    return text_callbacks[ctx];
		}

		else {
		    return null;
		}
	    };

	    var dict_mappings = {
		'wof.concordances.dbp:id': 'dbpedia',
		'wof.concordances.fb:id': 'freebase',
		'wof.concordances.fct:id': 'factual',
		'wof.concordances.gn:id': 'geonames',
		'wof.concordances.gp:id': 'geoplanet',
		'wof.concordances.loc:id': 'library of congress',
		'wof.concordances.nyt:id': 'new york times',
		'wof.concordances.wd:id': 'wikidata',
		// please build me on the fly using mz.wof.placetypes
		'wof.hierarchy.continent_id': 'continent',
		'wof.hierarchy.country_id': 'country',
		'wof.hierarchy.region_id': 'region',
		'wof.hierarchy.county_id': 'county',
		'wof.hierarchy.locality_id': 'locality',
		'wof.hierarchy.neighbourhood_id': 'neighbourhood',
	    };

	    var dict_renderers = function(d, ctx){

		// TO DO: something to match 3-letter language code + "_x_" + suffix
		// or more specifically something to match/ convert 3-letter language
		// codes wrapped up in a handy library (20160211/thisisaaronland)

		if (dict_mappings[ctx]){
		    return function(){
			return dict_mappings[ctx];
		    };
		}

		return null;
	    };

	    var text_exclusions = function(d, ctx){

		return function(){

		    if (ctx.match(/^geom/)){
			return true;
		    }

		    else if ((ctx.match(/^edtf/)) && (d == "uuuu")){
			return true;
		    }

		    else if (ctx == 'wof.lastmodified'){
			return true;
		    }

		    else {
			return false;
		    }
		};

	    };

	    mapzen.whosonfirst.yesnofix.set_submit_handler(self.submit_handler);

	    mapzen.whosonfirst.yesnofix.set_custom_renderers('text', text_renderers);
	    mapzen.whosonfirst.yesnofix.set_custom_renderers('dict', dict_renderers);

	    mapzen.whosonfirst.yesnofix.set_custom_exclusions('text', text_exclusions);

	    var pretty = mapzen.whosonfirst.yesnofix.render(props);
	    return pretty;
	},

	// TO DO : make 'mapzen.whosonfirst.spelunker.abs_root_url' something like
	// 'mapzen.whosonfirst.common.abs_root_url' or equivalent...

	'render_wof_id': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "id/" + encodeURIComponent(d) + "/";
	    var el = mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);

	    var text = el.children[0];
	    text.setAttribute("data-value", mapzen.whosonfirst.php.htmlspecialchars(d));
	    text.setAttribute("class", "props-uoc props-uoc-name props-uoc-name_" + mapzen.whosonfirst.php.htmlspecialchars(d));

	    return el;

	},

	'render_wof_placetype': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "placetypes/" + encodeURIComponent(d) + "/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_geonames_id': function(d, ctx){
	    var link = "http://geonames.org/" + encodeURIComponent(d) + "/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_woedb_id': function(d, ctx){
	    var link = "https://woe.spum.org/id/" + encodeURIComponent(d) + "/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_tgn_id': function(d, ctx){
	    var link = "http://vocab.getty.edu/tgn/" + encodeURIComponent(d);
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_megacity': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "megacities/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, "HOW BIG WOW MEGA SO CITY", ctx);
	},

	'render_wof_tags': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "tags/" + encodeURIComponent(d) + "/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_wof_name': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "search/?q=" + encodeURIComponent(d);
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_simplegeo_city': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "search/?q=" + encodeURIComponent(d) + "&placetype=locality";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_simplegeo_postcode': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "postalcodes/" + encodeURIComponent(d) + "/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_simplegeo_classifiers': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "categories/" + encodeURIComponent(d) + "/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_simplegeo_tags': function(d, ctx){
            var root = mapzen.whosonfirst.spelunker.abs_root_url();
            var link = root + "tags/" + encodeURIComponent(d) + "/";
            return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	// pending a final working soundbox installation
	// (20160405/thisisaaronland)

	'submit_handler': function(report){

	    var close_modal = function(){
		var about = document.getElementById("yesnofix-about");
		var parent = about.parentElement;
		parent.removeChild(about);
	    };

	    var on_submit = function(){

		close_modal();

		report = encodeURIComponent(report);
		var data = "data:text/plain;charset=UTF-8," + report;
		window.open(data, '_report');
	    };

	    var on_cancel = function(){
		close_modal();
	    };

	    // See this - we are purposefully re-using the CSS from the
	    // default about widget (20160405/thisisaaronland)

	    var about = document.createElement("div");
	    about.setAttribute("id", "yesnofix-about");

	    var text = document.createElement("div");
	    text.setAttribute("id", "yesnofix-about-text");

	    var head = document.createElement("h2");
	    head.appendChild(document.createTextNode("You have found an experimental feature!"));

	    var intro = document.createElement("div");

	    var p1_sentences = [
		"Thank you for taking the time to fact-check this data.",
		"There are two pieces to any data collection project: the reporting and the collecting.",
		"If you're reading this it means that only the reporting piece is live for Who's On First.",
		"We expect the collection piece to be live shortly but in the meantime you can generate a text version of your report.",
		"Soon you will be able to send it to Who's On First directly",
		"If you'd like to know more about this project all the details are available in this blog post:",
	    ];

	    var p1_text = p1_sentences.join(" ");

	    var p1 = document.createElement("p");
	    p1.appendChild(document.createTextNode(p1_text));

	    var href = "https://mapzen.com/blog/yesnofix/";

	    var link = document.createElement("a");
	    link.setAttribute("href", href);
	    link.setAttribute("target", "blog");
	    link.appendChild(document.createTextNode(href));

	    var p2 = document.createElement("p");
	    p2.appendChild(link);

	    intro.appendChild(p1);
	    intro.appendChild(p2);

	    text.appendChild(head);
	    text.appendChild(intro);

	    var close = document.createElement("div");
	    close.setAttribute("id", "yesnofix-about-submit");

	    var cancel_button = document.createElement("button");
	    cancel_button.setAttribute("id", "yesnofix-about-cancel-button");
	    cancel_button.appendChild(document.createTextNode("cancel"));

	    var submit_button = document.createElement("button");
	    submit_button.setAttribute("id", "yesnofix-about-submit-button");
	    submit_button.appendChild(document.createTextNode("submit"));

	    close.appendChild(cancel_button);
	    close.appendChild(submit_button);

	    about.appendChild(text);
	    about.appendChild(close);

	    cancel_button.onclick = on_cancel;
	    submit_button.onclick = on_submit;

	    var body = document.body;
	    body.insertBefore(about, body.firstChild);

	    return false;
	},

    };

    return self;

})();
<commit_msg>exclude wof.geomhash/id from yesnofix<commit_after>var mapzen = mapzen || {};
mapzen.whosonfirst = mapzen.whosonfirst || {};

mapzen.whosonfirst.properties = (function(){

    var self = {

	'render': function(props){

	    var possible_wof = [
		'wof.belongsto',
		'wof.parent_id', 'wof.children',
		'wof.breaches',
		'wof.supersedes',
		'wof.superseded_by',
		// TO DO : please to write js.whosonfirst.placetypes...
		'wof.hierarchy.continent_id', 'wof.hierarchy.country_id', 'wof.hierarchy.region_id',
		'wof.hierarchy.county_id', 'wof.hierarchy.locality_id', 'wof.hierarchy.neighbourhood_id',
		'wof.hierarchy.campus_id', 'wof.hierarchy.venue_id'
	    ];

	    var text_callbacks = {
		'wof.id': mapzen.whosonfirst.yesnofix.render_code,
		'wof.placetype': self.render_placetype,
		'wof.concordances.gn:id': self.render_geonames_id,
		'wof.concordances.gp:id': self.render_woedb_id,
		'wof.concordances.woe:id': self.render_woedb_id,
		'wof.concordances.tgn:id': self.render_tgn_id,
		'wof.lastmodified': mapzen.whosonfirst.yesnofix.render_timestamp,
		'wof.megacity': self.render_megacity,
		'wof.tags': self.render_wof_tags,
		'wof.name': self.render_wof_name,
		'sg.city': self.render_simplegeo_city,
		'sg.postcode': self.render_simplegeo_postcode,
		'sg.tags': self.render_simplegeo_tags,
		'sg.classifier': self.render_simplegeo_classifiers,
	    };

	    var text_renderers = function(d, ctx){

		if ((possible_wof.indexOf(ctx) != -1) && (d > 0)){
		    return self.render_wof_id;
		}

		else if (ctx.match(/^name-/)){
		    return self.render_wof_name;
		}

		else if (ctx.match(/^sg-classifiers-/)){
		    return self.render_simplegeo_classifiers;
		}

		else if (text_callbacks[ctx]){
		    return text_callbacks[ctx];
		}

		else {
		    return null;
		}
	    };

	    var dict_mappings = {
		'wof.concordances.dbp:id': 'dbpedia',
		'wof.concordances.fb:id': 'freebase',
		'wof.concordances.fct:id': 'factual',
		'wof.concordances.gn:id': 'geonames',
		'wof.concordances.gp:id': 'geoplanet',
		'wof.concordances.loc:id': 'library of congress',
		'wof.concordances.nyt:id': 'new york times',
		'wof.concordances.wd:id': 'wikidata',
		// please build me on the fly using mz.wof.placetypes
		'wof.hierarchy.continent_id': 'continent',
		'wof.hierarchy.country_id': 'country',
		'wof.hierarchy.region_id': 'region',
		'wof.hierarchy.county_id': 'county',
		'wof.hierarchy.locality_id': 'locality',
		'wof.hierarchy.neighbourhood_id': 'neighbourhood',
	    };

	    var dict_renderers = function(d, ctx){

		// TO DO: something to match 3-letter language code + "_x_" + suffix
		// or more specifically something to match/ convert 3-letter language
		// codes wrapped up in a handy library (20160211/thisisaaronland)

		if (dict_mappings[ctx]){
		    return function(){
			return dict_mappings[ctx];
		    };
		}

		return null;
	    };

	    var text_exclusions = function(d, ctx){

		return function(){

		    if (ctx.match(/^geom/)){
			return true;
		    }

		    else if ((ctx.match(/^edtf/)) && (d == "uuuu")){
			return true;
		    }

		    else if (ctx == 'wof.lastmodified'){
			return true;
		    }

		    else if (ctx == 'wof.geomhash'){
			return true;
		    }

		    else if (ctx == 'wof.id'){
			return true;
		    }

		    else {
			return false;
		    }
		};

	    };

	    mapzen.whosonfirst.yesnofix.set_submit_handler(self.submit_handler);

	    mapzen.whosonfirst.yesnofix.set_custom_renderers('text', text_renderers);
	    mapzen.whosonfirst.yesnofix.set_custom_renderers('dict', dict_renderers);

	    mapzen.whosonfirst.yesnofix.set_custom_exclusions('text', text_exclusions);

	    var pretty = mapzen.whosonfirst.yesnofix.render(props);
	    return pretty;
	},

	// TO DO : make 'mapzen.whosonfirst.spelunker.abs_root_url' something like
	// 'mapzen.whosonfirst.common.abs_root_url' or equivalent...

	'render_wof_id': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "id/" + encodeURIComponent(d) + "/";
	    var el = mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);

	    var text = el.children[0];
	    text.setAttribute("data-value", mapzen.whosonfirst.php.htmlspecialchars(d));
	    text.setAttribute("class", "props-uoc props-uoc-name props-uoc-name_" + mapzen.whosonfirst.php.htmlspecialchars(d));

	    return el;

	},

	'render_wof_placetype': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "placetypes/" + encodeURIComponent(d) + "/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_geonames_id': function(d, ctx){
	    var link = "http://geonames.org/" + encodeURIComponent(d) + "/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_woedb_id': function(d, ctx){
	    var link = "https://woe.spum.org/id/" + encodeURIComponent(d) + "/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_tgn_id': function(d, ctx){
	    var link = "http://vocab.getty.edu/tgn/" + encodeURIComponent(d);
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_megacity': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "megacities/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, "HOW BIG WOW MEGA SO CITY", ctx);
	},

	'render_wof_tags': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "tags/" + encodeURIComponent(d) + "/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_wof_name': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "search/?q=" + encodeURIComponent(d);
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_simplegeo_city': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "search/?q=" + encodeURIComponent(d) + "&placetype=locality";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_simplegeo_postcode': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "postalcodes/" + encodeURIComponent(d) + "/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_simplegeo_classifiers': function(d, ctx){
	    var root = mapzen.whosonfirst.spelunker.abs_root_url();
	    var link = root + "categories/" + encodeURIComponent(d) + "/";
	    return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	'render_simplegeo_tags': function(d, ctx){
            var root = mapzen.whosonfirst.spelunker.abs_root_url();
            var link = root + "tags/" + encodeURIComponent(d) + "/";
            return mapzen.whosonfirst.yesnofix.render_link(link, d, ctx);
	},

	// pending a final working soundbox installation
	// (20160405/thisisaaronland)

	'submit_handler': function(report){

	    var close_modal = function(){
		var about = document.getElementById("yesnofix-about");
		var parent = about.parentElement;
		parent.removeChild(about);
	    };

	    var on_submit = function(){

		close_modal();

		report = encodeURIComponent(report);
		var data = "data:text/plain;charset=UTF-8," + report;
		window.open(data, '_report');
	    };

	    var on_cancel = function(){
		close_modal();
	    };

	    // See this - we are purposefully re-using the CSS from the
	    // default about widget (20160405/thisisaaronland)

	    var about = document.createElement("div");
	    about.setAttribute("id", "yesnofix-about");

	    var text = document.createElement("div");
	    text.setAttribute("id", "yesnofix-about-text");

	    var head = document.createElement("h2");
	    head.appendChild(document.createTextNode("You have found an experimental feature!"));

	    var intro = document.createElement("div");

	    var p1_sentences = [
		"Thank you for taking the time to fact-check this data.",
		"There are two pieces to any data collection project: the reporting and the collecting.",
		"If you're reading this it means that only the reporting piece is live for Who's On First.",
		"We expect the collection piece to be live shortly but in the meantime you can generate a text version of your report.",
		"Soon you will be able to send it to Who's On First directly",
		"If you'd like to know more about this project all the details are available in this blog post:",
	    ];

	    var p1_text = p1_sentences.join(" ");

	    var p1 = document.createElement("p");
	    p1.appendChild(document.createTextNode(p1_text));

	    var href = "https://mapzen.com/blog/yesnofix/";

	    var link = document.createElement("a");
	    link.setAttribute("href", href);
	    link.setAttribute("target", "blog");
	    link.appendChild(document.createTextNode(href));

	    var p2 = document.createElement("p");
	    p2.appendChild(link);

	    intro.appendChild(p1);
	    intro.appendChild(p2);

	    text.appendChild(head);
	    text.appendChild(intro);

	    var close = document.createElement("div");
	    close.setAttribute("id", "yesnofix-about-submit");

	    var cancel_button = document.createElement("button");
	    cancel_button.setAttribute("id", "yesnofix-about-cancel-button");
	    cancel_button.appendChild(document.createTextNode("cancel"));

	    var submit_button = document.createElement("button");
	    submit_button.setAttribute("id", "yesnofix-about-submit-button");
	    submit_button.appendChild(document.createTextNode("submit"));

	    close.appendChild(cancel_button);
	    close.appendChild(submit_button);

	    about.appendChild(text);
	    about.appendChild(close);

	    cancel_button.onclick = on_cancel;
	    submit_button.onclick = on_submit;

	    var body = document.body;
	    body.insertBefore(about, body.firstChild);

	    return false;
	},

    };

    return self;

})();
<|endoftext|>


<commit_before>var keystone = require('keystone');
var async = require('async');

var Page = keystone.list('Page');
var Media = keystone.list('Media');

module.exports = function(done) {
	async.series([
		function(next) {
			new Page.model({"contentType":"Markdown","createdAt":"2015-10-25T22:54:41.000Z","published":true,"publishedAt":"2015-10-25T22:54:45.000Z","showInNavBar":false,"slug":"politique-de-confidentialite","sortOrder":6,"title":"Politique De Confidentialité","markdown":{"html":"
Politique de confidentialité
\n","md":"# Politique de confidentialité\r\n"}}).save(next);
		},
		function(next) { done(); }
	]);
};<commit_msg>Update the "politique de confidentialité" page.<commit_after>var keystone = require('keystone');
var async = require('async');

var Page = keystone.list('Page');
var Media = keystone.list('Media');

module.exports = function(done) {
	async.series([
		function(next) {
			new Page.model({"contentType":"Markdown","createdAt":"2015-10-25T22:54:41.000Z","published":true,"publishedAt":"2015-10-25T22:54:45.000Z","showInNavBar":false,"slug":"politique-de-confidentialite","sortOrder":6,"title":"Politique De Confidentialité","markdown":{"html":"
Politique de confidentialité
\n
Qu'est-ce que FranceConnect ?
\n
FranceConnect est un service du Secrétariat Général pour la Modernisation de l'Action Publique (SGMAP), placé sous l’autorité du Premier ministre et piloté par la DISIC.
\n
FranceConnect permet de vous authentifier sur des services tiers - comme Cocorico - en utilisant vos identifiants des différentes administrations françaises (impôts, sécurité sociale...) sans jamais les divulguer. Ainsi, FranceConnect fourni une sorte de passeport/carte d'identité numérique qui vous est unique.
\n
Grâce à FranceConnect, Cocorico guarantit que chaque utilisateur est un citoyen français et que chaque vote/contribution est donc fiable.
\n
Quelles sont les données personnelles récupérées par Cocorico ?
\n
Si vous ne vous connectez pas, aucune donnée personnelle n'est récupérée.
\n
Lorsque vous vous connectez à Cocorico, nous récupérons certaines informations fournies par votre "carte d'identité numérique" FranceConnect :
\n
\n
un identifiant unique,
\n
vos nom et prénom,
\n
votre sexe,
\n
votre date de naissance.
\n
\n
Comment sont utilisées ces données personnelles ? Quelles données sont enregistrées ?
\n
Vos nom et prénom sont utilisés pour personnaliser l'interface graphique de Cocorico : ils ne sont jamais enregistrés.
\n
Votre date de naissance et votre sexe sont enregistrés uniquement lorsque vous contribuez pour restituer des statistiques de participation anonymes (ex: 32% des hommes de 18-24 ans ont voté "pour"). Pour plus d'anonymat, votre date de naissance n'est jamais enregistrée telle quelle : à la place nous enregistrons votre âge au moment de votre contribution.
\n
Votre identifiant unique est utilisé pour savoir quelle contribution vous appartient mais il n'est jamais enregistré pour que vos contributions restent anonymes.
\n
Si vous ne contribuez pas, aucune de ces données n'est enregistrée. Même si vous vous connectez.
\n
Comment mon anonymat est-il garantit ?
\n
Tout d'abord vos nom, prénom et date de naissance ne sont jamais enregistrés.
\n
Seul votre identifiant unique est utilisé pour identifier vos contributions. Mais nous ne stockons jamais votre identifiant unique. En effet, même si ce dernier ne permet pas de vous identifier directement, il pourrait être utilisé pour créer des profils et désanonymiser les données de Cocorico. Votre identifiant est donc chiffré avant d'être enregistré.
\n
Nous utilisons une technique de chiffrement dite "asymétrique" : lorsque vous contribuez, votre contribution est identifiée par une clef unique chiffrée générée à partir de votre identifiant unique et de valeurs aléatoires. Le chiffrage asymétrique signifie que :
\n
\n
à partir de votre identifiant, on peut savoir si une contribution donnée vous est attribuée,
\n
mais pour une contribution donnée, il est impossible de savoir quel identifiant et donc quel utilisateur en est l'auteur.
\n
\n
Cet identifiant unique est renvoyé par FranceConnect lorsque vous vous connectez : il n'est jamais stocké sur nos serveurs  mais seulement dans un "cookie" dans votre navigateur Web.
\n
Il est donc impossible de savoir à qui appartient une contribution : vous êtes la seule personne capable de consulter le contenu vous concernant, et ce uniquement lorsque vous êtes authentifié sur le site.
\n
Où sont stockées les données de Cocorico ?
\n
Elles sont stockées sur le territoire français, chez un hébergeur français soumis au droit français.
\n
Comment ces données sont-elles sécurisées ?
\n
Aucune donnée personnelle nominative n'est enregistrée sur nos serveurs. Si notre base de donnée venait à être "dérobée", elle ne fournirait donc rien d'autre que des statistiques anonymes.
\n
De plus, ces données sont ouvertes et il n'y a donc rien à voler.
\n
Enfin, tout le code informatique de Cocorico est ouvert et libre : tous les développeurs sont donc en mesure d'en perfectionner le fonctionnement et la sécurité.
\n
Qui développe Cocorico ?
\n
Cocorico est un projet communautaire : tout le code est ouvert et libre, et tout le monde peut contribuer.
\n","md":"# Politique de confidentialité\r\n\r\n## Qu'est-ce que FranceConnect ?\r\n\r\nFranceConnect est un service du Secrétariat Général pour la Modernisation de l'Action Publique (SGMAP), placé sous l’autorité du Premier ministre et piloté par la DISIC.\r\n\r\nFranceConnect permet de vous authentifier sur des services tiers - comme Cocorico - en utilisant vos identifiants des différentes administrations françaises (impôts, sécurité sociale...) sans jamais les divulguer. Ainsi, FranceConnect fourni une sorte de passeport/carte d'identité numérique qui vous est unique.\r\n\r\nGrâce à FranceConnect, Cocorico guarantit que chaque utilisateur est un citoyen français et que chaque vote/contribution est donc fiable.\r\n\r\n## Quelles sont les données personnelles récupérées par Cocorico ?\r\n\r\nSi vous ne vous connectez pas, aucune donnée personnelle n'est récupérée.\r\n\r\nLorsque vous vous connectez à Cocorico, nous récupérons certaines informations fournies par votre \"carte d'identité numérique\" FranceConnect :\r\n- un identifiant unique,\r\n- vos nom et prénom,\r\n- votre sexe,\r\n- votre date de naissance.\r\n\r\n## Comment sont utilisées ces données personnelles ? Quelles données sont enregistrées ?\r\n\r\nVos nom et prénom sont utilisés pour personnaliser l'interface graphique de Cocorico : ils ne sont **_jamais_** enregistrés.\r\n\r\nVotre date de naissance et votre sexe sont enregistrés uniquement lorsque vous contribuez pour restituer des statistiques de participation _anonymes_ (ex: 32% des hommes de 18-24 ans ont voté \"pour\"). Pour plus d'anonymat, votre date de naissance n'est jamais enregistrée telle quelle : à la place nous enregistrons votre âge au moment de votre contribution.\r\n\r\nVotre identifiant unique est utilisé pour savoir quelle contribution vous appartient mais il n'est jamais enregistré pour que vos contributions restent anonymes.\r\n\r\nSi vous ne contribuez pas, aucune de ces données n'est enregistrée. Même si vous vous connectez.\r\n\r\n## Comment mon anonymat est-il garantit ?\r\n\r\nTout d'abord vos nom, prénom et date de naissance ne sont _**jamais**_ enregistrés.\r\n\r\nSeul votre identifiant unique est utilisé pour identifier vos contributions. Mais nous ne stockons _**jamais**_ votre identifiant unique. En effet, même si ce dernier ne permet pas de vous identifier directement, il pourrait être utilisé pour créer des profils et désanonymiser les données de Cocorico. Votre identifiant est donc chiffré avant d'être enregistré.\r\n\r\nNous utilisons une technique de chiffrement dite \"asymétrique\" : lorsque vous contribuez, votre contribution est identifiée par une clef unique chiffrée générée à partir de votre identifiant unique et de valeurs aléatoires. Le chiffrage asymétrique signifie que :\r\n- à partir de votre identifiant, on peut savoir si une contribution donnée vous est attribuée,\r\n- mais pour une contribution donnée, il est impossible de savoir quel identifiant et donc quel utilisateur en est l'auteur.\r\n\r\nCet identifiant unique est renvoyé par FranceConnect lorsque vous vous connectez : il n'est jamais stocké sur nos serveurs  mais seulement dans un \"cookie\" dans votre navigateur Web.\r\n\r\n_Il est donc impossible de savoir à qui appartient une contribution_ : vous êtes la seule personne capable de consulter le contenu vous concernant, et ce uniquement lorsque vous êtes authentifié sur le site.\r\n\r\n## Où sont stockées les données de Cocorico ?\r\n\r\nElles sont stockées sur le territoire français, chez un hébergeur français soumis au droit français.\r\n\r\n## Comment ces données sont-elles sécurisées ?\r\n\r\nAucune donnée personnelle nominative n'est enregistrée sur nos serveurs. Si notre base de donnée venait à être \"dérobée\", elle ne fournirait donc rien d'autre que des statistiques anonymes.\r\n\r\nDe plus, ces données sont ouvertes et il n'y a donc rien à voler.\r\n\r\nEnfin, tout le code informatique de Cocorico est ouvert et libre : tous les développeurs sont donc en mesure d'en perfectionner le fonctionnement et la sécurité.\r\n\r\n## Qui développe Cocorico ?\r\n\r\nCocorico est un projet communautaire : tout le code est ouvert et libre, et tout le monde peut contribuer."}}).save(next);
		},
		function(next) { done(); }
	]);
};<|endoftext|>


<commit_before>import { connect } from 'react-redux'
import { denormalizeArticles } from '../utils/denormalize-articles'
import AuthorCollection from '../components/author-page/author-collection'
import AuthorData from '../components/author-page/author-data'
import Helmet from 'react-helmet'
import loggerFactory from '../logger'
import PropTypes from 'prop-types'
import React from 'react'
import Sponsor from '../components/Sponsor'
import siteMeta from '../constants/site-meta'
// @twreporter
import { replaceGCSUrlOrigin } from '@twreporter/core/lib/utils/storage-url-processor'
import twreporterRedux from '@twreporter/redux'
// lodash
import get from 'lodash/get'

const {
  fetchAuthorCollectionIfNeeded,
  fetchAuthorDetails,
} = twreporterRedux.actions

const _ = {
  get,
}

const authorDefaultImg = {
  url: '/asset/author-default-img.svg',
  width: 500,
  height: 500,
}

const logger = loggerFactory.getLogger()

class Author extends React.Component {
  static propTypes = {
    author: PropTypes.object,
    collections: PropTypes.arrayOf(PropTypes.object),
    collectionMeta: PropTypes.shape({
      hasMore: PropTypes.bool.isRequired,
      isFetching: PropTypes.bool.isRequired,
      currentPage: PropTypes.number.isRequired,
      totalResults: PropTypes.number,
    }),
    authorIsFull: PropTypes.bool,
    fetchAuthorCollectionIfNeeded: PropTypes.func,
    fetchAuthorDetails: PropTypes.func,
  }

  static defaultProps = {
    totalResults: 0,
    author: {},
    collections: [],
  }

  componentDidMount() {
    const authorId = _.get(this.props, 'match.params.authorId')
    if (authorId) {
      this.fetchAuthorCollectionIfNeededWithCatch(authorId)
      const { authorIsFull } = this.props
      if (!authorIsFull) {
        return this.fetchAuthorDetailsWithCatch(authorId)
      }
    }
  }

  fetchAuthorCollectionIfNeededWithCatch = authorId => {
    return (
      this.props
        .fetchAuthorCollectionIfNeeded(authorId)
        // TODO render alter message
        .catch(failAction => {
          logger.errorReport({
            report: _.get(failAction, 'payload.error'),
            message: `Error to fetch the posts of the author (id: '${authorId}').`,
          })
        })
    )
  }

  fetchAuthorDetailsWithCatch = authorId => {
    return (
      this.props
        .fetchAuthorDetails(authorId)
        // TODO render alter message
        .catch(failAction => {
          logger.errorReport({
            report: _.get(failAction, 'payload.error'),
            message: `Error to fetch description of the author (id: '${authorId}').`,
          })
        })
    )
  }

  handleLoadmore = () => {
    const authorId = _.get(this.props, 'author.id')
    return this.fetchAuthorCollectionIfNeededWithCatch(authorId)
  }

  render() {
    const { author, collections, collectionMeta } = this.props
    const fullTitle = author.name + siteMeta.name.separator + siteMeta.name.full
    const canonical = `${siteMeta.urlOrigin}/authors/${author.id}`
    const pureTextBio = author.bio ? author.bio.replace(/<[^>]*>?/gm, '') : '' // pure text only
    return (
      <React.Fragment>
        <Helmet
          title={fullTitle}
          link={[{ rel: 'canonical', href: canonical }]}
          meta={[
            { name: 'description', content: pureTextBio },
            { name: 'twitter:title', content: fullTitle },
            { name: 'twitter:description', content: pureTextBio },
            { name: 'twitter:image', content: siteMeta.ogImage.url },
            { name: 'twitter:card', content: 'summary' },
            { property: 'og:title', content: fullTitle },
            { property: 'og:description', content: pureTextBio },
            { property: 'og:image', content: siteMeta.ogImage.url },
            { property: 'og:image:width', content: siteMeta.ogImage.width },
            { property: 'og:image:height', content: siteMeta.ogImage.height },
            { property: 'og:type', content: 'profile' },
            { property: 'og:url', content: canonical },
            { property: 'og:rich_attachment', content: 'true' },
          ]}
        />
        <AuthorData authorData={author} />
        <AuthorCollection
          collections={collections}
          currentId={author.id}
          hasMore={collectionMeta.hasMore}
          isFetching={collectionMeta.isFetching}
          currentPage={collectionMeta.currentPage}
          handleLoadmore={this.handleLoadmore}
          totalResults={collectionMeta.totalResults}
        />
        <Sponsor />
      </React.Fragment>
    )
  }
}

function mapStateToProps(state, ownProps) {
  const authorId = _.get(ownProps, 'match.params.authorId')
  const articlesByAuthor = _.get(state, 'articlesByAuthor', {})
  const entities = _.get(state, 'entitiesForAuthors', {})
  const authorIsFull = _.get(entities, [authorId, 'full'], false)
  const {
    hasMore,
    isFetching,
    currentPage,
    collectIndexList,
    totalResults,
  } = _.get(articlesByAuthor, authorId, {})
  const collections = denormalizeArticles(collectIndexList, entities)
  const authorEntity = _.get(entities, ['authors', authorId], {})
  const authorImageSouce = _.get(
    authorEntity,
    'thumbnail.resizedTargets.mobile'
  )
  const authorImage = authorImageSouce
    ? { ...authorImageSouce, url: replaceGCSUrlOrigin(authorImageSouce.url) }
    : authorDefaultImg
  const author = {
    id: authorId,
    name: _.get(authorEntity, 'name') || '',
    title: _.get(authorEntity, 'jobTitle') || '',
    image: authorImage,
    mail: _.get(authorEntity, 'email') || '',
    bio: _.get(authorEntity, 'bio.md' || ''),
  }
  return {
    author,
    authorIsFull,
    collections,
    collectionMeta: {
      hasMore,
      isFetching,
      currentPage,
      totalResults,
    },
  }
}

export default connect(
  mapStateToProps,
  { fetchAuthorCollectionIfNeeded, fetchAuthorDetails }
)(Author)
<commit_msg>refactor: tweak path to `bio` for author detail v2 endpoint<commit_after>import { connect } from 'react-redux'
import { denormalizeArticles } from '../utils/denormalize-articles'
import AuthorCollection from '../components/author-page/author-collection'
import AuthorData from '../components/author-page/author-data'
import Helmet from 'react-helmet'
import loggerFactory from '../logger'
import PropTypes from 'prop-types'
import React from 'react'
import Sponsor from '../components/Sponsor'
import siteMeta from '../constants/site-meta'
// @twreporter
import { replaceGCSUrlOrigin } from '@twreporter/core/lib/utils/storage-url-processor'
import twreporterRedux from '@twreporter/redux'
// lodash
import get from 'lodash/get'

const {
  fetchAuthorCollectionIfNeeded,
  fetchAuthorDetails,
} = twreporterRedux.actions

const _ = {
  get,
}

const authorDefaultImg = {
  url: '/asset/author-default-img.svg',
  width: 500,
  height: 500,
}

const logger = loggerFactory.getLogger()

class Author extends React.Component {
  static propTypes = {
    author: PropTypes.object,
    collections: PropTypes.arrayOf(PropTypes.object),
    collectionMeta: PropTypes.shape({
      hasMore: PropTypes.bool.isRequired,
      isFetching: PropTypes.bool.isRequired,
      currentPage: PropTypes.number.isRequired,
      totalResults: PropTypes.number,
    }),
    authorIsFull: PropTypes.bool,
    fetchAuthorCollectionIfNeeded: PropTypes.func,
    fetchAuthorDetails: PropTypes.func,
  }

  static defaultProps = {
    totalResults: 0,
    author: {},
    collections: [],
  }

  componentDidMount() {
    const authorId = _.get(this.props, 'match.params.authorId')
    if (authorId) {
      this.fetchAuthorCollectionIfNeededWithCatch(authorId)
      const { authorIsFull } = this.props
      if (!authorIsFull) {
        return this.fetchAuthorDetailsWithCatch(authorId)
      }
    }
  }

  fetchAuthorCollectionIfNeededWithCatch = authorId => {
    return (
      this.props
        .fetchAuthorCollectionIfNeeded(authorId)
        // TODO render alter message
        .catch(failAction => {
          logger.errorReport({
            report: _.get(failAction, 'payload.error'),
            message: `Error to fetch the posts of the author (id: '${authorId}').`,
          })
        })
    )
  }

  fetchAuthorDetailsWithCatch = authorId => {
    return (
      this.props
        .fetchAuthorDetails(authorId)
        // TODO render alter message
        .catch(failAction => {
          logger.errorReport({
            report: _.get(failAction, 'payload.error'),
            message: `Error to fetch description of the author (id: '${authorId}').`,
          })
        })
    )
  }

  handleLoadmore = () => {
    const authorId = _.get(this.props, 'author.id')
    return this.fetchAuthorCollectionIfNeededWithCatch(authorId)
  }

  render() {
    const { author, collections, collectionMeta } = this.props
    const fullTitle = author.name + siteMeta.name.separator + siteMeta.name.full
    const canonical = `${siteMeta.urlOrigin}/authors/${author.id}`
    const pureTextBio = author.bio ? author.bio.replace(/<[^>]*>?/gm, '') : '' // pure text only
    return (
      <React.Fragment>
        <Helmet
          title={fullTitle}
          link={[{ rel: 'canonical', href: canonical }]}
          meta={[
            { name: 'description', content: pureTextBio },
            { name: 'twitter:title', content: fullTitle },
            { name: 'twitter:description', content: pureTextBio },
            { name: 'twitter:image', content: siteMeta.ogImage.url },
            { name: 'twitter:card', content: 'summary' },
            { property: 'og:title', content: fullTitle },
            { property: 'og:description', content: pureTextBio },
            { property: 'og:image', content: siteMeta.ogImage.url },
            { property: 'og:image:width', content: siteMeta.ogImage.width },
            { property: 'og:image:height', content: siteMeta.ogImage.height },
            { property: 'og:type', content: 'profile' },
            { property: 'og:url', content: canonical },
            { property: 'og:rich_attachment', content: 'true' },
          ]}
        />
        <AuthorData authorData={author} />
        <AuthorCollection
          collections={collections}
          currentId={author.id}
          hasMore={collectionMeta.hasMore}
          isFetching={collectionMeta.isFetching}
          currentPage={collectionMeta.currentPage}
          handleLoadmore={this.handleLoadmore}
          totalResults={collectionMeta.totalResults}
        />
        <Sponsor />
      </React.Fragment>
    )
  }
}

function mapStateToProps(state, ownProps) {
  const authorId = _.get(ownProps, 'match.params.authorId')
  const articlesByAuthor = _.get(state, 'articlesByAuthor', {})
  const entities = _.get(state, 'entitiesForAuthors', {})
  const authorIsFull = _.get(entities, [authorId, 'full'], false)
  const {
    hasMore,
    isFetching,
    currentPage,
    collectIndexList,
    totalResults,
  } = _.get(articlesByAuthor, authorId, {})
  const collections = denormalizeArticles(collectIndexList, entities)
  const authorEntity = _.get(entities, ['authors', authorId], {})
  const authorImageSouce = _.get(
    authorEntity,
    'thumbnail.resizedTargets.mobile'
  )
  const authorImage = authorImageSouce
    ? { ...authorImageSouce, url: replaceGCSUrlOrigin(authorImageSouce.url) }
    : authorDefaultImg
  const author = {
    id: authorId,
    name: _.get(authorEntity, 'name') || '',
    title: _.get(authorEntity, 'jobTitle') || '',
    image: authorImage,
    mail: _.get(authorEntity, 'email') || '',
    bio: _.get(authorEntity, 'bio' || ''),
  }
  return {
    author,
    authorIsFull,
    collections,
    collectionMeta: {
      hasMore,
      isFetching,
      currentPage,
      totalResults,
    },
  }
}

export default connect(
  mapStateToProps,
  { fetchAuthorCollectionIfNeeded, fetchAuthorDetails }
)(Author)
<|endoftext|>


<commit_before>import UtilsNew from "./utilsNew.js";

export class Notification {

    static parent = null;
    static iconsMap = {
        danger: "glyphicon glyphicon-remove-sign",
        info: "glyphicon glyphicon-info-sign",
        success: "glyphicon glyphicon-ok-sign",
        warning: "glyphicon glyphicon-warning-sign",
    };

    // Get notifications parent element
    static getParent() {
        if (!Notification.parent) {
            Notification.parent = document.createElement("div");
            Object.assign(Notification.parent.style, {
                "left": "50%",
                "maxWidth": "600px",
                "position": "fixed",
                "top": "8px",
                "transform": "translateX(-50%)",
                "width": "100%",
                "zIndex": "9999",
            });
            // Append notification to document
            document.body.appendChild(Notification.parent);
        }

        // Return parent element
        return Notification.parent;
    }

    // Display a notification alert
    static show(config) {
        const type = (config.type || "info").toLowerCase();
        const parent = Notification.getParent();

        // Generate notification element
        const element = UtilsNew.renderHTML(`

                ${config.showIcon ? `

                ` : ""}

${config.message || ""}

                    ${config.buttons && config.buttons?.length > 0 ? `

                            ${config.buttons.map((button, index) => `
                                <button data-index="${index}" class="btn btn-${type}">
                                    ${button.text || ""}
                                </button>
                            `).join("")}

                    ` : ""}

                ${config?.showCloseButton ? `

                        <button type="button" class="close" aria-label="Close">
                            ×
                        </button>

                ` : ""}

        `).querySelector("div.alert");

        // Method to remove the notification
        const removeNotification = () => {
            parent.contains(element) && parent.removeChild(element);
        };

        // Register buttons actions
        Array.from(element.querySelectorAll("button.btn")).forEach(buttonElement => {
            const index = parseInt(buttonElement.dataset.index);

            buttonElement.addEventListener("click", () => {
                return config.buttons[index].onClick({
                    hide: removeNotification,
                });
            });
        });

        // Append notification
        parent.appendChild(element);

        if (config.removeAfter > 0) {
            // Register event to remove the notification when the close button is clicked
            element.querySelector("button.close").addEventListener("click", () => {
                return removeNotification();
            });

            // Register the timer to automatically remove the notification after the specified ms
            UtilsNew.sleep(config.removeAfter).then(() => removeNotification());
        }
    }

    // Alias to create a success notification
    static success(message, customDelay) {
        return Notification.show({
            type: "success",
            showIcon: true,
            showCloseButton: true,
            removeAfter: customDelay || 5000,
            message,
        });
    }

    // Alias to create an info notification
    static info(message, customDelay) {
        return Notification.show({
            type: "info",
            showIcon: true,
            showCloseButton: true,
            removeAfter: customDelay || 5000,
            message,
        });
    }

    // Alias to create a warning notification
    static warning(message, customDelay) {
        return Notification.show({
            type: "warning",
            showIcon: true,
            showCloseButton: true,
            removeAfter: customDelay || 9000,
            message,
        });
    }

    // Alias to create an error notification
    static error(message) {
        return Notification.show({
            type: "danger",
            showIcon: true,
            removeAfter: 0,
            buttons: [
                {
                    text: "Close",
                    onClick: actions => actions.hide(),
                },
            ],
            message,
        });
    }

}
<commit_msg>core: fix export of notification manager (#248)<commit_after>import UtilsNew from "./utilsNew.js";

export default class Notification {

    static parent = null;
    static iconsMap = {
        danger: "glyphicon glyphicon-remove-sign",
        info: "glyphicon glyphicon-info-sign",
        success: "glyphicon glyphicon-ok-sign",
        warning: "glyphicon glyphicon-warning-sign",
    };

    // Get notifications parent element
    static getParent() {
        if (!Notification.parent) {
            Notification.parent = document.createElement("div");
            Object.assign(Notification.parent.style, {
                "left": "50%",
                "maxWidth": "600px",
                "position": "fixed",
                "top": "8px",
                "transform": "translateX(-50%)",
                "width": "100%",
                "zIndex": "9999",
            });
            // Append notification to document
            document.body.appendChild(Notification.parent);
        }

        // Return parent element
        return Notification.parent;
    }

    // Display a notification alert
    static show(config) {
        const type = (config.type || "info").toLowerCase();
        const parent = Notification.getParent();

        // Generate notification element
        const element = UtilsNew.renderHTML(`

                ${config.showIcon ? `

                ` : ""}

${config.message || ""}

                    ${config.buttons && config.buttons?.length > 0 ? `

                            ${config.buttons.map((button, index) => `
                                <button data-index="${index}" class="btn btn-${type}">
                                    ${button.text || ""}
                                </button>
                            `).join("")}

                    ` : ""}

                ${config?.showCloseButton ? `

                        <button type="button" class="close" aria-label="Close">
                            ×
                        </button>

                ` : ""}

        `).querySelector("div.alert");

        // Method to remove the notification
        const removeNotification = () => {
            parent.contains(element) && parent.removeChild(element);
        };

        // Register buttons actions
        Array.from(element.querySelectorAll("button.btn")).forEach(buttonElement => {
            const index = parseInt(buttonElement.dataset.index);

            buttonElement.addEventListener("click", () => {
                return config.buttons[index].onClick({
                    hide: removeNotification,
                });
            });
        });

        // Append notification
        parent.appendChild(element);

        if (config.removeAfter > 0) {
            // Register event to remove the notification when the close button is clicked
            element.querySelector("button.close").addEventListener("click", () => {
                return removeNotification();
            });

            // Register the timer to automatically remove the notification after the specified ms
            UtilsNew.sleep(config.removeAfter).then(() => removeNotification());
        }
    }

    // Alias to create a success notification
    static success(message, customDelay) {
        return Notification.show({
            type: "success",
            showIcon: true,
            showCloseButton: true,
            removeAfter: customDelay || 5000,
            message,
        });
    }

    // Alias to create an info notification
    static info(message, customDelay) {
        return Notification.show({
            type: "info",
            showIcon: true,
            showCloseButton: true,
            removeAfter: customDelay || 5000,
            message,
        });
    }

    // Alias to create a warning notification
    static warning(message, customDelay) {
        return Notification.show({
            type: "warning",
            showIcon: true,
            showCloseButton: true,
            removeAfter: customDelay || 9000,
            message,
        });
    }

    // Alias to create an error notification
    static error(message) {
        return Notification.show({
            type: "danger",
            showIcon: true,
            removeAfter: 0,
            buttons: [
                {
                    text: "Close",
                    onClick: actions => actions.hide(),
                },
            ],
            message,
        });
    }

}
<|endoftext|>


<commit_before>import {
  INIT_AUTH,
  SIGN_IN_SUCCESS,
  SIGN_OUT_SUCCESS
} from './action-types';

function authenticate(provider) {
  return (dispatch, getState) => {
    const { firebase } = getState();

    firebase.authWithOAuthPopup(provider, (error, authData) => {
      if (error) {
        console.error('ERROR @ authWithOAuthPopup :', error); // eslint-disable-line no-console
      }
      else {
        dispatch({
          type: SIGN_IN_SUCCESS,
          authData
        });
      }
    });
  };
}

export function initAuth() {
  return (dispatch, getState) => {
    const { firebase } = getState();
    dispatch({
      type: INIT_AUTH,
      authData: firebase.getAuth()
    });
  };
}

export function signInWithGithub() {
  return dispatch => dispatch(authenticate('github'));
}

export function signInWithGoogle() {
  return dispatch => dispatch(authenticate('google'));
}

export function signInWithTwitter() {
  return dispatch => dispatch(authenticate('twitter'));
}

export function signOut() {
  return (dispatch, getState) => {
    const { firebase } = getState();
    firebase.unauth();
    dispatch({
      type: SIGN_OUT_SUCCESS
    });
  };
}
<commit_msg>fix(authActions): don't wrap calls to `authenticate()` with `dispatch()`<commit_after>import {
  INIT_AUTH,
  SIGN_IN_SUCCESS,
  SIGN_OUT_SUCCESS
} from './action-types';

function authenticate(provider) {
  return (dispatch, getState) => {
    const { firebase } = getState();

    firebase.authWithOAuthPopup(provider, (error, authData) => {
      if (error) {
        console.error('ERROR @ authWithOAuthPopup :', error); // eslint-disable-line no-console
      }
      else {
        dispatch({
          type: SIGN_IN_SUCCESS,
          authData
        });
      }
    });
  };
}

export function initAuth() {
  return (dispatch, getState) => {
    const { firebase } = getState();
    dispatch({
      type: INIT_AUTH,
      authData: firebase.getAuth()
    });
  };
}

export function signInWithGithub() {
  return authenticate('github');
}

export function signInWithGoogle() {
  return authenticate('google');
}

export function signInWithTwitter() {
  return authenticate('twitter');
}

export function signOut() {
  return (dispatch, getState) => {
    const { firebase } = getState();
    firebase.unauth();
    dispatch({
      type: SIGN_OUT_SUCCESS
    });
  };
}
<|endoftext|>


<commit_before>/**
# T5.Map
*/
var Map = function(container, params) {
    // initialise defaults
    params = _extend({
        controls: ['zoombar', 'copyright'],

        // zoom parameters
        minZoom: 1,
        maxZoom: 18,
        renderer: 'canvas/dom',
        zoom: 1,

        zoombar: {}
    }, params);

    /* internals */

    var lastBoundsChangeOffset = new GeoXY(),
        rpp,
        zoomLevel = params.zoom || params.zoomLevel,
        residualScaleFactor = 0,
        zoomTimeout = 0;

    function checkScaling(evt, scaleFactor) {
        // calculate the scale factor exponent
        var scaleFactorExp = log(scaleFactor) / Math.LN2 | 0;

        // _log('scale factor = ' + scaleFactor + ', exp = ' + scaleFactorExp);
        if (scaleFactorExp !== 0) {
            // scaleFactor = pow(2, scaleFactorExp);
            residualScaleFactor = scaleFactor - pow(2, scaleFactorExp);

            clearTimeout(zoomTimeout);
            zoomTimeout = setTimeout(function() {
                zoom(zoomLevel + scaleFactorExp);
            }, 500);
        } // ifg
    } // checkScaling

    function handleRefresh(evt) {
        var viewport = _self.viewport();

        // check the offset has changed (refreshes can happen for other reasons)
        if (lastBoundsChangeOffset.x != viewport.x || lastBoundsChangeOffset.y != viewport.y) {
            // trigger the event
            _self.trigger('boundsChange', bounds());

            // update the last bounds change offset
            lastBoundsChangeOffset.x = viewport.x;
            lastBoundsChangeOffset.y = viewport.y;
        } // if
    } // handleRefresh

    /* exports */

    /**
    ### bounds(newBounds)
    */
    function bounds(newBounds, maxZoomLevel) {
        var viewport = _self.viewport();

        if (newBounds) {
            // calculate the zoom level we are going to
            var zoomLevel = max(newBounds.bestZoomLevel(viewport.w, viewport.h) - 1, maxZoomLevel || 0);

            // move the map
            return zoom(zoomLevel).center(newBounds.center());
        }
        else {
            return new GeoJS.BBox(
                new GeoXY(viewport.x, viewport.y2).sync(_self, true).pos(),
                new GeoXY(viewport.x2, viewport.y).sync(_self, true).pos()
            );
        } // if..else
    } // bounds

    /**
    ### zoom(int): int
    Either update or simply return the current zoomlevel.
    */
    function zoom(value, zoomX, zoomY) {
        if (_is(value, typeNumber)) {
            value = max(params.minZoom, min(params.maxZoom, value | 0));
            if (value !== zoomLevel) {
                var viewport = _self.viewport(),
                    offset = _self.offset(),
                    halfWidth = viewport.w / 2,
                    halfHeight = viewport.h / 2,
                    scaling = pow(2, value - zoomLevel),
                    scaledHalfWidth = halfWidth / scaling | 0,
                    scaledHalfHeight = halfHeight / scaling | 0;

                // update the zoom level
                zoomLevel = value;

                // update the offset
                _self.offset(
                    ((zoomX || offset.x + halfWidth) - scaledHalfWidth) * scaling,
                    ((zoomY || offset.y + halfHeight) - scaledHalfHeight) * scaling
                );

                // trigger the change
                _self.trigger('zoom', value);
                _self.trigger('reset');

                // update the rads per pixel to reflect the zoom level change
                rpp = _self.rpp = radsPerPixel(zoomLevel);

                // calculate the grid size
                _self.setMaxOffset(TWO_PI / rpp | 0, TWO_PI / rpp | 0, true, false);

                // reset the scale factor
                _self.scale(1 + residualScaleFactor, false, true);
                residualScaleFactor = 0;

                // reset scaling and resync the map
                _self.trigger('resync');

                // refresh the display
                _self.refresh();
            } // if

            // return the view so we can chain
            return _self;
        }
        else {
            return zoomLevel;
        } // if..else
    } // zoom

    var _self = _extend(new View(container, params), {
        XY: GeoXY,

        bounds: bounds,
        zoom: zoom
    });

    // initialise the default rpp
    rpp = _self.rpp = radsPerPixel(zoomLevel);

    // bind events
    _self.bind('refresh', handleRefresh);
    _self.bind('scaleChanged', checkScaling);

    return _self;
};<commit_msg>Tweaked map zoom method to return current zoom level<commit_after>/**
# T5.Map
*/
var Map = function(container, params) {
    // initialise defaults
    params = _extend({
        controls: ['zoombar', 'copyright'],

        // zoom parameters
        minZoom: 1,
        maxZoom: 18,
        renderer: 'canvas/dom',
        zoom: 1,

        zoombar: {}
    }, params);

    /* internals */

    var lastBoundsChangeOffset = new GeoXY(),
        rpp,
        zoomLevel = params.zoom || params.zoomLevel,
        residualScaleFactor = 0,
        zoomTimeout = 0;

    function checkScaling(evt, scaleFactor) {
        // calculate the scale factor exponent
        var scaleFactorExp = log(scaleFactor) / Math.LN2 | 0;

        // _log('scale factor = ' + scaleFactor + ', exp = ' + scaleFactorExp);
        if (scaleFactorExp !== 0) {
            // scaleFactor = pow(2, scaleFactorExp);
            residualScaleFactor = scaleFactor - pow(2, scaleFactorExp);

            clearTimeout(zoomTimeout);
            zoomTimeout = setTimeout(function() {
                zoom(zoomLevel + scaleFactorExp);
            }, 500);
        } // ifg
    } // checkScaling

    function handleRefresh(evt) {
        var viewport = _self.viewport();

        // check the offset has changed (refreshes can happen for other reasons)
        if (lastBoundsChangeOffset.x != viewport.x || lastBoundsChangeOffset.y != viewport.y) {
            // trigger the event
            _self.trigger('boundsChange', bounds());

            // update the last bounds change offset
            lastBoundsChangeOffset.x = viewport.x;
            lastBoundsChangeOffset.y = viewport.y;
        } // if
    } // handleRefresh

    /* exports */

    /**
    ### bounds(newBounds)
    */
    function bounds(newBounds, maxZoomLevel) {
        var viewport = _self.viewport();

        if (newBounds) {
            // calculate the zoom level we are going to
            var zoomLevel = max(newBounds.bestZoomLevel(viewport.w, viewport.h) - 1, maxZoomLevel || 0);

            // move the map
            return zoom(zoomLevel).center(newBounds.center());
        }
        else {
            return new GeoJS.BBox(
                new GeoXY(viewport.x, viewport.y2).sync(_self, true).pos(),
                new GeoXY(viewport.x2, viewport.y).sync(_self, true).pos()
            );
        } // if..else
    } // bounds

    /**
    ### zoom(int): int
    Either update or simply return the current zoomlevel.
    */
    function zoom(value, zoomX, zoomY) {
        if (_is(value, typeNumber)) {
            value = max(params.minZoom, min(params.maxZoom, value | 0));
            if (value !== zoomLevel) {
                var viewport = _self.viewport(),
                    offset = _self.offset(),
                    halfWidth = viewport.w / 2,
                    halfHeight = viewport.h / 2,
                    scaling = pow(2, value - zoomLevel),
                    scaledHalfWidth = halfWidth / scaling | 0,
                    scaledHalfHeight = halfHeight / scaling | 0;

                // update the zoom level
                zoomLevel = value;

                // update the offset
                _self.offset(
                    ((zoomX || offset.x + halfWidth) - scaledHalfWidth) * scaling,
                    ((zoomY || offset.y + halfHeight) - scaledHalfHeight) * scaling
                );

                // trigger the change
                _self.trigger('zoom', value);
                _self.trigger('reset');

                // update the rads per pixel to reflect the zoom level change
                rpp = _self.rpp = radsPerPixel(zoomLevel);

                // calculate the grid size
                _self.setMaxOffset(TWO_PI / rpp | 0, TWO_PI / rpp | 0, true, false);

                // reset the scale factor
                _self.scale(1 + residualScaleFactor, false, true);
                residualScaleFactor = 0;

                // reset scaling and resync the map
                _self.trigger('resync');

                // refresh the display
                _self.refresh();
            } // if

            // return the view so we can chain
            return _self;
        }
        else {
            return zoomLevel;
        } // if..else
    } // zoom

    var _self = _extend(new View(container, params), {
        XY: GeoXY,

        bounds: bounds,
        zoom: zoom
    });

    // initialise the default rpp
    rpp = _self.rpp = radsPerPixel(zoomLevel);

    // bind events
    _self.bind('refresh', handleRefresh);
    _self.bind('scaleChanged', checkScaling);

    return _self;
};<|endoftext|>


<commit_before>'use strict';

var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
$interpolateMinErr.throwNoconcat = function(text) {
  throw $interpolateMinErr('noconcat',
      "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
      "interpolations that concatenate multiple expressions when a trusted value is " +
      "required.  See http://docs.angularjs.org/api/ng.$sce", text);
};

$interpolateMinErr.interr = function(text, err) {
  return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
};

/**
 * @ngdoc provider
 * @name $interpolateProvider
 *
 * @description
 *
 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
 *
 * @example
<example module="customInterpolationApp">
<file name="index.html">

    //demo.label//

</file>
<file name="protractor.js" type="protractor">
  it('should interpolate binding with custom symbols', function() {
    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
  });
</file>
</example>
 */
function $InterpolateProvider() {
  var startSymbol = '{{';
  var endSymbol = '}}';

  /**
   * @ngdoc method
   * @name $interpolateProvider#startSymbol
   * @description
   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
   *
   * @param {string=} value new value to set the starting symbol to.
   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
   */
  this.startSymbol = function(value) {
    if (value) {
      startSymbol = value;
      return this;
    } else {
      return startSymbol;
    }
  };

  /**
   * @ngdoc method
   * @name $interpolateProvider#endSymbol
   * @description
   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
   *
   * @param {string=} value new value to set the ending symbol to.
   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
   */
  this.endSymbol = function(value) {
    if (value) {
      endSymbol = value;
      return this;
    } else {
      return endSymbol;
    }
  };

  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
    var startSymbolLength = startSymbol.length,
        endSymbolLength = endSymbol.length,
        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');

    function escape(ch) {
      return '\\\\\\' + ch;
    }

    function unescapeText(text) {
      return text.replace(escapedStartRegexp, startSymbol).
        replace(escapedEndRegexp, endSymbol);
    }

    function stringify(value) {
      if (value == null) { // null || undefined
        return '';
      }
      switch (typeof value) {
        case 'string':
          break;
        case 'number':
          value = '' + value;
          break;
        default:
          value = toJson(value);
      }

      return value;
    }

    /**
     * @ngdoc service
     * @name $interpolate
     * @kind function
     *
     * @requires $parse
     * @requires $sce
     *
     * @description
     *
     * Compiles a string with markup into an interpolation function. This service is used by the
     * HTML {@link ng.$compile $compile} service for data binding. See
     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
     * interpolation markup.
     *
     *
     * ```js
     *   var $interpolate = ...; // injected
     *   var exp = $interpolate('Hello {{name | uppercase}}!');
     *   expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
     * ```
     *
     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
     * `true`, the interpolation function will return `undefined` unless all embedded expressions
     * evaluate to a value other than `undefined`.
     *
     * ```js
     *   var $interpolate = ...; // injected
     *   var context = {greeting: 'Hello', name: undefined };
     *
     *   // default "forgiving" mode
     *   var exp = $interpolate('{{greeting}} {{name}}!');
     *   expect(exp(context)).toEqual('Hello !');
     *
     *   // "allOrNothing" mode
     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
     *   expect(exp(context)).toBeUndefined();
     *   context.name = 'Angular';
     *   expect(exp(context)).toEqual('Hello Angular!');
     * ```
     *
     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
     *
     * ####Escaped Interpolation
     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
     * or binding.
     *
     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
     * degree, while also enabling code examples to work without relying on the
     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
     *
     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
     * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
     * interpolation start/end markers with their escaped counterparts.**
     *
     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
     * output when the $interpolate service processes the text. So, for HTML elements interpolated
     * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
     * this is typically useful only when user-data is used in rendering a template from the server, or
     * when otherwise untrusted data is used by a directive.
     *
     * <example>
     *  <file name="index.html">
     *

     *
{{apptitle}}: \{\{ username = "defaced value"; \}\}
     *

     *
{{username}} attempts to inject code which will deface the
     *        application, but fails to accomplish their task, because the server has correctly
     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
     *        characters.

     *
Instead, the result of the attempted script injection is visible, and can be removed
     *        from the database by an administrator.

     *

     *  </file>
     * </example>
     *
     * @param {string} text The text with markup to interpolate.
     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
     *    embedded expression in order to return an interpolation function. Strings with no
     *    embedded expression will return null for the interpolation function.
     * @param {string=} trustedContext when provided, the returned function passes the interpolated
     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
     *    provides Strict Contextual Escaping for details.
     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
     *    unless all embedded expressions evaluate to a value other than `undefined`.
     * @returns {function(context)} an interpolation function which is used to compute the
     *    interpolated string. The function has these parameters:
     *
     * - `context`: evaluation context for all expressions embedded in the interpolated text
     */
    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
      allOrNothing = !!allOrNothing;
      var startIndex,
          endIndex,
          index = 0,
          expressions = [],
          parseFns = [],
          textLength = text.length,
          exp,
          concat = [],
          expressionPositions = [];

      while (index < textLength) {
        if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
          if (index !== startIndex) {
            concat.push(unescapeText(text.substring(index, startIndex)));
          }
          exp = text.substring(startIndex + startSymbolLength, endIndex);
          expressions.push(exp);
          parseFns.push($parse(exp, parseStringifyInterceptor));
          index = endIndex + endSymbolLength;
          expressionPositions.push(concat.length);
          concat.push('');
        } else {
          // we did not find an interpolation, so we have to add the remainder to the separators array
          if (index !== textLength) {
            concat.push(unescapeText(text.substring(index)));
          }
          break;
        }
      }

      // Concatenating expressions makes it hard to reason about whether some combination of
      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
      // single expression be used for iframe[src], object[src], etc., we ensure that the value
      // that's used is assigned or constructed by some JS code somewhere that is more testable or
      // make it obvious that you bound the value to some user controlled value.  This helps reduce
      // the load when auditing for XSS issues.
      if (trustedContext && concat.length > 1) {
          $interpolateMinErr.throwNoconcat(text);
      }

      if (!mustHaveExpression || expressions.length) {
        var compute = function(values) {
          for (var i = 0, ii = expressions.length; i < ii; i++) {
            if (allOrNothing && isUndefined(values[i])) return;
            concat[expressionPositions[i]] = values[i];
          }
          return concat.join('');
        };

        var getValue = function(value) {
          return trustedContext ?
            $sce.getTrusted(trustedContext, value) :
            $sce.valueOf(value);
        };

        return extend(function interpolationFn(context) {
            var i = 0;
            var ii = expressions.length;
            var values = new Array(ii);

            try {
              for (; i < ii; i++) {
                values[i] = parseFns[i](context);
              }

              return compute(values);
            } catch (err) {
              $exceptionHandler($interpolateMinErr.interr(text, err));
            }

          }, {
          // all of these properties are undocumented for now
          exp: text, //just for compatibility with regular watchers created via $watch
          expressions: expressions,
          $$watchDelegate: function(scope, listener) {
            var lastValue;
            return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
              var currValue = compute(values);
              if (isFunction(listener)) {
                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
              }
              lastValue = currValue;
            });
          }
        });
      }

      function parseStringifyInterceptor(value) {
        try {
          value = getValue(value);
          return allOrNothing && !isDefined(value) ? value : stringify(value);
        } catch (err) {
          $exceptionHandler($interpolateMinErr.interr(text, err));
        }
      }
    }

    /**
     * @ngdoc method
     * @name $interpolate#startSymbol
     * @description
     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
     *
     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
     * the symbol.
     *
     * @returns {string} start symbol.
     */
    $interpolate.startSymbol = function() {
      return startSymbol;
    };

    /**
     * @ngdoc method
     * @name $interpolate#endSymbol
     * @description
     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
     *
     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
     * the symbol.
     *
     * @returns {string} end symbol.
     */
    $interpolate.endSymbol = function() {
      return endSymbol;
    };

    return $interpolate;
  }];
}

<commit_msg>docs($interpolate): add missing bracket in first example<commit_after>'use strict';

var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
$interpolateMinErr.throwNoconcat = function(text) {
  throw $interpolateMinErr('noconcat',
      "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
      "interpolations that concatenate multiple expressions when a trusted value is " +
      "required.  See http://docs.angularjs.org/api/ng.$sce", text);
};

$interpolateMinErr.interr = function(text, err) {
  return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
};

/**
 * @ngdoc provider
 * @name $interpolateProvider
 *
 * @description
 *
 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
 *
 * @example
<example module="customInterpolationApp">
<file name="index.html">

    //demo.label//

</file>
<file name="protractor.js" type="protractor">
  it('should interpolate binding with custom symbols', function() {
    expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
  });
</file>
</example>
 */
function $InterpolateProvider() {
  var startSymbol = '{{';
  var endSymbol = '}}';

  /**
   * @ngdoc method
   * @name $interpolateProvider#startSymbol
   * @description
   * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
   *
   * @param {string=} value new value to set the starting symbol to.
   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
   */
  this.startSymbol = function(value) {
    if (value) {
      startSymbol = value;
      return this;
    } else {
      return startSymbol;
    }
  };

  /**
   * @ngdoc method
   * @name $interpolateProvider#endSymbol
   * @description
   * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
   *
   * @param {string=} value new value to set the ending symbol to.
   * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
   */
  this.endSymbol = function(value) {
    if (value) {
      endSymbol = value;
      return this;
    } else {
      return endSymbol;
    }
  };

  this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
    var startSymbolLength = startSymbol.length,
        endSymbolLength = endSymbol.length,
        escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
        escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');

    function escape(ch) {
      return '\\\\\\' + ch;
    }

    function unescapeText(text) {
      return text.replace(escapedStartRegexp, startSymbol).
        replace(escapedEndRegexp, endSymbol);
    }

    function stringify(value) {
      if (value == null) { // null || undefined
        return '';
      }
      switch (typeof value) {
        case 'string':
          break;
        case 'number':
          value = '' + value;
          break;
        default:
          value = toJson(value);
      }

      return value;
    }

    /**
     * @ngdoc service
     * @name $interpolate
     * @kind function
     *
     * @requires $parse
     * @requires $sce
     *
     * @description
     *
     * Compiles a string with markup into an interpolation function. This service is used by the
     * HTML {@link ng.$compile $compile} service for data binding. See
     * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
     * interpolation markup.
     *
     *
     * ```js
     *   var $interpolate = ...; // injected
     *   var exp = $interpolate('Hello {{name | uppercase}}!');
     *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
     * ```
     *
     * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
     * `true`, the interpolation function will return `undefined` unless all embedded expressions
     * evaluate to a value other than `undefined`.
     *
     * ```js
     *   var $interpolate = ...; // injected
     *   var context = {greeting: 'Hello', name: undefined };
     *
     *   // default "forgiving" mode
     *   var exp = $interpolate('{{greeting}} {{name}}!');
     *   expect(exp(context)).toEqual('Hello !');
     *
     *   // "allOrNothing" mode
     *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
     *   expect(exp(context)).toBeUndefined();
     *   context.name = 'Angular';
     *   expect(exp(context)).toEqual('Hello Angular!');
     * ```
     *
     * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
     *
     * ####Escaped Interpolation
     * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
     * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
     * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
     * or binding.
     *
     * This enables web-servers to prevent script injection attacks and defacing attacks, to some
     * degree, while also enabling code examples to work without relying on the
     * {@link ng.directive:ngNonBindable ngNonBindable} directive.
     *
     * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
     * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
     * interpolation start/end markers with their escaped counterparts.**
     *
     * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
     * output when the $interpolate service processes the text. So, for HTML elements interpolated
     * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
     * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
     * this is typically useful only when user-data is used in rendering a template from the server, or
     * when otherwise untrusted data is used by a directive.
     *
     * <example>
     *  <file name="index.html">
     *

     *
{{apptitle}}: \{\{ username = "defaced value"; \}\}
     *

     *
{{username}} attempts to inject code which will deface the
     *        application, but fails to accomplish their task, because the server has correctly
     *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
     *        characters.

     *
Instead, the result of the attempted script injection is visible, and can be removed
     *        from the database by an administrator.

     *

     *  </file>
     * </example>
     *
     * @param {string} text The text with markup to interpolate.
     * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
     *    embedded expression in order to return an interpolation function. Strings with no
     *    embedded expression will return null for the interpolation function.
     * @param {string=} trustedContext when provided, the returned function passes the interpolated
     *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
     *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
     *    provides Strict Contextual Escaping for details.
     * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
     *    unless all embedded expressions evaluate to a value other than `undefined`.
     * @returns {function(context)} an interpolation function which is used to compute the
     *    interpolated string. The function has these parameters:
     *
     * - `context`: evaluation context for all expressions embedded in the interpolated text
     */
    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
      allOrNothing = !!allOrNothing;
      var startIndex,
          endIndex,
          index = 0,
          expressions = [],
          parseFns = [],
          textLength = text.length,
          exp,
          concat = [],
          expressionPositions = [];

      while (index < textLength) {
        if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
             ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
          if (index !== startIndex) {
            concat.push(unescapeText(text.substring(index, startIndex)));
          }
          exp = text.substring(startIndex + startSymbolLength, endIndex);
          expressions.push(exp);
          parseFns.push($parse(exp, parseStringifyInterceptor));
          index = endIndex + endSymbolLength;
          expressionPositions.push(concat.length);
          concat.push('');
        } else {
          // we did not find an interpolation, so we have to add the remainder to the separators array
          if (index !== textLength) {
            concat.push(unescapeText(text.substring(index)));
          }
          break;
        }
      }

      // Concatenating expressions makes it hard to reason about whether some combination of
      // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
      // single expression be used for iframe[src], object[src], etc., we ensure that the value
      // that's used is assigned or constructed by some JS code somewhere that is more testable or
      // make it obvious that you bound the value to some user controlled value.  This helps reduce
      // the load when auditing for XSS issues.
      if (trustedContext && concat.length > 1) {
          $interpolateMinErr.throwNoconcat(text);
      }

      if (!mustHaveExpression || expressions.length) {
        var compute = function(values) {
          for (var i = 0, ii = expressions.length; i < ii; i++) {
            if (allOrNothing && isUndefined(values[i])) return;
            concat[expressionPositions[i]] = values[i];
          }
          return concat.join('');
        };

        var getValue = function(value) {
          return trustedContext ?
            $sce.getTrusted(trustedContext, value) :
            $sce.valueOf(value);
        };

        return extend(function interpolationFn(context) {
            var i = 0;
            var ii = expressions.length;
            var values = new Array(ii);

            try {
              for (; i < ii; i++) {
                values[i] = parseFns[i](context);
              }

              return compute(values);
            } catch (err) {
              $exceptionHandler($interpolateMinErr.interr(text, err));
            }

          }, {
          // all of these properties are undocumented for now
          exp: text, //just for compatibility with regular watchers created via $watch
          expressions: expressions,
          $$watchDelegate: function(scope, listener) {
            var lastValue;
            return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
              var currValue = compute(values);
              if (isFunction(listener)) {
                listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
              }
              lastValue = currValue;
            });
          }
        });
      }

      function parseStringifyInterceptor(value) {
        try {
          value = getValue(value);
          return allOrNothing && !isDefined(value) ? value : stringify(value);
        } catch (err) {
          $exceptionHandler($interpolateMinErr.interr(text, err));
        }
      }
    }

    /**
     * @ngdoc method
     * @name $interpolate#startSymbol
     * @description
     * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
     *
     * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
     * the symbol.
     *
     * @returns {string} start symbol.
     */
    $interpolate.startSymbol = function() {
      return startSymbol;
    };

    /**
     * @ngdoc method
     * @name $interpolate#endSymbol
     * @description
     * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
     *
     * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
     * the symbol.
     *
     * @returns {string} end symbol.
     */
    $interpolate.endSymbol = function() {
      return endSymbol;
    };

    return $interpolate;
  }];
}

<|endoftext|>


<commit_before>/**
 * @author mikael emtinger / http://gomo.se/
 */

THREE.Sprite = function ( parameters ) {

	THREE.Object3D.call( this );

	this.color = ( parameters.color !== undefined ) ? new THREE.Color( parameters.color ) : new THREE.Color( 0xffffff );
	this.map = ( parameters.map instanceof THREE.Texture ) ? parameters.map : THREE.ImageUtils.loadTexture( parameters.map );
	this.blending = ( parameters.blending !== undefined ) ? parameters.blending : THREE.NormalBlending;

	this.useScreenCoordinates = ( parameters.useScreenCoordinates !== undefined ) ? parameters.useScreenCoordinates : true;
	this.mergeWith3D = ( parameters.mergeWith3D !== undefined ) ? parameters.mergeWith3D : !this.useScreenCoordinates;
	this.affectedByDistance = ( parameters.affectedByDistance !== undefined ) ? parameters.affectedByDistance : !this.useScreenCoordinates;
	this.scaleByViewport = ( parameters.scaleByViewport !== undefined ) ? parameters.scaleByViewport : !this.affectedByDistance;
	this.alignment = ( parameters.alignment instanceof THREE.Vector2 ) ? parameters.alignment : THREE.SpriteAlignment.center;

	this.rotation3d = this.rotation;
	this.rotation = 0;
	this.opacity = 1;

	this.uvOffset = new THREE.Vector2( 0, 0 );
	this.uvScale  = new THREE.Vector2( 1, 1 );

};

THREE.Sprite.prototype = new THREE.Object3D();
THREE.Sprite.prototype.constructor = THREE.Sprite;

/*
 * Custom update matrix
 */

THREE.Sprite.prototype.updateMatrix = function () {

	this.matrix.setPosition( this.position );

	this.rotation3d.set( 0, 0, this.rotation );
	this.matrix.setRotationFromEuler( this.rotation3d );

	if ( this.scale.x !== 1 || this.scale.y !== 1 ) {

		this.matrix.scale( this.scale );
		this.boundRadiusScale = Math.max( this.scale.x, this.scale.y );

	}

	this.matrixWorldNeedsUpdate = true;

};

/*
 * Alignment
 */

THREE.SpriteAlignment = {};
THREE.SpriteAlignment.topLeft = new THREE.Vector2( 1, -1 );
THREE.SpriteAlignment.topCenter = new THREE.Vector2( 0, -1 );
THREE.SpriteAlignment.topRight = new THREE.Vector2( -1, -1 );
THREE.SpriteAlignment.centerLeft = new THREE.Vector2( 1, 0 );
THREE.SpriteAlignment.center = new THREE.Vector2( 0, 0 );
THREE.SpriteAlignment.centerRight = new THREE.Vector2( -1, 0 );
THREE.SpriteAlignment.bottomLeft = new THREE.Vector2( 1, 1 );
THREE.SpriteAlignment.bottomCenter = new THREE.Vector2( 0, 1 );
THREE.SpriteAlignment.bottomRight = new THREE.Vector2( -1, 1 );
<commit_msg>Sprite now supports only Texture object as parameters.map.<commit_after>/**
 * @author mikael emtinger / http://gomo.se/
 */

THREE.Sprite = function ( parameters ) {

	THREE.Object3D.call( this );

	this.color = ( parameters.color !== undefined ) ? new THREE.Color( parameters.color ) : new THREE.Color( 0xffffff );
	this.map = ( parameters.map !== undefined ) ? parameters.map : new THREE.Texture();
	this.blending = ( parameters.blending !== undefined ) ? parameters.blending : THREE.NormalBlending;

	this.useScreenCoordinates = ( parameters.useScreenCoordinates !== undefined ) ? parameters.useScreenCoordinates : true;
	this.mergeWith3D = ( parameters.mergeWith3D !== undefined ) ? parameters.mergeWith3D : !this.useScreenCoordinates;
	this.affectedByDistance = ( parameters.affectedByDistance !== undefined ) ? parameters.affectedByDistance : !this.useScreenCoordinates;
	this.scaleByViewport = ( parameters.scaleByViewport !== undefined ) ? parameters.scaleByViewport : !this.affectedByDistance;
	this.alignment = ( parameters.alignment instanceof THREE.Vector2 ) ? parameters.alignment : THREE.SpriteAlignment.center;

	this.rotation3d = this.rotation;
	this.rotation = 0;
	this.opacity = 1;

	this.uvOffset = new THREE.Vector2( 0, 0 );
	this.uvScale  = new THREE.Vector2( 1, 1 );

};

THREE.Sprite.prototype = new THREE.Object3D();
THREE.Sprite.prototype.constructor = THREE.Sprite;

/*
 * Custom update matrix
 */

THREE.Sprite.prototype.updateMatrix = function () {

	this.matrix.setPosition( this.position );

	this.rotation3d.set( 0, 0, this.rotation );
	this.matrix.setRotationFromEuler( this.rotation3d );

	if ( this.scale.x !== 1 || this.scale.y !== 1 ) {

		this.matrix.scale( this.scale );
		this.boundRadiusScale = Math.max( this.scale.x, this.scale.y );

	}

	this.matrixWorldNeedsUpdate = true;

};

/*
 * Alignment
 */

THREE.SpriteAlignment = {};
THREE.SpriteAlignment.topLeft = new THREE.Vector2( 1, -1 );
THREE.SpriteAlignment.topCenter = new THREE.Vector2( 0, -1 );
THREE.SpriteAlignment.topRight = new THREE.Vector2( -1, -1 );
THREE.SpriteAlignment.centerLeft = new THREE.Vector2( 1, 0 );
THREE.SpriteAlignment.center = new THREE.Vector2( 0, 0 );
THREE.SpriteAlignment.centerRight = new THREE.Vector2( -1, 0 );
THREE.SpriteAlignment.bottomLeft = new THREE.Vector2( 1, 1 );
THREE.SpriteAlignment.bottomCenter = new THREE.Vector2( 0, 1 );
THREE.SpriteAlignment.bottomRight = new THREE.Vector2( -1, 1 );
<|endoftext|>


<commit_before>/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Sprite = ( function () {

	var indices = new Uint16Array( [ 0, 1, 2,  0, 2, 3 ] );
	var vertices = new Float32Array( [ - 0.5, - 0.5, 0,   0.5, - 0.5, 0,   0.5, 0.5, 0,   - 0.5, 0.5, 0 ] );
	var uvs = new Float32Array( [ 0, 0,   1, 0,   1, 1,   0, 1 ] );

	var geometry = new THREE.BufferGeometry();
	geometry.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) );
	geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
	geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );

	return function ( material ) {

		THREE.Object3D.call( this );

		this.type = 'Sprite';

		this.geometry = geometry;
		this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial();

	};

} )();

THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype );
THREE.Sprite.prototype.constructor = THREE.Sprite;

THREE.Sprite.prototype.raycast = ( function () {

	var matrixPosition = new THREE.Vector3();

	return function ( raycaster, intersects ) {

		matrixPosition.setFromMatrixPosition( this.matrixWorld );

		var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition );

		if ( distanceSq > this.scale.x * this.scale.x ) {

			return;

		}

		intersects.push( {

			distance: Math.sqrt( distanceSq ),
			point: this.position,
			face: null,
			object: this

		} );

	};

}() );

THREE.Sprite.prototype.clone = function ( object ) {

	if ( object === undefined ) object = new THREE.Sprite( this.material );

	THREE.Object3D.prototype.clone.call( this, object );

	return object;

};

THREE.Sprite.prototype.toJSON = function ( meta ) {

	var data = THREE.Object3D.prototype.toJSON.call( this, meta );

	// only serialize if not in meta materials cache
	if ( meta.materials[ this.material.uuid ] === undefined ) {
		meta.materials[ this.material.uuid ] = this.material.toJSON();
	}

	data.object.material = this.material.uuid;

	return data;

};

// Backwards compatibility

THREE.Particle = THREE.Sprite;
<commit_msg>Sprite intersect: Use diagonal to approximate size<commit_after>/**
 * @author mikael emtinger / http://gomo.se/
 * @author alteredq / http://alteredqualia.com/
 */

THREE.Sprite = ( function () {

	var indices = new Uint16Array( [ 0, 1, 2,  0, 2, 3 ] );
	var vertices = new Float32Array( [ - 0.5, - 0.5, 0,   0.5, - 0.5, 0,   0.5, 0.5, 0,   - 0.5, 0.5, 0 ] );
	var uvs = new Float32Array( [ 0, 0,   1, 0,   1, 1,   0, 1 ] );

	var geometry = new THREE.BufferGeometry();
	geometry.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) );
	geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
	geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );

	return function ( material ) {

		THREE.Object3D.call( this );

		this.type = 'Sprite';

		this.geometry = geometry;
		this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial();

	};

} )();

THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype );
THREE.Sprite.prototype.constructor = THREE.Sprite;

THREE.Sprite.prototype.raycast = ( function () {

	var matrixPosition = new THREE.Vector3();

	return function ( raycaster, intersects ) {

		matrixPosition.setFromMatrixPosition( this.matrixWorld );

		var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition );
		var diag = ( this.scale.x + this.scale.y ) / 2;

		if ( distanceSq > diag * diag ) {

			return;

		}

		intersects.push( {

			distance: Math.sqrt( distanceSq ),
			point: this.position,
			face: null,
			object: this

		} );

	};

}() );

THREE.Sprite.prototype.clone = function ( object ) {

	if ( object === undefined ) object = new THREE.Sprite( this.material );

	THREE.Object3D.prototype.clone.call( this, object );

	return object;

};

THREE.Sprite.prototype.toJSON = function ( meta ) {

	var data = THREE.Object3D.prototype.toJSON.call( this, meta );

	// only serialize if not in meta materials cache
	if ( meta.materials[ this.material.uuid ] === undefined ) {
		meta.materials[ this.material.uuid ] = this.material.toJSON();
	}

	data.object.material = this.material.uuid;

	return data;

};

// Backwards compatibility

THREE.Particle = THREE.Sprite;
<|endoftext|>