<commit_before>/**
 * Local SQLite sync adapter which will store all models in
 * an on device database
 */
var _ = require("alloy/underscore")._,
	db;

function InitDB()
{
	if (!db)
	{
		module.exports.db = db = Ti.Database.open("_alloy_");

		// create the table in case it doesn't exist
		db.execute("CREATE TABLE IF NOT EXISTS migrations (latest TEXT, model TEXT)");
	}
	return db;
}

function GetMigrationFor(table)
{
	var mid;
	// get the latest migratino
	var rs = db.execute("SELECT latest FROM migrations where model = ?",table);
	if (rs.isValidRow())
	{
		mid = rs.field(0);
	}
	rs.close();
	return mid;
}

function SQLiteMigrateDB()
{
	//TODO: we should move this into the codegen so we don't waste precious cpu cycles doing this
	this.column = function(name)
	{
		switch(name)
		{
			case 'string':
			case 'varchar':
			case 'text':
			{
				return 'TEXT';
			}
			case 'int':
			case 'tinyint':
			case 'smallint':
			case 'bigint':
			case 'integer':
			{
				return 'INTEGER';
			}
			case 'double':
			case 'float':
			case 'real':
			{
				return 'REAL';
			}
			case 'blob':
			{
				return 'BLOB';
			}
			case 'decimal':
			case 'number':
			case 'date':
			case 'datetime':
			case 'boolean':
			{
				return 'NUMERIC';
			}
			case 'null':
			{
				return 'NULL';
			}
		}
		return 'TEXT';
	};

	this.createTable = function(name,config)
	{
		Ti.API.info("create table migration called for "+config.adapter.tablename);

		var self = this,
			columns = [];

		for (var k in config.columns)
		{
			columns.push(k + ' ' + self.column(config.columns[k]));
		}

		var sql = "CREATE TABLE "+config.adapter.tablename+" ( " + columns.join(",") + " )";
		Ti.API.info(sql);

		db.execute(sql);
	};

	this.dropTable = function(name)
	{
		Ti.API.info("drop table migration called for "+name);
		db.execute("DROP TABLE IF EXISTS "+name);
	};
}

function SQLSync(model)
{
	this.model = model;
	this.table = model.config.adapter.tablename;
	this.columns = model.config.columns;

	var self = this;

	this.read = function(opts)
	{
		var sql = "select rowid,* from "+self.table;
		var rs = db.execute(sql);
		var results = [];
		while(rs.isValidRow())
		{
			var o = {};
			_.times(rs.fieldCount(),function(c){
				var fn = rs.fieldName(c);
				var rv = rs.field(c);
				o[fn]=rv;
			});
			o.id = rs.fieldByName('rowid');
			var m = new self.model.model(o);
			results.push(m);
			rs.next();
		}
		rs.close();
		return results;
	};

	this.create = function(opts)
	{
		var names = [];
		var values = [];
		var q = [];
		for (var k in self.columns)
		{
			names.push(k);
			values.push(self.model.get(k));
			q.push("?");
		}
		var sql = "insert into "+self.table + " (" + names.join(",") + ") values (" +  q.join(",") +")";
		db.execute(sql,values);
		self.model.id = db.getLastInsertRowId();
	};

	this['delete'] = function(opts)
	{
		var sql = "delete from "+self.table+" where rowid = ?";
		db.execute(sql,self.model.id);
		self.model.id = null;
	};

}

function GetMigrationForCached(t,m)
{
	if (m[t])
	{
		return m[t];
	}
	var v = GetMigrationFor(t);
	if (v)
	{
		m[t] = v;
	}
	return v;
}

function Migrate(migrations)
{
	var prev;

	//TODO: check config for the right adapter and then delegate. for now just doing SQL
	var sqlMigration = new SQLiteMigrateDB;
	var migrationIds = {}; // cache for latest mid by model name

	db.execute("BEGIN;");

	// iterate through all our migrations and call up/down and the last migration should
	// have the up called but never the down -- the migrations come in pre sorted from
	// oldest to newest based on timestamp
	_.each(migrations,function(migration)
	{
		var mctx = {};
		migration(mctx);
		var mid = GetMigrationForCached(mctx.name,migrationIds);
		Ti.API.info("mid = "+mid+", name = "+mctx.name);
		if (!mid || mctx.id > mid)
		{
			Ti.API.info("Migration starting to "+mctx.id+" for "+mctx.name);
			if (prev && _.isFunction(prev.down))
			{
				prev.down(sqlMigration);
			}
			if (_.isFunction(mctx.up))
			{
				mctx.down(sqlMigration);
				mctx.up(sqlMigration);
			}
			prev = mctx;
		}
		else
		{
			Ti.API.info("skipping migration "+mctx.id+", already performed");
			prev = null;
		}
	});

	if (prev && prev.id)
	{
		db.execute("DELETE FROM migrations");
		db.execute("INSERT INTO migrations VALUES (?,?)",prev.id,prev.name);
	}

	db.execute("COMMIT;");
}

function Sync(model, method, opts)
{
	var sync = new SQLSync(model);
	return sync[method](opts);
}

module.exports.init = InitDB;
module.exports.migrate = Migrate;
module.exports.sync = Sync;
<commit_msg>don't put the rowid in the model<commit_after>/**
 * Local SQLite sync adapter which will store all models in
 * an on device database
 */
var _ = require("alloy/underscore")._,
	db;

function InitDB()
{
	if (!db)
	{
		module.exports.db = db = Ti.Database.open("_alloy_");

		// create the table in case it doesn't exist
		db.execute("CREATE TABLE IF NOT EXISTS migrations (latest TEXT, model TEXT)");
	}
	return db;
}

function GetMigrationFor(table)
{
	var mid;
	// get the latest migratino
	var rs = db.execute("SELECT latest FROM migrations where model = ?",table);
	if (rs.isValidRow())
	{
		mid = rs.field(0);
	}
	rs.close();
	return mid;
}

function SQLiteMigrateDB()
{
	//TODO: we should move this into the codegen so we don't waste precious cpu cycles doing this
	this.column = function(name)
	{
		switch(name)
		{
			case 'string':
			case 'varchar':
			case 'text':
			{
				return 'TEXT';
			}
			case 'int':
			case 'tinyint':
			case 'smallint':
			case 'bigint':
			case 'integer':
			{
				return 'INTEGER';
			}
			case 'double':
			case 'float':
			case 'real':
			{
				return 'REAL';
			}
			case 'blob':
			{
				return 'BLOB';
			}
			case 'decimal':
			case 'number':
			case 'date':
			case 'datetime':
			case 'boolean':
			{
				return 'NUMERIC';
			}
			case 'null':
			{
				return 'NULL';
			}
		}
		return 'TEXT';
	};

	this.createTable = function(name,config)
	{
		Ti.API.info("create table migration called for "+config.adapter.tablename);

		var self = this,
			columns = [];

		for (var k in config.columns)
		{
			columns.push(k + ' ' + self.column(config.columns[k]));
		}

		var sql = "CREATE TABLE "+config.adapter.tablename+" ( " + columns.join(",") + " )";
		Ti.API.info(sql);

		db.execute(sql);
	};

	this.dropTable = function(name)
	{
		Ti.API.info("drop table migration called for "+name);
		db.execute("DROP TABLE IF EXISTS "+name);
	};
}

function SQLSync(model)
{
	this.model = model;
	this.table = model.config.adapter.tablename;
	this.columns = model.config.columns;

	var self = this;

	this.read = function(opts)
	{
		var sql = "select rowid,* from "+self.table;
		var rs = db.execute(sql);
		var results = [];
		while(rs.isValidRow())
		{
			var o = {};
			_.times(rs.fieldCount(),function(c){
				var fn = rs.fieldName(c);
				if (fn!='rowid')
				{
					// don't place rowid in the model
					var rv = rs.field(c);
					o[fn]=rv;
				}
			});
			o.id = rs.fieldByName('rowid');
			var m = new self.model.model(o);
			results.push(m);
			rs.next();
		}
		rs.close();
		return results;
	};

	this.create = function(opts)
	{
		var names = [];
		var values = [];
		var q = [];
		for (var k in self.columns)
		{
			names.push(k);
			values.push(self.model.get(k));
			q.push("?");
		}
		var sql = "insert into "+self.table + " (" + names.join(",") + ") values (" +  q.join(",") +")";
		db.execute(sql,values);
		self.model.id = db.getLastInsertRowId();
	};

	this['delete'] = function(opts)
	{
		var sql = "delete from "+self.table+" where rowid = ?";
		db.execute(sql,self.model.id);
		self.model.id = null;
	};

}

function GetMigrationForCached(t,m)
{
	if (m[t])
	{
		return m[t];
	}
	var v = GetMigrationFor(t);
	if (v)
	{
		m[t] = v;
	}
	return v;
}

function Migrate(migrations)
{
	var prev;

	//TODO: check config for the right adapter and then delegate. for now just doing SQL
	var sqlMigration = new SQLiteMigrateDB;
	var migrationIds = {}; // cache for latest mid by model name

	db.execute("BEGIN;");

	// iterate through all our migrations and call up/down and the last migration should
	// have the up called but never the down -- the migrations come in pre sorted from
	// oldest to newest based on timestamp
	_.each(migrations,function(migration)
	{
		var mctx = {};
		migration(mctx);
		var mid = GetMigrationForCached(mctx.name,migrationIds);
		Ti.API.info("mid = "+mid+", name = "+mctx.name);
		if (!mid || mctx.id > mid)
		{
			Ti.API.info("Migration starting to "+mctx.id+" for "+mctx.name);
			if (prev && _.isFunction(prev.down))
			{
				prev.down(sqlMigration);
			}
			if (_.isFunction(mctx.up))
			{
				mctx.down(sqlMigration);
				mctx.up(sqlMigration);
			}
			prev = mctx;
		}
		else
		{
			Ti.API.info("skipping migration "+mctx.id+", already performed");
			prev = null;
		}
	});

	if (prev && prev.id)
	{
		db.execute("DELETE FROM migrations");
		db.execute("INSERT INTO migrations VALUES (?,?)",prev.id,prev.name);
	}

	db.execute("COMMIT;");
}

function Sync(model, method, opts)
{
	var sync = new SQLSync(model);
	return sync[method](opts);
}

module.exports.init = InitDB;
module.exports.migrate = Migrate;
module.exports.sync = Sync;
<|endoftext|>


<commit_before>/**
 * Created by abousk01 on 20/07/2016.
 */
var api_controller = require(__dirname + '/../controllers/api');
var properties = require(__dirname + '/../properties/properties');
var restify = require('restify');
var utils = require(__dirname + '/../services/utils');
var logger = require(__dirname + '/../services/logger').getInstance();
var gcm = require('node-gcm');
var qrCode = require('qrcode-npm');
var sockets = require('../server/sockets');

// Set up the sender with you API key, prepare your recipients' registration tokens.
var opts={};
if(properties.getEsupProperty('proxyUrl'))opts={'proxy': properties.getEsupProperty('proxyUrl')};
var sender = new gcm.Sender(properties.getMethodProperty('push', 'serverKey'), opts);

exports.name = "push";

exports.send_message = function (user, req, res, next) {
    user.push.code = utils.generate_digit_code(properties.getMethod('random_code').code_length);
    var message = new gcm.Message({
        priority: "high",
        data: {
            title: "Esup-OTP-Push",
            body: "Demande de connexion à votre compte",
            text: "Demande de connexion à votre compte",
            action: 'auth',
            uid: user.uid,
            code: user.push.code,
            lt: req.params.lt
        },
        to: user.push.device.gcm_id
    });

    var regTokens = [user.push.device.gcm_id];

    user.save( function () {
        sender.send(message, {registrationTokens: regTokens}, function (err, response) {
            if (err) {
                logger.info(err);
                res.send({
                    "code": "Error",
                    "message": JSON.stringify(err)
                });
            } else {
                if(response.failure>0){
                    logger.debug(response);
                    if(response.results[0].error == "NotRegistered"){
                        user_unactivate(user, req, res, next);
                    };
                }else res.send({
                    "code": "Ok",
                    "message": response
                });
            }
        });
    });
};

/**
 * Vérifie si l'otp fourni correspond à celui généré
 * si oui: on retourne un réponse positive
 * sinon: on renvoie une erreur 401 InvalidCredentialsError
 *
 * @param req requete HTTP contenant le nom la personne recherchee
 * @param res response HTTP
 * @param next permet d'appeler le prochain gestionnaire (handler)
 */
exports.verify_code = function (user, req, res, callbacks) {
    logger.debug(utils.getFileName(__filename) + ' ' + "verify_code: " + user.uid);
    if (user.push.code == req.params.otp) {
        delete user.push.code;
        user.save( function () {
            logger.info("Valid credentials by " + user.uid);
            res.send({
                "code": "Ok",
                "message": properties.getMessage('success', 'valid_credentials')
            });
        });
    } else {
        var next = callbacks.pop();
        next(user, req, res, callbacks)
    }
}

exports.generate_method_secret = function (user, req, res, next) {
    res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'unvailable_method_operation')
    });
}

exports.delete_method_secret = function (user, req, res, next) {
    res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'unvailable_method_operation')
    });
}

exports.get_method_secret = function (user, req, res, next) {
    res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'unvailable_method_operation')
    });
}

exports.user_activate = function (user, req, res, next) {
    var activation_code = utils.generate_digit_code(6);
    user.push.activation_code = activation_code;
    var qr = qrCode.qrcode(10, 'M');
    var http = 'http://';
    if(req.headers["x-forwarded-proto"])http=req.headers["x-forwarded-proto"]+"://";
    var host = req.headers.host;
    if(req.headers["x-forwarded-host"])host=req.headers["x-forwarded-host"].replace(/,.*/,'');
    qr.addData(http+host+'/users/'+user.uid+'/methods/push/'+activation_code);
    qr.make();
    user.save( function () {
        res.send({
            "code": "Ok",
            "message1": properties.getMessage('success', 'push_confirmation1'),
            "message2": properties.getMessage('success', 'push_confirmation2'),
            "message3": properties.getMessage('success', 'push_confirmation3'),
            "message4": properties.getMessage('success', 'push_confirmation4'),
            "message5": properties.getMessage('success', 'push_confirmation5'),
            "qrCode" : qr.createImgTag(4),
            "activationCode" : activation_code
        });
    });
}
// generation of tokenSecret sent to the client, edited by mbdeme on June 2020

exports.confirm_user_activate = function (user, req, res, next) {
    if (req.params.activation_code == user.push.activation_code) {
        var token_secret = utils.generate_string_code(128);
        user.push.token_secret = token_secret;
        user.push.active = true;
        user.push.device.platform = req.params.platform || "AndroidDev";
        user.push.device.gcm_id = req.params.gcm_id || "GCMIDDev";
        user.push.device.manufacturer = req.params.manufacturer || "DevCorp";
        user.push.device.model = req.params.model || "DevDevice";
        user.push.activation_code = utils.generate_digit_code(6);
        user.save( function () {
            sockets.emitManager('userPushActivate',{uid:user.uid});
            sockets.emitToManagers('userPushActivateManager', user.uid);
            res.send({
                "code": "Ok",
                "message": "",
                "tokenSecret": token_secret
            });
        });
    } else res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'invalid_credentials')
    });
}

// Checks whether the tokenSecret received is equal to the one generated, changed by mbdeme on June 2020

exports.accept_authentication = function (user, req, res, next) {
    if (user.push.token_secret == req.params.tokenSecret) {
        var lt = req.params.loginTicket;
        user.push.lt = lt;
        user.save(function () {
            sockets.emitCas(user.uid,'userAuth');
            res.send({
                "code": "Ok"
            });
        });
    } else res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'unvailable_method_operation')
    });
}

exports.check_accept_authentication = function (user, req, res, next) {
    if (user.push.lt.indexOf(req.params.loginTicket)>-1) {
        code = user.push.code;
        user.push.lt = "";
        user.save( function () {
            res.send({
                "code": "Ok",
                "otp": user.push.code
            });
        })
    } else res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'unvailable_method_operation')
    });
}

exports.user_deactivate = user_deactivate;

function user_deactivate(user, req, res, next) {
    alert_deactivate(user);
    user.push.active = false;
    user.push.device.platform = "";
    user.push.device.gcm_id = "";
    user.push.device.manufacturer = "";
    user.push.device.model = "";
    user.push.device.phone_number = "";
    user.save( function () {
        sockets.emitManager('userPushDeactivate',{uid:user.uid});
        res.send({
            "code": "Ok",
            "message": ""
        });
    });
};

function user_unactivate(user, req, res, next) {
    user.push.active = false;
    user.push.device.platform = "";
    user.push.device.gcm_id = "";
    user.push.device.manufacturer = "";
    user.push.device.model = ""
    user.push.device.phone_number = "";
    user.save( function () {
        res.send({
            "code": "Error",
            "message": properties.getMessage('error', 'push_not_registered')
        });
    });
};

function alert_deactivate(user) {
    var message = new gcm.Message({
        priority: "high",
        data: {
            title: "Esup-OTP-Push",
            body: "Les notifications push ont été désactivées pour votre compte",
            text: "Les notifications push ont été désactivées pour votre compte",
            action: "desync"
        },
        to: user.push.device.gcm_id
    });
    var regTokens = [user.push.device.gcm_id];
    sender.send(message, {registrationTokens: regTokens}, function (err, response) {
        if (err) {
            logger.info(err);
        }
    });
}

exports.user_desync = function (user, req, res, next) {
    logger.debug(utils.getFileName(__filename) + ' ' + "user_desync: " + user.uid);
    if(req.params.tokenSecret == user.push.tokenSecret){
        user.push.active = false;
        user.push.device.platform = "";
        user.push.tokenSecret = "";
        user.push.device.phone_number = "";
        user.save( function () {
            res.send({
                "code": "Ok",
                "message": ""
            });
        });
    }else {
        res.send({
            "code": "Error",
            "message": "tokenSecret"
        });

    }
}

exports.admin_activate = function (req, res, next) {
    res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'unvailable_method_operation')
    });
}
<commit_msg>modif title Esup OTP Push to Esup Auth<commit_after>/**
 * Created by abousk01 on 20/07/2016.
 */
var api_controller = require(__dirname + '/../controllers/api');
var properties = require(__dirname + '/../properties/properties');
var restify = require('restify');
var utils = require(__dirname + '/../services/utils');
var logger = require(__dirname + '/../services/logger').getInstance();
var gcm = require('node-gcm');
var qrCode = require('qrcode-npm');
var sockets = require('../server/sockets');

// Set up the sender with you API key, prepare your recipients' registration tokens.
var opts={};
if(properties.getEsupProperty('proxyUrl'))opts={'proxy': properties.getEsupProperty('proxyUrl')};
var sender = new gcm.Sender(properties.getMethodProperty('push', 'serverKey'), opts);

exports.name = "push";

exports.send_message = function (user, req, res, next) {
    user.push.code = utils.generate_digit_code(properties.getMethod('random_code').code_length);
    var message = new gcm.Message({
        priority: "high",
        data: {
            title: "Esup Auth",
            body: "Demande de connexion à votre compte",
            text: "Demande de connexion à votre compte",
            action: 'auth',
            uid: user.uid,
            code: user.push.code,
            lt: req.params.lt
        },
        to: user.push.device.gcm_id
    });

    var regTokens = [user.push.device.gcm_id];

    user.save( function () {
        sender.send(message, {registrationTokens: regTokens}, function (err, response) {
            if (err) {
                logger.info(err);
                res.send({
                    "code": "Error",
                    "message": JSON.stringify(err)
                });
            } else {
                if(response.failure>0){
                    logger.debug(response);
                    if(response.results[0].error == "NotRegistered"){
                        user_unactivate(user, req, res, next);
                    };
                }else res.send({
                    "code": "Ok",
                    "message": response
                });
            }
        });
    });
};

/**
 * Vérifie si l'otp fourni correspond à celui généré
 * si oui: on retourne un réponse positive
 * sinon: on renvoie une erreur 401 InvalidCredentialsError
 *
 * @param req requete HTTP contenant le nom la personne recherchee
 * @param res response HTTP
 * @param next permet d'appeler le prochain gestionnaire (handler)
 */
exports.verify_code = function (user, req, res, callbacks) {
    logger.debug(utils.getFileName(__filename) + ' ' + "verify_code: " + user.uid);
    if (user.push.code == req.params.otp) {
        delete user.push.code;
        user.save( function () {
            logger.info("Valid credentials by " + user.uid);
            res.send({
                "code": "Ok",
                "message": properties.getMessage('success', 'valid_credentials')
            });
        });
    } else {
        var next = callbacks.pop();
        next(user, req, res, callbacks)
    }
}

exports.generate_method_secret = function (user, req, res, next) {
    res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'unvailable_method_operation')
    });
}

exports.delete_method_secret = function (user, req, res, next) {
    res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'unvailable_method_operation')
    });
}

exports.get_method_secret = function (user, req, res, next) {
    res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'unvailable_method_operation')
    });
}

exports.user_activate = function (user, req, res, next) {
    var activation_code = utils.generate_digit_code(6);
    user.push.activation_code = activation_code;
    var qr = qrCode.qrcode(10, 'M');
    var http = 'http://';
    if(req.headers["x-forwarded-proto"])http=req.headers["x-forwarded-proto"]+"://";
    var host = req.headers.host;
    if(req.headers["x-forwarded-host"])host=req.headers["x-forwarded-host"].replace(/,.*/,'');
    qr.addData(http+host+'/users/'+user.uid+'/methods/push/'+activation_code);
    qr.make();
    user.save( function () {
        res.send({
            "code": "Ok",
            "message1": properties.getMessage('success', 'push_confirmation1'),
            "message2": properties.getMessage('success', 'push_confirmation2'),
            "message3": properties.getMessage('success', 'push_confirmation3'),
            "message4": properties.getMessage('success', 'push_confirmation4'),
            "message5": properties.getMessage('success', 'push_confirmation5'),
            "qrCode" : qr.createImgTag(4),
            "activationCode" : activation_code
        });
    });
}
// generation of tokenSecret sent to the client, edited by mbdeme on June 2020

exports.confirm_user_activate = function (user, req, res, next) {
    if (req.params.activation_code == user.push.activation_code) {
        var token_secret = utils.generate_string_code(128);
        user.push.token_secret = token_secret;
        user.push.active = true;
        user.push.device.platform = req.params.platform || "AndroidDev";
        user.push.device.gcm_id = req.params.gcm_id || "GCMIDDev";
        user.push.device.manufacturer = req.params.manufacturer || "DevCorp";
        user.push.device.model = req.params.model || "DevDevice";
        user.push.activation_code = utils.generate_digit_code(6);
        user.save( function () {
            sockets.emitManager('userPushActivate',{uid:user.uid});
            sockets.emitToManagers('userPushActivateManager', user.uid);
            res.send({
                "code": "Ok",
                "message": "",
                "tokenSecret": token_secret
            });
        });
    } else res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'invalid_credentials')
    });
}

// Checks whether the tokenSecret received is equal to the one generated, changed by mbdeme on June 2020

exports.accept_authentication = function (user, req, res, next) {
    if (user.push.token_secret == req.params.tokenSecret) {
        var lt = req.params.loginTicket;
        user.push.lt = lt;
        user.save(function () {
            sockets.emitCas(user.uid,'userAuth');
            res.send({
                "code": "Ok"
            });
        });
    } else res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'unvailable_method_operation')
    });
}

exports.check_accept_authentication = function (user, req, res, next) {
    if (user.push.lt.indexOf(req.params.loginTicket)>-1) {
        code = user.push.code;
        user.push.lt = "";
        user.save( function () {
            res.send({
                "code": "Ok",
                "otp": user.push.code
            });
        })
    } else res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'unvailable_method_operation')
    });
}

exports.user_deactivate = user_deactivate;

function user_deactivate(user, req, res, next) {
    alert_deactivate(user);
    user.push.active = false;
    user.push.device.platform = "";
    user.push.device.gcm_id = "";
    user.push.device.manufacturer = "";
    user.push.device.model = "";
    user.push.device.phone_number = "";
    user.save( function () {
        sockets.emitManager('userPushDeactivate',{uid:user.uid});
        res.send({
            "code": "Ok",
            "message": ""
        });
    });
};

function user_unactivate(user, req, res, next) {
    user.push.active = false;
    user.push.device.platform = "";
    user.push.device.gcm_id = "";
    user.push.device.manufacturer = "";
    user.push.device.model = ""
    user.push.device.phone_number = "";
    user.save( function () {
        res.send({
            "code": "Error",
            "message": properties.getMessage('error', 'push_not_registered')
        });
    });
};

function alert_deactivate(user) {
    var message = new gcm.Message({
        priority: "high",
        data: {
            title: "Esup Auth",
            body: "Les notifications push ont été désactivées pour votre compte",
            text: "Les notifications push ont été désactivées pour votre compte",
            action: "desync"
        },
        to: user.push.device.gcm_id
    });
    var regTokens = [user.push.device.gcm_id];
    sender.send(message, {registrationTokens: regTokens}, function (err, response) {
        if (err) {
            logger.info(err);
        }
    });
}

exports.user_desync = function (user, req, res, next) {
    logger.debug(utils.getFileName(__filename) + ' ' + "user_desync: " + user.uid);
    if(req.params.tokenSecret == user.push.tokenSecret){
        user.push.active = false;
        user.push.device.platform = "";
        user.push.tokenSecret = "";
        user.push.device.phone_number = "";
        user.save( function () {
            res.send({
                "code": "Ok",
                "message": ""
            });
        });
    }else {
        res.send({
            "code": "Error",
            "message": "tokenSecret"
        });

    }
}

exports.admin_activate = function (req, res, next) {
    res.send({
        "code": "Error",
        "message": properties.getMessage('error', 'unvailable_method_operation')
    });
}
<|endoftext|>


<commit_before>
var rentalDates;

 function disableDays(date)
 {
    var month = date.getMonth()+1;
    var day = date.getDate();
    var year = date.getFullYear().toString().substr(2,2);

    var totalDate = month + "/" + day + "/" + year;

    totalDate = new Date (totalDate);

    console.log(totalDate);
    console.log(x1);

     for (i = 0; i<rentalDate.length; i++)
     {
        x1 = rentalDate[i].startDate;
        x2 = rentalDate[i].endDate;

        x1 = new Date(x1);
        x2 = new Date(x2);

        if ((totalDate.getTime() > x1.getTime()) && (totalDate.getTime() < x2.getTime()))
        {
            return [false, "", "not valid"];
        }

     }
       return [true];

  }

$( function()
{
          //function used to init the calender.
          //also blocks out certain dates from the server rented dates.

    var dateFormat = "mm/dd/yy",
        from = $( "#from" )
            .datepicker({
              defaultDate: "+1w",
              changeMonth: true,
              numberOfMonths: 1,
                beforeShowDay: disableDays
            })
            .on( "change", function() {
              to.datepicker( "option", "minDate", getDate(this) );
            }),
          to = $( "#to" ).datepicker({
            defaultDate: "+1w",
            changeMonth: true,
            numberOfMonths: 1,
              beforeShowDay: disableDays
          })
          .on( "change", function() {
            from.datepicker( "option", "maxDate", getDate( this ) );
          });

        function getDate( element ) {
          //returns the date from the calender.

            var date;
          try {
            date = $.datepicker.parseDate( dateFormat, element.value );
          } catch( error ) {
            date = null;
          }

          return date;
        }
      } );

function jqueryCalender()
{
    var startDate = $("from").val();
    var endDate = $("to").val();

    $.ajax(url: "Servlet", data: {calenderVal : "calender" }, success:function(data)
    {
        data = jQuery.parseJSON(data);

        rentalDates = data;
        //rental date array set to servlet response.

        //$("#calendarArea").availabilityCalendar(data);

    });
}
<commit_msg>cleaned up ajax code<commit_after>
var rentalDates;

 function disableDays(date)
 {
    var month = date.getMonth()+1;
    var day = date.getDate();
    var year = date.getFullYear().toString().substr(2,2);

    var totalDate = month + "/" + day + "/" + year;

    totalDate = new Date (totalDate);

    console.log(totalDate);
    console.log(x1);

     for (i = 0; i<rentalDate.length; i++)
     {
        x1 = rentalDate[i].startDate;
        x2 = rentalDate[i].endDate;

        x1 = new Date(x1);
        x2 = new Date(x2);

        if ((totalDate.getTime() > x1.getTime()) && (totalDate.getTime() < x2.getTime()))
        {
            return [false, "", "not valid"];
        }

     }
       return [true];

  }

$( function()
{
          //function used to init the calender.
          //also blocks out certain dates from the server rented dates.

    var dateFormat = "mm/dd/yy",
        from = $( "#from" )
            .datepicker({
              defaultDate: "+1w",
              changeMonth: true,
              numberOfMonths: 1,
                beforeShowDay: disableDays
            })
            .on( "change", function() {
              to.datepicker( "option", "minDate", getDate(this) );
            }),
          to = $( "#to" ).datepicker({
            defaultDate: "+1w",
            changeMonth: true,
            numberOfMonths: 1,
              beforeShowDay: disableDays
          })
          .on( "change", function() {
            from.datepicker( "option", "maxDate", getDate( this ) );
          });

        function getDate( element ) {
          //returns the date from the calender.

            var date;
          try {
            date = $.datepicker.parseDate( dateFormat, element.value );
          } catch( error ) {
            date = null;
          }

          return date;
        }
      } );

function jqueryCalender()
{

    $.ajax(url: "Servlet", data: {calender : "calender" }, success:function(data)
    {
        data = jQuery.parseJSON(data);

        rentalDates = data;
        //rental date array set to servlet response.

        //$("#calendarArea").availabilityCalendar(data);

    });
}
<|endoftext|>


<commit_before>module.exports = {
  scripts: {
      files: ['src/js/*.js', 'src/scss/*.scss'],
      tasks: ['concat', 'uglify'],
      options: {
          nospawn: true,
          debounceDelay: 250,
      },
  },
};
<commit_msg>adding newer to watch task<commit_after>// Hint: newer:taskName - configure Grunt tasks to run with newer files only.

module.exports = {
  scripts: {
      files: ['src/js/*.js', 'src/scss/*.scss'],
      tasks: ['newer:jshint', 'concat', 'newer:uglify'],
      options: {
          nospawn: true,
          debounceDelay: 250,
      },
  },
};
<|endoftext|>


<commit_before>/**
 * Copyright 2014 aixigo AG
 * Released under the MIT license.
 * http://laxarjs.org/license
 */
define( [
   '../utilities/assert',
   '../utilities/string',
   '../utilities/path'
], function( assert, string, path ) {
   'use strict';

   var q_;
   var httpClient_;
   var BORDER_SLASHES_MATCHER = /^\/|\/$/g;

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * A provider for file resources that tries to minimize the amount of 404 errors when requesting files that
    * are not available. To achieve this it is backed by one or more directory tree mappings that already list
    * which files are available on the server. For any file being located at a path that is not supported by a
    * mapping, a HEAD request takes place, that might or might not result in a 404 error. If a file is
    * located at a path supported by a mapping, but is not found in that mapping (because it was added later),
    * it is assumed to be nonexistent.
    *
    * @param {String} rootPath
    *    the path to the root of the application. It is needed to prefix relative paths found in the listing
    *    with an absolute prefix
    * @constructor
    */
   function FileResourceProvider( rootPath ) {
      this.rootPath_ = path.normalize( rootPath );
      this.fileListings_ = {};
      this.fileListingUris_ = {};

      this.httpGets_ = {};
      this.httpHeads_ = {};
      this.httpHeadCache_ = {};
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * If available, resolves the returned promise with the file's contents. Otherwise the promise is rejected.
    * It uses the file mapping prior to fetching the contents to prevent from 404 errors. If no listing for
    * the path is available, the request simply takes place and either succeeds or fails.
    *
    * @param {String} resourceUri
    *    the uri to the resource to provide
    * @return {Promise}
    *    resolved with the file's content or rejected when the file could not be fetched
    */
   FileResourceProvider.prototype.provide = function( url ) {
      var self = this;
      return entry( this, url ).then( function( knownEntry ) {
         if( typeof( knownEntry ) === 'string' ) {
            return q_.when( knownEntry ).then( resourceTransform( url ) );
         }
         return knownEntry !== false ? httpGet( self, url ).then( resourceTransform( url ) ) : q_.reject();
      }, function() {
         return httpGet( self, url ).then( resourceTransform( url ) );
      } );
   };

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * Resolves the returned promise with `true` as argument, if the requested resource is available and
    * `false` otherwise.  If no listing for the path is available, a HEAD request takes place and either
    * succeeds or fails.
    *
    * @param {String} resourceUri
    *    the uri to check for availability
    * @return {Promise}
    *    a promise that is always resolved with a boolean value
    */
   FileResourceProvider.prototype.isAvailable = function isAvailable( url ) {
      var self = this;
      return entry( self, url ).then( function( knownEntry ) {
         return q_.when( knownEntry !== false );
      }, function() {
         return httpHead( self, url ).then( function( knownAvailable ) {
            return q_.when( knownAvailable );
         } );
     } );
   };

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * Sets the uri to a file listing file for a given path.
    *
    * @param {String} directory
    *    the directory the file listing is valid for
    * @param {String} listingUri
    *    the uri to the listing file
    */
   FileResourceProvider.prototype.setFileListingUri = function( directory, listingUri ) {
      var filePathPrefix = path.join( this.rootPath_, directory );
      this.fileListingUris_[ filePathPrefix ] = path.join( this.rootPath_, listingUri );
      this.fileListings_[ filePathPrefix ] = null;
   };

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * Try to lookup a file resource in the provider's listings.
    * @private
    *
    * @returns {Promise<*>}
    *    Resolves to `true` (listed but not embedded), to `false` (file is not listed), or to a string
    *    (embedded content for a listed file).
    */
   function entry( provider, resourcePath ) {
      var usablePrefixes = Object.keys( provider.fileListingUris_ ).filter( function( prefix ) {
         return resourcePath.indexOf( prefix ) === 0;
      } );

      if( usablePrefixes.length ) {
         var prefix = usablePrefixes[ 0 ];
         return fetchListingForPath( provider, prefix ).then( function( listing ) {
            return q_.when( lookup( provider, resourcePath, listing ) );
         } );
      }

      return q_.reject();

      ////////////////////////////////////////////////////////////////////////////////////////////////////////

      function lookup( self, file, listing ) {
         var parts = file.replace( self.rootPath_, '' ).replace( BORDER_SLASHES_MATCHER, '' ).split( '/' );
         for( var i = 0, len = parts.length; i < len; ++i ) {
            if( i === len - 1 ) {
               var value = listing[ parts[ i ] ];
               return typeof( value ) === 'string' ? value : (value === 1);
            }

            listing = listing[ parts[ i ] ];
            if( typeof listing !== 'object' ) {
               return false;
            }
         }
      }
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   function resourceTransform( path ) {
      return string.endsWith( path, '.json' ) ?
         function( contents ) { return JSON.parse( contents ); } :
         function( contents ) { return contents; };
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   function fetchListingForPath( self, path ) {
      if( self.fileListings_[ path ] ) {
         return q_.when( self.fileListings_[ path ] );
      }

      var listingUri = self.fileListingUris_[ path ];
      return httpGet( self, listingUri )
         .then( resourceTransform( listingUri ) )
         .then( function( listing ) {
            self.fileListings_[ path ] = listing;
            return listing;
         } );
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * @private
    * @param {FileResourceProvider} self
    * @param {String} url A url to get
    * @return {Promise<String>} Resolved to the file contents if the request succeeds
    */
   function httpGet( self, url ) {
      if( url in self.httpGets_ ) {
         return self.httpGets_[ url ];
      }

      var promise = self.httpGets_[ url ] = httpClient_
         .get( url, { transformResponse: [] } )
         .then( function( response ) {
            return q_.when( response.data );
         } );

      // Free memory when the response is complete:
      promise.then( function() {
         delete self.httpGets_[ url ];
      } );

      return promise;
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * @private
    * @param {FileResourceProvider} self
    * @param {String} url A url to check using a HEAD request
    * @return {Promise<Boolean>} Resolved to `true` if a HEAD-request to the url succeeds, else to `false`.
    */
   function httpHead( self, url ) {
      if( url in self.httpHeadCache_ ) {
         return q_.when( self.httpHeadCache_[ url ] );
      }
      if( url in self.httpHeads_ ) {
         return self.httpHeads_[ url ];
      }

      var promise = self.httpHeads_[ url ] = httpClient_.head( url )
         .then( function() {
            return true;
         }, function() {
            return false;
         } );

      // Free memory and cache result when the response is complete:
      promise.then( function( result ) {
         self.httpHeadCache_[ url ] = result;
         delete self.httpHeads_[ url ];
      } );

      return promise;
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   return {

      /**
       * Creates and returns a new instance.
       *
       * @param {String} rootPath
       *    the path to the root of the application. It is needed to prefix relative paths found in the
       *    listing with an absolute prefix
       * @return {FileResourceProvider}
       */
      create: function( rootPath ) {
         assert( q_ ).isNotNull( 'Need a promise implementation like $q or Q' );
         assert( httpClient_ ).isNotNull( 'Need a http client implementation like $http' );

         return new FileResourceProvider( rootPath );
      },

      ////////////////////////////////////////////////////////////////////////////////////////////////////////

      /**
       * Initializes the module.
       *
       * @param {Object} q
       *    a promise library like AngularJS' $q
       * @param {Object} httpClient
       *    a http client whose api is like AngularJS' $http service
       */
      init: function( q, httpClient ) {
         q_ = q;
         httpClient_ = httpClient;
      }

   };

} );<commit_msg>(#22) Load file listings as soon as possible<commit_after>/**
 * Copyright 2014 aixigo AG
 * Released under the MIT license.
 * http://laxarjs.org/license
 */
define( [
   '../utilities/assert',
   '../utilities/string',
   '../utilities/path'
], function( assert, string, path ) {
   'use strict';

   var q_;
   var httpClient_;
   var BORDER_SLASHES_MATCHER = /^\/|\/$/g;

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * A provider for file resources that tries to minimize the amount of 404 errors when requesting files that
    * are not available. To achieve this it is backed by one or more directory tree mappings that already list
    * which files are available on the server. For any file being located at a path that is not supported by a
    * mapping, a HEAD request takes place, that might or might not result in a 404 error. If a file is
    * located at a path supported by a mapping, but is not found in that mapping (because it was added later),
    * it is assumed to be nonexistent.
    *
    * @param {String} rootPath
    *    the path to the root of the application. It is needed to prefix relative paths found in the listing
    *    with an absolute prefix
    * @constructor
    */
   function FileResourceProvider( rootPath ) {
      this.rootPath_ = path.normalize( rootPath );
      this.fileListings_ = {};
      this.fileListingUris_ = {};

      this.httpGets_ = {};
      this.httpHeads_ = {};
      this.httpHeadCache_ = {};
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * If available, resolves the returned promise with the file's contents. Otherwise the promise is rejected.
    * It uses the file mapping prior to fetching the contents to prevent from 404 errors. If no listing for
    * the path is available, the request simply takes place and either succeeds or fails.
    *
    * @param {String} resourceUri
    *    the uri to the resource to provide
    * @return {Promise}
    *    resolved with the file's content or rejected when the file could not be fetched
    */
   FileResourceProvider.prototype.provide = function( url ) {
      var self = this;
      return entry( this, url ).then( function( knownEntry ) {
         if( typeof( knownEntry ) === 'string' ) {
            return q_.when( knownEntry ).then( resourceTransform( url ) );
         }
         return knownEntry !== false ? httpGet( self, url ).then( resourceTransform( url ) ) : q_.reject();
      }, function() {
         return httpGet( self, url ).then( resourceTransform( url ) );
      } );
   };

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * Resolves the returned promise with `true` as argument, if the requested resource is available and
    * `false` otherwise.  If no listing for the path is available, a HEAD request takes place and either
    * succeeds or fails.
    *
    * @param {String} resourceUri
    *    the uri to check for availability
    * @return {Promise}
    *    a promise that is always resolved with a boolean value
    */
   FileResourceProvider.prototype.isAvailable = function isAvailable( url ) {
      var self = this;
      return entry( self, url ).then( function( knownEntry ) {
         return q_.when( knownEntry !== false );
      }, function() {
         return httpHead( self, url ).then( function( knownAvailable ) {
            return q_.when( knownAvailable );
         } );
     } );
   };

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * Sets the uri to a file listing file for a given path.
    *
    * @param {String} directory
    *    the directory the file listing is valid for
    * @param {String} listingUri
    *    the uri to the listing file
    */
   FileResourceProvider.prototype.setFileListingUri = function( directory, listingUri ) {
      var filePathPrefix = path.join( this.rootPath_, directory );
      this.fileListingUris_[ filePathPrefix ] = path.join( this.rootPath_, listingUri );
      this.fileListings_[ filePathPrefix ] = null;
      fetchListingForPath( this, filePathPrefix );
   };

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * Try to lookup a file resource in the provider's listings.
    * @private
    *
    * @returns {Promise<*>}
    *    Resolves to `true` (listed but not embedded), to `false` (file is not listed), or to a string
    *    (embedded content for a listed file).
    */
   function entry( provider, resourcePath ) {
      var usablePrefixes = Object.keys( provider.fileListingUris_ ).filter( function( prefix ) {
         return resourcePath.indexOf( prefix ) === 0;
      } );

      if( usablePrefixes.length ) {
         var prefix = usablePrefixes[ 0 ];
         return fetchListingForPath( provider, prefix ).then( function( listing ) {
            return q_.when( lookup( provider, resourcePath, listing ) );
         } );
      }

      return q_.reject();

      ////////////////////////////////////////////////////////////////////////////////////////////////////////

      function lookup( self, file, listing ) {
         var parts = file.replace( self.rootPath_, '' ).replace( BORDER_SLASHES_MATCHER, '' ).split( '/' );
         for( var i = 0, len = parts.length; i < len; ++i ) {
            if( i === len - 1 ) {
               var value = listing[ parts[ i ] ];
               return typeof( value ) === 'string' ? value : (value === 1);
            }

            listing = listing[ parts[ i ] ];
            if( typeof listing !== 'object' ) {
               return false;
            }
         }
      }
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   function resourceTransform( path ) {
      return string.endsWith( path, '.json' ) ?
         function( contents ) { return JSON.parse( contents ); } :
         function( contents ) { return contents; };
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   function fetchListingForPath( self, path ) {
      if( self.fileListings_[ path ] ) {
         return q_.when( self.fileListings_[ path ] );
      }

      var listingUri = self.fileListingUris_[ path ];
      return httpGet( self, listingUri )
         .then( resourceTransform( listingUri ) )
         .then( function( listing ) {
            self.fileListings_[ path ] = listing;
            return listing;
         } );
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * @private
    * @param {FileResourceProvider} self
    * @param {String} url A url to get
    * @return {Promise<String>} Resolved to the file contents if the request succeeds
    */
   function httpGet( self, url ) {
      if( url in self.httpGets_ ) {
         return self.httpGets_[ url ];
      }

      var promise = self.httpGets_[ url ] = httpClient_
         .get( url, { transformResponse: [] } )
         .then( function( response ) {
            return q_.when( response.data );
         } );

      // Free memory when the response is complete:
      promise.then( function() {
         delete self.httpGets_[ url ];
      } );

      return promise;
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   /**
    * @private
    * @param {FileResourceProvider} self
    * @param {String} url A url to check using a HEAD request
    * @return {Promise<Boolean>} Resolved to `true` if a HEAD-request to the url succeeds, else to `false`.
    */
   function httpHead( self, url ) {
      if( url in self.httpHeadCache_ ) {
         return q_.when( self.httpHeadCache_[ url ] );
      }
      if( url in self.httpHeads_ ) {
         return self.httpHeads_[ url ];
      }

      var promise = self.httpHeads_[ url ] = httpClient_.head( url )
         .then( function() {
            return true;
         }, function() {
            return false;
         } );

      // Free memory and cache result when the response is complete:
      promise.then( function( result ) {
         self.httpHeadCache_[ url ] = result;
         delete self.httpHeads_[ url ];
      } );

      return promise;
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////////////

   return {

      /**
       * Creates and returns a new instance.
       *
       * @param {String} rootPath
       *    the path to the root of the application. It is needed to prefix relative paths found in the
       *    listing with an absolute prefix
       * @return {FileResourceProvider}
       */
      create: function( rootPath ) {
         assert( q_ ).isNotNull( 'Need a promise implementation like $q or Q' );
         assert( httpClient_ ).isNotNull( 'Need a http client implementation like $http' );

         return new FileResourceProvider( rootPath );
      },

      ////////////////////////////////////////////////////////////////////////////////////////////////////////

      /**
       * Initializes the module.
       *
       * @param {Object} q
       *    a promise library like AngularJS' $q
       * @param {Object} httpClient
       *    a http client whose api is like AngularJS' $http service
       */
      init: function( q, httpClient ) {
         q_ = q;
         httpClient_ = httpClient;
      }

   };

} );<|endoftext|>


<commit_before>'use strict';

import React, {
	Component,
	PropTypes,
} from 'react';
import {
	ListView,
	Text,
	View,
} from 'react-native';

import SwipeRow from './SwipeRow';

/**
 * ListView that renders SwipeRows.
 */
class SwipeListView extends Component {

	constructor(props){
		super(props);
		this._rows = {};
		this.openCellId = null;
	}

	setScrollEnabled(enable) {
		this._listView.setNativeProps({scrollEnabled: enable});
	}

	safeCloseOpenRow() {
		// if the openCellId is stale due to deleting a row this could be undefined
		if (this._rows[this.openCellId]) {
			this._rows[this.openCellId].closeRow();
		}
	}

	rowSwipeGestureBegan(id) {
		if (this.props.closeOnRowBeginSwipe && this.openCellId && this.openCellId !== id) {
			this.safeCloseOpenRow();
		}
	}

	onRowOpen(secId, rowId, rowMap) {
		const cellIdentifier = `${secId}${rowId}`;
		if (this.openCellId && this.openCellId !== cellIdentifier) {
			this.safeCloseOpenRow();
		}
		this.openCellId = cellIdentifier;
		this.props.onRowOpen && this.props.onRowOpen(secId, rowId, rowMap);
	}

	onRowPress(id) {
		if (this.openCellId) {
			if (this.props.closeOnRowPress) {
				this.safeCloseOpenRow();
				this.openCellId = null;
			}
		}
	}

	onScroll(e) {
		if (this.openCellId) {
			if (this.props.closeOnScroll) {
				this.safeCloseOpenRow();
				this.openCellId = null;
			}
		}
		this.props.onScroll && this.props.onScroll(e);
	}

	setRefs(ref) {
		this._listView = ref;
		this.props.listViewRef && this.props.listViewRef(ref);
	}

	renderRow(rowData, secId, rowId, rowMap) {
		const Component = this.props.renderRow(rowData, secId, rowId, rowMap);
		if (!this.props.renderHiddenRow) {
			return React.cloneElement(
				Component,
				{
					...Component.props,
					ref: row => this._rows[`${secId}${rowId}`] = row,
					onRowOpen: _ => this.onRowOpen(secId, rowId, this._rows),
					onRowDidOpen: _ => this.props.onRowDidOpen && this.props.onRowDidOpen(secId, rowId, this._rows),
					onRowClose: _ => this.props.onRowClose && this.props.onRowClose(secId, rowId, this._rows),
					onRowDidClose: _ => this.props.onRowDidClose && this.props.onRowDidClose(secId, rowId, this._rows),
					onRowPress: _ => this.onRowPress(`${secId}${rowId}`),
					setScrollEnabled: enable => this.setScrollEnabled(enable),
					swipeGestureBegan: _ => this.rowSwipeGestureBegan(`${secId}${rowId}`)
				}
			);
		} else {
			const previewRowId = this.props.dataSource && this.props.dataSource.getRowIDForFlatIndex(this.props.previewRowIndex || 0);
			return (
				<SwipeRow
					ref={row => this._rows[`${secId}${rowId}`] = row}
					swipeGestureBegan={ _ => this.rowSwipeGestureBegan(`${secId}${rowId}`) }
					onRowOpen={ _ => this.onRowOpen(secId, rowId, this._rows) }
					onRowDidOpen={ _ => this.props.onRowDidOpen && this.props.onRowDidOpen(secId, rowId, this._rows)}
					onRowClose={ _ => this.props.onRowClose && this.props.onRowClose(secId, rowId, this._rows) }
					onRowDidClose={ _ => this.props.onRowDidClose && this.props.onRowDidClose(secId, rowId, this._rows) }
					onRowPress={ _ => this.onRowPress(`${secId}${rowId}`) }
					setScrollEnabled={ (enable) => this.setScrollEnabled(enable) }
					leftOpenValue={this.props.leftOpenValue}
					rightOpenValue={this.props.rightOpenValue}
					closeOnRowPress={this.props.closeOnRowPress}
					disableLeftSwipe={this.props.disableLeftSwipe}
					disableRightSwipe={this.props.disableRightSwipe}
					stopLeftSwipe={this.props.stopLeftSwipe}
					stopRightSwipe={this.props.stopRightSwipe}
					recalculateHiddenLayout={this.props.recalculateHiddenLayout}
					style={this.props.swipeRowStyle}
					preview={(this.props.previewFirstRow || this.props.previewRowIndex) && rowId === previewRowId}
					previewDuration={this.props.previewDuration}
					previewOpenValue={this.props.previewOpenValue}
					tension={this.props.tension}
					friction={this.props.friction}
					directionalDistanceChangeThreshold={this.props.directionalDistanceChangeThreshold}
					swipeToOpenPercent={this.props.swipeToOpenPercent}
				>
					{this.props.renderHiddenRow(rowData, secId, rowId, this._rows)}
					{this.props.renderRow(rowData, secId, rowId, this._rows)}
				</SwipeRow>
			);
		}
	}

	render() {
		return (
			<ListView
				{...this.props}
				ref={ c => this.setRefs(c) }
				onScroll={ e => this.onScroll(e) }
				renderRow={(rowData, secId, rowId) => this.renderRow(rowData, secId, rowId, this._rows)}
			/>
		)
	}

}

SwipeListView.propTypes = {
	/**
	 * How to render a row. Should return a valid React Element.
	 */
	renderRow: PropTypes.func.isRequired,
	/**
	 * How to render a hidden row (renders behind the row). Should return a valid React Element.
	 * This is required unless renderRow is passing a SwipeRow.
	 */
	renderHiddenRow: PropTypes.func,
	/**
	 * TranslateX value for opening the row to the left (positive number)
	 */
	leftOpenValue: PropTypes.number,
	/**
	 * TranslateX value for opening the row to the right (negative number)
	 */
	rightOpenValue: PropTypes.number,
	/**
	 * TranslateX value for stop the row to the left (positive number)
	 */
	stopLeftSwipe: PropTypes.number,
	/**
	 * TranslateX value for stop the row to the right (negative number)
	 */
	stopRightSwipe: PropTypes.number,
	/**
	 * Should open rows be closed when the listView begins scrolling
	 */
	closeOnScroll: PropTypes.bool,
	/**
	 * Should open rows be closed when a row is pressed
	 */
	closeOnRowPress: PropTypes.bool,
	/**
	 * Should open rows be closed when a row begins to swipe open
	 */
	closeOnRowBeginSwipe: PropTypes.bool,
	/**
	 * Disable ability to swipe rows left
	 */
	disableLeftSwipe: PropTypes.bool,
	/**
	 * Disable ability to swipe rows right
	 */
	disableRightSwipe: PropTypes.bool,
	/**
	 * Enable hidden row onLayout calculations to run always.
	 *
	 * By default, hidden row size calculations are only done on the first onLayout event
	 * for performance reasons.
	 * Passing ```true``` here will cause calculations to run on every onLayout event.
	 * You may want to do this if your rows' sizes can change.
	 * One case is a SwipeListView with rows of different heights and an options to delete rows.
	 */
	recalculateHiddenLayout: PropTypes.bool,
	/**
	 * Called when a swipe row is animating open
	 */
	onRowOpen: PropTypes.func,
	/**
	 * Called when a swipe row has animated open
	 */
	onRowDidOpen: PropTypes.func,
	/**
	 * Called when a swipe row is animating closed
	 */
	onRowClose: PropTypes.func,
	/**
	 * Called when a swipe row has animated closed
	 */
	onRowDidClose: PropTypes.func,
	/**
	 * Styles for the parent wrapper View of the SwipeRow
	 */
	swipeRowStyle: View.propTypes.style,
	/**
	 * Called when the ListView ref is set and passes a ref to the ListView
	 * e.g. listViewRef={ ref => this._swipeListViewRef = ref }
	 */
	listViewRef: PropTypes.func,
	/**
	 * Should the first SwipeRow do a slide out preview to show that the list is swipeable
	 */
	previewFirstRow: PropTypes.bool,
	/**
	 * Should the specified rowId do a slide out preview to show that the list is swipeable
	 * Note: This ID will be passed to this function to get the correct row index
	 * https://facebook.github.io/react-native/docs/listviewdatasource.html#getrowidforflatindex
	 */
	previewRowIndex: PropTypes.number,
	/**
	 * Duration of the slide out preview animation (milliseconds)
	 */
	previewDuration: PropTypes.number,
	/**
	 * TranslateX value for the slide out preview animation
	 * Default: 0.5 * props.rightOpenValue
	 */
	previewOpenValue: PropTypes.number,
	/**
	 * Friction for the open / close animation
	 */
	friction: PropTypes.number,
	/**
	 * Tension for the open / close animation
	 */
	tension: PropTypes.number,
	/**
	 * The dx value used to detect when a user has begun a swipe gesture
	 */
	directionalDistanceChangeThreshold: PropTypes.number,
	/**
	 * What % of the left/right openValue does the user need to swipe
	 * past to trigger the row opening.
	 */
	swipeToOpenPercent: PropTypes.number,
}

SwipeListView.defaultProps = {
	leftOpenValue: 0,
	rightOpenValue: 0,
	closeOnRowBeginSwipe: false,
	closeOnScroll: true,
	closeOnRowPress: true,
	disableLeftSwipe: false,
	disableRightSwipe: false,
	recalculateHiddenLayout: false,
	previewFirstRow: false,
	directionalDistanceChangeThreshold: 2,
	swipeToOpenPercent: 50
}

export default SwipeListView;
<commit_msg>Add ability to customize leftOpenValue, rightOpenValue, closeOnRowPress, disableLeftSwipe, disableRightSwipe, stopLeftSwipe, stopRightSwipe variables to needed rows<commit_after>'use strict';

import React, {
	Component,
	PropTypes,
} from 'react';
import {
	ListView,
	Text,
	View,
} from 'react-native';

import SwipeRow from './SwipeRow';

/**
 * ListView that renders SwipeRows.
 */
class SwipeListView extends Component {

	constructor(props){
		super(props);
		this._rows = {};
		this.openCellId = null;
	}

	setScrollEnabled(enable) {
		this._listView.setNativeProps({scrollEnabled: enable});
	}

	safeCloseOpenRow() {
		// if the openCellId is stale due to deleting a row this could be undefined
		if (this._rows[this.openCellId]) {
			this._rows[this.openCellId].closeRow();
		}
	}

	rowSwipeGestureBegan(id) {
		if (this.props.closeOnRowBeginSwipe && this.openCellId && this.openCellId !== id) {
			this.safeCloseOpenRow();
		}
	}

	onRowOpen(secId, rowId, rowMap) {
		const cellIdentifier = `${secId}${rowId}`;
		if (this.openCellId && this.openCellId !== cellIdentifier) {
			this.safeCloseOpenRow();
		}
		this.openCellId = cellIdentifier;
		this.props.onRowOpen && this.props.onRowOpen(secId, rowId, rowMap);
	}

	onRowPress(id) {
		if (this.openCellId) {
			if (this.props.closeOnRowPress) {
				this.safeCloseOpenRow();
				this.openCellId = null;
			}
		}
	}

	onScroll(e) {
		if (this.openCellId) {
			if (this.props.closeOnScroll) {
				this.safeCloseOpenRow();
				this.openCellId = null;
			}
		}
		this.props.onScroll && this.props.onScroll(e);
	}

	setRefs(ref) {
		this._listView = ref;
		this.props.listViewRef && this.props.listViewRef(ref);
	}

	renderRow(rowData, secId, rowId, rowMap) {
		const Component = this.props.renderRow(rowData, secId, rowId, rowMap);
		if (!this.props.renderHiddenRow) {
			return React.cloneElement(
				Component,
				{
					...Component.props,
					ref: row => this._rows[`${secId}${rowId}`] = row,
					onRowOpen: _ => this.onRowOpen(secId, rowId, this._rows),
					onRowDidOpen: _ => this.props.onRowDidOpen && this.props.onRowDidOpen(secId, rowId, this._rows),
					onRowClose: _ => this.props.onRowClose && this.props.onRowClose(secId, rowId, this._rows),
					onRowDidClose: _ => this.props.onRowDidClose && this.props.onRowDidClose(secId, rowId, this._rows),
					onRowPress: _ => this.onRowPress(`${secId}${rowId}`),
					setScrollEnabled: enable => this.setScrollEnabled(enable),
					swipeGestureBegan: _ => this.rowSwipeGestureBegan(`${secId}${rowId}`)
				}
			);
		} else {
			const previewRowId = this.props.dataSource && this.props.dataSource.getRowIDForFlatIndex(this.props.previewRowIndex || 0);
			return (
				<SwipeRow
					ref={row => this._rows[`${secId}${rowId}`] = row}
					swipeGestureBegan={ _ => this.rowSwipeGestureBegan(`${secId}${rowId}`) }
					onRowOpen={ _ => this.onRowOpen(secId, rowId, this._rows) }
					onRowDidOpen={ _ => this.props.onRowDidOpen && this.props.onRowDidOpen(secId, rowId, this._rows)}
					onRowClose={ _ => this.props.onRowClose && this.props.onRowClose(secId, rowId, this._rows) }
					onRowDidClose={ _ => this.props.onRowDidClose && this.props.onRowDidClose(secId, rowId, this._rows) }
					onRowPress={ _ => this.onRowPress(`${secId}${rowId}`) }
					setScrollEnabled={ (enable) => this.setScrollEnabled(enable) }
					leftOpenValue={rowData.leftOpenValue || this.props.leftOpenValue}
					rightOpenValue={rowData.rightOpenValue || this.props.rightOpenValue}
					closeOnRowPress={rowData.closeOnRowPress || this.props.closeOnRowPress}
					disableLeftSwipe={rowData.disableLeftSwipe || this.props.disableLeftSwipe}
					disableRightSwipe={rowData.disableRightSwipe || this.props.disableRightSwipe}
					stopLeftSwipe={rowData.stopLeftSwipe || this.props.stopLeftSwipe}
					stopRightSwipe={rowData.stopRightSwipe || this.props.stopRightSwipe}
					recalculateHiddenLayout={this.props.recalculateHiddenLayout}
					style={this.props.swipeRowStyle}
					preview={(this.props.previewFirstRow || this.props.previewRowIndex) && rowId === previewRowId}
					previewDuration={this.props.previewDuration}
					previewOpenValue={this.props.previewOpenValue}
					tension={this.props.tension}
					friction={this.props.friction}
					directionalDistanceChangeThreshold={this.props.directionalDistanceChangeThreshold}
					swipeToOpenPercent={this.props.swipeToOpenPercent}
				>
					{this.props.renderHiddenRow(rowData, secId, rowId, this._rows)}
					{this.props.renderRow(rowData, secId, rowId, this._rows)}
				</SwipeRow>
			);
		}
	}

	render() {
		return (
			<ListView
				{...this.props}
				ref={ c => this.setRefs(c) }
				onScroll={ e => this.onScroll(e) }
				renderRow={(rowData, secId, rowId) => this.renderRow(rowData, secId, rowId, this._rows)}
			/>
		)
	}

}

SwipeListView.propTypes = {
	/**
	 * How to render a row. Should return a valid React Element.
	 */
	renderRow: PropTypes.func.isRequired,
	/**
	 * How to render a hidden row (renders behind the row). Should return a valid React Element.
	 * This is required unless renderRow is passing a SwipeRow.
	 */
	renderHiddenRow: PropTypes.func,
	/**
	 * TranslateX value for opening the row to the left (positive number)
	 */
	leftOpenValue: PropTypes.number,
	/**
	 * TranslateX value for opening the row to the right (negative number)
	 */
	rightOpenValue: PropTypes.number,
	/**
	 * TranslateX value for stop the row to the left (positive number)
	 */
	stopLeftSwipe: PropTypes.number,
	/**
	 * TranslateX value for stop the row to the right (negative number)
	 */
	stopRightSwipe: PropTypes.number,
	/**
	 * Should open rows be closed when the listView begins scrolling
	 */
	closeOnScroll: PropTypes.bool,
	/**
	 * Should open rows be closed when a row is pressed
	 */
	closeOnRowPress: PropTypes.bool,
	/**
	 * Should open rows be closed when a row begins to swipe open
	 */
	closeOnRowBeginSwipe: PropTypes.bool,
	/**
	 * Disable ability to swipe rows left
	 */
	disableLeftSwipe: PropTypes.bool,
	/**
	 * Disable ability to swipe rows right
	 */
	disableRightSwipe: PropTypes.bool,
	/**
	 * Enable hidden row onLayout calculations to run always.
	 *
	 * By default, hidden row size calculations are only done on the first onLayout event
	 * for performance reasons.
	 * Passing ```true``` here will cause calculations to run on every onLayout event.
	 * You may want to do this if your rows' sizes can change.
	 * One case is a SwipeListView with rows of different heights and an options to delete rows.
	 */
	recalculateHiddenLayout: PropTypes.bool,
	/**
	 * Called when a swipe row is animating open
	 */
	onRowOpen: PropTypes.func,
	/**
	 * Called when a swipe row has animated open
	 */
	onRowDidOpen: PropTypes.func,
	/**
	 * Called when a swipe row is animating closed
	 */
	onRowClose: PropTypes.func,
	/**
	 * Called when a swipe row has animated closed
	 */
	onRowDidClose: PropTypes.func,
	/**
	 * Styles for the parent wrapper View of the SwipeRow
	 */
	swipeRowStyle: View.propTypes.style,
	/**
	 * Called when the ListView ref is set and passes a ref to the ListView
	 * e.g. listViewRef={ ref => this._swipeListViewRef = ref }
	 */
	listViewRef: PropTypes.func,
	/**
	 * Should the first SwipeRow do a slide out preview to show that the list is swipeable
	 */
	previewFirstRow: PropTypes.bool,
	/**
	 * Should the specified rowId do a slide out preview to show that the list is swipeable
	 * Note: This ID will be passed to this function to get the correct row index
	 * https://facebook.github.io/react-native/docs/listviewdatasource.html#getrowidforflatindex
	 */
	previewRowIndex: PropTypes.number,
	/**
	 * Duration of the slide out preview animation (milliseconds)
	 */
	previewDuration: PropTypes.number,
	/**
	 * TranslateX value for the slide out preview animation
	 * Default: 0.5 * props.rightOpenValue
	 */
	previewOpenValue: PropTypes.number,
	/**
	 * Friction for the open / close animation
	 */
	friction: PropTypes.number,
	/**
	 * Tension for the open / close animation
	 */
	tension: PropTypes.number,
	/**
	 * The dx value used to detect when a user has begun a swipe gesture
	 */
	directionalDistanceChangeThreshold: PropTypes.number,
	/**
	 * What % of the left/right openValue does the user need to swipe
	 * past to trigger the row opening.
	 */
	swipeToOpenPercent: PropTypes.number,
}

SwipeListView.defaultProps = {
	leftOpenValue: 0,
	rightOpenValue: 0,
	closeOnRowBeginSwipe: false,
	closeOnScroll: true,
	closeOnRowPress: true,
	disableLeftSwipe: false,
	disableRightSwipe: false,
	recalculateHiddenLayout: false,
	previewFirstRow: false,
	directionalDistanceChangeThreshold: 2,
	swipeToOpenPercent: 50
}

export default SwipeListView;
<|endoftext|>


<commit_before>import React, {useEffect} from 'react';
import {Carousel, Image} from 'react-bootstrap';
import {Link} from 'react-router-dom';
import slideOne from '../../../images/slide_one.jpg';
import slideTwo from '../../../images/slide_two.jpg';
import slideThree from '../../../images/slide_three.jpg';
import {event} from '../../../lib/gtag.js';
import {CodeModal} from '../../../components/CodeModal/CodeModal.js';
import {getItemParameters, getEventCodeSnippet, getMeasureCodeSnippet} from '../../../utils.js';
import './HomeCarousel.css';

/**
 * Creates slideshow component for showcasing our latest deals and offerings.
 * @return {!JSX}
 */
export function HomeCarousel() {
  const viewPromotionParameters = {
    items: [
      {
        ...getItemParameters('7ba94'),
        promotion_name: 'Home Carousel',
      },
      {
        ...getItemParameters('hjdf7'),
        promotion_name: 'Home Carousel',
        discount: 25,
      },
      {
        ...getItemParameters('3h488'),
        promotion_name: 'Home Carousel',
      },
    ],
    promotion_name: 'Home Carousel',
  };

  // Send view_promotion event when the carousel loads for the first time.
  useEffect(() => {
    event('view_promotion', viewPromotionParameters);
  }, []);

  const carouselCodeModal =
      <CodeModal
        popupId="view_promotion"
        gtagCode={
          getEventCodeSnippet('view_promotion', viewPromotionParameters)
        }
        measureCode={getMeasureCodeSnippet()}
      />;

  /**
   * Collects a single promotional item's standard parameters and
   * returns it as an object with items attribute mapped to
   * the items array containing a single promotional item.
   * @param {string} itemId
   * @param {number=} discount Price discount of item (if any)
   * @return {{items: Array<!ItemParameters>}} An object containing
   *     an items attibute mapped to an array of {@link ItemParameters}
   */
  function getSelectPromotionParameters(itemId, discount = 0) {
    return {
      items: [
        {
          ...getItemParameters(itemId),
          promotion_name: 'Home Carousel',
          // if discount is positive, include it in the item parameter
          ...(discount > 0 && {discount}),
        },
      ],
    };
  }

  /**
   * Send select_promotion event to Google Analytics when a carousel
   * slide promotional item is selected.
   * @param {string} itemId
   * @param {number=} discount Price discount of item (if any)
   */
  function onSlideClick(itemId, discount = 0) {
    event('select_promotion', getSelectPromotionParameters(itemId, discount));
  }

  /**
   * Creates a select_promotion event code modal with code snippets
   * for gtag and measure libraries.
   * @param {string} itemId
   * @param {number=} discount Price discount of item (if any)
   * @return {!JSX} Code snippet of the event.
   */
  function getSelectPromotionCodeModal(itemId, discount = 0) {
    return (
      <CodeModal
        popupId="view_promotion"
        gtagCode={
          getEventCodeSnippet(
              'select_promotion',
              getSelectPromotionParameters(itemId, discount),
          )
        }
        measureCode={getMeasureCodeSnippet()}
      />
    );
  }

  return (
    <Carousel>
      <Carousel.Item>
        <Image
          className="d-block w-100"
          src={slideOne}
          alt="First slide"
        />
        <Carousel.Caption>

              Stunning High Quality Prints Available
              {carouselCodeModal}

{`View our `}
              <Link
                onClick={() => {
                  onSlideClick('7ba94');
                }}
                to="/product/7ba94"
              >
              latest piece
              </Link>

            {getSelectPromotionCodeModal('7ba94')}

        </Carousel.Caption>
      </Carousel.Item>
      <Carousel.Item>
        <Image
          className="d-block w-100"
          src={slideTwo}
          alt="Third slide"
        />
        <Carousel.Caption>

              SALE!

            {carouselCodeModal}

{`Get this classic 50% off. `}
              <Link
                onClick={() => {
                  onSlideClick('hjdf7', 25);
                }}
                to="/product/hjdf7"
              >
              View Deal
              </Link>

            {getSelectPromotionCodeModal('hjdf7', 25)}

        </Carousel.Caption>
      </Carousel.Item>
      <Carousel.Item>
        <Image
          className="d-block w-100"
          src={slideThree}
          alt="Third slide"
        />
        <Carousel.Caption>

              Toe Beans

            {carouselCodeModal}

              {`Seen like never before. `}
              <Link
                onClick={() => {
                  onSlideClick('3h488');
                }}
                to="/product/3h488"
              >
                Buy Now
              </Link>

            {getSelectPromotionCodeModal('3h488')}

        </Carousel.Caption>
      </Carousel.Item>
    </Carousel>
  );
}
<commit_msg>Git rid of incorrect parameter at top level<commit_after>import React, {useEffect} from 'react';
import {Carousel, Image} from 'react-bootstrap';
import {Link} from 'react-router-dom';
import slideOne from '../../../images/slide_one.jpg';
import slideTwo from '../../../images/slide_two.jpg';
import slideThree from '../../../images/slide_three.jpg';
import {event} from '../../../lib/gtag.js';
import {CodeModal} from '../../../components/CodeModal/CodeModal.js';
import {getItemParameters, getEventCodeSnippet, getMeasureCodeSnippet} from '../../../utils.js';
import './HomeCarousel.css';

/**
 * Creates slideshow component for showcasing our latest deals and offerings.
 * @return {!JSX}
 */
export function HomeCarousel() {
  const viewPromotionParameters = {
    items: [
      {
        ...getItemParameters('7ba94'),
        promotion_name: 'Home Carousel',
      },
      {
        ...getItemParameters('hjdf7'),
        promotion_name: 'Home Carousel',
        discount: 25,
      },
      {
        ...getItemParameters('3h488'),
        promotion_name: 'Home Carousel',
      },
    ],
  };

  // Send view_promotion event when the carousel loads for the first time.
  useEffect(() => {
    event('view_promotion', viewPromotionParameters);
  }, []);

  const carouselCodeModal =
      <CodeModal
        popupId="view_promotion"
        gtagCode={
          getEventCodeSnippet('view_promotion', viewPromotionParameters)
        }
        measureCode={getMeasureCodeSnippet()}
      />;

  /**
   * Collects a single promotional item's standard parameters and
   * returns it as an object with items attribute mapped to
   * the items array containing a single promotional item.
   * @param {string} itemId
   * @param {number=} discount Price discount of item (if any)
   * @return {{items: Array<!ItemParameters>}} An object containing
   *     an items attibute mapped to an array of {@link ItemParameters}
   */
  function getSelectPromotionParameters(itemId, discount = 0) {
    return {
      items: [
        {
          ...getItemParameters(itemId),
          promotion_name: 'Home Carousel',
          // if discount is positive, include it in the item parameter
          ...(discount > 0 && {discount}),
        },
      ],
    };
  }

  /**
   * Send select_promotion event to Google Analytics when a carousel
   * slide promotional item is selected.
   * @param {string} itemId
   * @param {number=} discount Price discount of item (if any)
   */
  function onSlideClick(itemId, discount = 0) {
    event('select_promotion', getSelectPromotionParameters(itemId, discount));
  }

  /**
   * Creates a select_promotion event code modal with code snippets
   * for gtag and measure libraries.
   * @param {string} itemId
   * @param {number=} discount Price discount of item (if any)
   * @return {!JSX} Code snippet of the event.
   */
  function getSelectPromotionCodeModal(itemId, discount = 0) {
    return (
      <CodeModal
        popupId="view_promotion"
        gtagCode={
          getEventCodeSnippet(
              'select_promotion',
              getSelectPromotionParameters(itemId, discount),
          )
        }
        measureCode={getMeasureCodeSnippet()}
      />
    );
  }

  return (
    <Carousel>
      <Carousel.Item>
        <Image
          className="d-block w-100"
          src={slideOne}
          alt="First slide"
        />
        <Carousel.Caption>

              Stunning High Quality Prints Available
              {carouselCodeModal}

{`View our `}
              <Link
                onClick={() => {
                  onSlideClick('7ba94');
                }}
                to="/product/7ba94"
              >
              latest piece
              </Link>

            {getSelectPromotionCodeModal('7ba94')}

        </Carousel.Caption>
      </Carousel.Item>
      <Carousel.Item>
        <Image
          className="d-block w-100"
          src={slideTwo}
          alt="Third slide"
        />
        <Carousel.Caption>

              SALE!

            {carouselCodeModal}

{`Get this classic 50% off. `}
              <Link
                onClick={() => {
                  onSlideClick('hjdf7', 25);
                }}
                to="/product/hjdf7"
              >
              View Deal
              </Link>

            {getSelectPromotionCodeModal('hjdf7', 25)}

        </Carousel.Caption>
      </Carousel.Item>
      <Carousel.Item>
        <Image
          className="d-block w-100"
          src={slideThree}
          alt="Third slide"
        />
        <Carousel.Caption>

              Toe Beans

            {carouselCodeModal}

              {`Seen like never before. `}
              <Link
                onClick={() => {
                  onSlideClick('3h488');
                }}
                to="/product/3h488"
              >
                Buy Now
              </Link>

            {getSelectPromotionCodeModal('3h488')}

        </Carousel.Caption>
      </Carousel.Item>
    </Carousel>
  );
}
<|endoftext|>


<commit_before>[
{"id": "align_relative_to", "title": "Align relative to ..."},
{"id": "angle", "title": "Change rotation angle"},
{"id": "angleLabel", "textContent": "angle:"},
{"id": "bkgnd_color", "title": "Change background color/opacity"},
{"id": "circle_cx", "title": "Change circle's cx coordinate"},
{"id": "circle_cy", "title": "Change circle's cy coordinate"},
{"id": "circle_r", "title": "Change circle's radius"},
{"id": "cornerRadiusLabel", "textContent": "Corner Radius:"},
{"id": "curve_segments", "textContent": "Curve"},
{"id": "ellipse_cx", "title": "Change ellipse's cx coordinate"},
{"id": "ellipse_cy", "title": "Change ellipse's cy coordinate"},
{"id": "ellipse_rx", "title": "Change ellipse's x radius"},
{"id": "ellipse_ry", "title": "Change ellipse's y radius"},
{"id": "fill_color", "title": "Change fill color"},
{"id": "fill_tool_bottom", "textContent": "fill:"},
{"id": "fitToContent", "textContent": "Fit to Content"},
{"id": "fit_to_all", "textContent": "Fit to all content"},
{"id": "fit_to_canvas", "textContent": "Fit to canvas"},
{"id": "fit_to_layer_content", "textContent": "Fit to layer content"},
{"id": "fit_to_sel", "textContent": "Fit to selection"},
{"id": "font_family", "title": "Change Font Family"},
{"id": "font_size", "title": "Change Font Size"},
{"id": "group_opacity", "title": "Change selected item opacity"},
{"id": "icon_large", "textContent": "Large"},
{"id": "icon_medium", "textContent": "Medium"},
{"id": "icon_small", "textContent": "Small"},
{"id": "icon_xlarge", "textContent": "Extra Large"},
{"id": "iheightLabel", "textContent": "height:"},
{"id": "image_height", "title": "Change image height"},
{"id": "image_opt_embed", "textContent": "Embed data (local files)"},
{"id": "image_opt_ref", "textContent": "Use file reference"},
{"id": "image_url", "title": "Change URL"},
{"id": "image_width", "title": "Change image width"},
{"id": "includedImages", "textContent": "Included Images"},
{"id": "iwidthLabel", "textContent": "width:"},
{"id": "largest_object", "textContent": "largest object"},
{"id": "layer_delete", "title": "Delete Layer"},
{"id": "layer_down", "title": "Move Layer Down"},
{"id": "layer_new", "title": "New Layer"},
{"id": "layer_rename", "title": "Rename Layer"},
{"id": "layer_up", "title": "Move Layer Up"},
{"id": "layersLabel", "textContent": "Layers:"},
{"id": "line_x1", "title": "Change line's starting x coordinate"},
{"id": "line_x2", "title": "Change line's ending x coordinate"},
{"id": "line_y1", "title": "Change line's starting y coordinate"},
{"id": "line_y2", "title": "Change line's ending y coordinate"},
{"id": "page", "textContent": "page"},
{"id": "palette", "title": "Click to change fill color, shift-click to change stroke color"},
{"id": "path_node_x", "title": "Change node's x coordinate"},
{"id": "path_node_y", "title": "Change node's y coordinate"},
{"id": "rect_height_tool", "title": "Change rectangle height"},
{"id": "rect_rx", "title": "Change Rectangle Corner Radius"},
{"id": "rect_width_tool", "title": "Change rectangle width"},
{"id": "relativeToLabel", "textContent": "relative to:"},
{"id": "rheightLabel", "textContent": "height:"},
{"id": "rwidthLabel", "textContent": "width:"},
{"id": "seg_type", "title": "Change Segment type"},
{"id": "selLayerLabel", "textContent": "Move elements to:"},
{"id": "selLayerNames", "title": "Move selected elements to a different layer"},
{"id": "selectedPredefined", "textContent": "Select predefined:"},
{"id": "selected_objects", "textContent": "selected objects"},
{"id": "selected_x", "title": "Change X coordinate"},
{"id": "selected_y", "title": "Change Y coordinate"},
{"id": "smallest_object", "textContent": "smallest object"},
{"id": "straight_segments", "textContent": "Straight"},
{"id": "stroke_color", "title": "Change stroke color"},
{"id": "stroke_style", "title": "Change stroke dash style"},
{"id": "stroke_tool_bottom", "textContent": "stroke:"},
{"id": "stroke_width", "title": "Change stroke width by 1, shift-click to change by 0.1"},
{"id": "svginfo_bg_note", "textContent": "Note: Background will not be saved with image."},
{"id": "svginfo_change_background", "textContent": "Editor Background"},
{"id": "svginfo_dim", "textContent": "Canvas Dimensions"},
{"id": "svginfo_editor_prefs", "textContent": "Editor Preferences"},
{"id": "svginfo_height", "textContent": "Height:"},
{"id": "svginfo_icons", "textContent": "Icon size"},
{"id": "svginfo_image_props", "textContent": "Image Properties"},
{"id": "svginfo_lang", "textContent": "Language"},
{"id": "svginfo_title", "textContent": "Title"},
{"id": "svginfo_width", "textContent": "Width:"},
{"id": "text", "title": "Change text contents"},
{"id": "tool_alignbottom", "title": "Align Bottom"},
{"id": "tool_aligncenter", "title": "Align Center"},
{"id": "tool_alignleft", "title": "Align Left"},
{"id": "tool_alignmiddle", "title": "Align Middle"},
{"id": "tool_alignright", "title": "Align Right"},
{"id": "tool_aligntop", "title": "Align Top"},
{"id": "tool_blur", "title": "Change gaussian blur value"},
{"id": "tool_bold", "title": "Bold Text"},
{"id": "tool_circle", "title": "Circle"},
{"id": "tool_clear", "textContent": "New Image"},
{"id": "tool_clone", "title": "Clone Element"},
{"id": "tool_clone_multi", "title": "Clone Elements"},
{"id": "tool_delete", "title": "Delete Element"},
{"id": "tool_delete_multi", "title": "Delete Selected Elements"},
{"id": "tool_docprops", "textContent": "Document Properties"},
{"id": "tool_docprops_cancel", "textContent": "Cancel"},
{"id": "tool_docprops_save", "textContent": "OK"},
{"id": "tool_ellipse", "title": "Ellipse"},
{"id": "tool_fhellipse", "title": "Free-Hand Ellipse"},
{"id": "tool_fhpath", "title": "Pencil Tool"},
{"id": "tool_fhrect", "title": "Free-Hand Rectangle"},
{"id": "tool_group", "title": "Group Elements"},
{"id": "tool_image", "title": "Image Tool"},
{"id": "tool_import", "textContent": "Import Image"},
{"id": "tool_italic", "title": "Italic Text"},
{"id": "tool_line", "title": "Line Tool"},
{"id": "tool_move_bottom", "title": "Move to Bottom"},
{"id": "tool_move_top", "title": "Move to Top"},
{"id": "tool_node_clone", "title": "Clone Node"},
{"id": "tool_node_delete", "title": "Delete Node"},
{"id": "tool_node_link", "title": "Link Control Points"},
{"id": "tool_open", "textContent": "Open Image"},
{"id": "tool_path", "title": "Path Tool"},
{"id": "tool_rect", "title": "Rectangle"},
{"id": "tool_redo", "title": "Redo"},
{"id": "tool_reorient", "title": "Reorient path"},
{"id": "tool_save", "textContent": "Save Image"},
{"id": "tool_select", "title": "Select Tool"},
{"id": "tool_source", "title": "Edit Source"},
{"id": "tool_source_cancel", "textContent": "Cancel"},
{"id": "tool_source_save", "textContent": "Apply Changes"},
{"id": "tool_square", "title": "Square"},
{"id": "tool_text", "title": "Text Tool"},
{"id": "tool_topath", "title": "Convert to Path"},
{"id": "tool_undo", "title": "Undo"},
{"id": "tool_ungroup", "title": "Ungroup Elements"},
{"id": "tool_wireframe", "title": "Wireframe Mode"},
{"id": "tool_zoom", "title": "Zoom Tool"},
{"id": "zoom", "title": "Change zoom level"},
{"id": "zoomLabel", "textContent": "zoom:"},
{"id": "sidepanel_handle", "textContent": "L a y e r s", "title": "Drag left/right to resize side panel"},
{
 "js_strings": {
  "QerrorsRevertToSource": "There were parsing errors in your SVG source.\nRevert back to original SVG source?",
  "QignoreSourceChanges": "Ignore changes made to SVG source?",
  "QmoveElemsToLayer": "Move selected elements to layer '%s'?",
  "QwantToClear": "Do you want to clear the drawing?\nThis will also erase your undo history!",
  "cancel": "Cancel",
  "dupeLayerName": "There is already a layer named that!",
  "enterNewImgURL": "Enter the new image URL",
  "enterNewLayerName": "Please enter the new layer name",
  "enterUniqueLayerName": "Please enter a unique layer name",
  "featNotSupported": "Feature not supported",
  "invalidAttrValGiven": "Invalid value given",
  "key_backspace": "backspace",
  "key_del": "delete",
  "key_down": "down",
  "key_up": "up",
  "layer": "Layer",
  "layerHasThatName": "Layer already has that name",
  "noContentToFitTo": "No content to fit to",
  "ok": "OK",
  "pathCtrlPtTooltip": "Drag control point to adjust curve properties",
  "pathNodeTooltip": "Drag node to move it. Double-click node to change segment type"
 }
}
]<commit_msg>Add some missing tooltips to English translation<commit_after>[
{"id": "align_relative_to", "title": "Align relative to ..."},
{"id": "angle", "title": "Change rotation angle"},
{"id": "angleLabel", "textContent": "angle:"},
{"id": "bkgnd_color", "title": "Change background color/opacity"},
{"id": "circle_cx", "title": "Change circle's cx coordinate"},
{"id": "circle_cy", "title": "Change circle's cy coordinate"},
{"id": "circle_r", "title": "Change circle's radius"},
{"id": "cornerRadiusLabel", "textContent": "Corner Radius:"},
{"id": "curve_segments", "textContent": "Curve"},
{"id": "ellipse_cx", "title": "Change ellipse's cx coordinate"},
{"id": "ellipse_cy", "title": "Change ellipse's cy coordinate"},
{"id": "ellipse_rx", "title": "Change ellipse's x radius"},
{"id": "ellipse_ry", "title": "Change ellipse's y radius"},
{"id": "fill_color", "title": "Change fill color"},
{"id": "fill_tool_bottom", "textContent": "fill:"},
{"id": "fitToContent", "textContent": "Fit to Content"},
{"id": "fit_to_all", "textContent": "Fit to all content"},
{"id": "fit_to_canvas", "textContent": "Fit to canvas"},
{"id": "fit_to_layer_content", "textContent": "Fit to layer content"},
{"id": "fit_to_sel", "textContent": "Fit to selection"},
{"id": "font_family", "title": "Change Font Family"},
{"id": "font_size", "title": "Change Font Size"},
{"id": "group_opacity", "title": "Change selected item opacity"},
{"id": "icon_large", "textContent": "Large"},
{"id": "icon_medium", "textContent": "Medium"},
{"id": "icon_small", "textContent": "Small"},
{"id": "icon_xlarge", "textContent": "Extra Large"},
{"id": "iheightLabel", "textContent": "height:"},
{"id": "image_height", "title": "Change image height"},
{"id": "image_opt_embed", "textContent": "Embed data (local files)"},
{"id": "image_opt_ref", "textContent": "Use file reference"},
{"id": "image_url", "title": "Change URL"},
{"id": "image_width", "title": "Change image width"},
{"id": "includedImages", "textContent": "Included Images"},
{"id": "iwidthLabel", "textContent": "width:"},
{"id": "largest_object", "textContent": "largest object"},
{"id": "layer_delete", "title": "Delete Layer"},
{"id": "layer_down", "title": "Move Layer Down"},
{"id": "layer_new", "title": "New Layer"},
{"id": "layer_rename", "title": "Rename Layer"},
{"id": "layer_up", "title": "Move Layer Up"},
{"id": "layersLabel", "textContent": "Layers:"},
{"id": "line_x1", "title": "Change line's starting x coordinate"},
{"id": "line_x2", "title": "Change line's ending x coordinate"},
{"id": "line_y1", "title": "Change line's starting y coordinate"},
{"id": "line_y2", "title": "Change line's ending y coordinate"},
{"id": "page", "textContent": "page"},
{"id": "palette", "title": "Click to change fill color, shift-click to change stroke color"},
{"id": "path_node_x", "title": "Change node's x coordinate"},
{"id": "path_node_y", "title": "Change node's y coordinate"},
{"id": "rect_height_tool", "title": "Change rectangle height"},
{"id": "rect_rx", "title": "Change Rectangle Corner Radius"},
{"id": "rect_width_tool", "title": "Change rectangle width"},
{"id": "relativeToLabel", "textContent": "relative to:"},
{"id": "rheightLabel", "textContent": "height:"},
{"id": "rwidthLabel", "textContent": "width:"},
{"id": "seg_type", "title": "Change Segment type"},
{"id": "selLayerLabel", "textContent": "Move elements to:"},
{"id": "selLayerNames", "title": "Move selected elements to a different layer"},
{"id": "selectedPredefined", "textContent": "Select predefined:"},
{"id": "selected_objects", "textContent": "selected objects"},
{"id": "selected_x", "title": "Change X coordinate"},
{"id": "selected_y", "title": "Change Y coordinate"},
{"id": "smallest_object", "textContent": "smallest object"},
{"id": "straight_segments", "textContent": "Straight"},
{"id": "stroke_color", "title": "Change stroke color"},
{"id": "stroke_style", "title": "Change stroke dash style"},
{"id": "stroke_tool_bottom", "textContent": "stroke:"},
{"id": "stroke_width", "title": "Change stroke width by 1, shift-click to change by 0.1"},
{"id": "svginfo_bg_note", "textContent": "Note: Background will not be saved with image."},
{"id": "svginfo_change_background", "textContent": "Editor Background"},
{"id": "svginfo_dim", "textContent": "Canvas Dimensions"},
{"id": "svginfo_editor_prefs", "textContent": "Editor Preferences"},
{"id": "svginfo_height", "textContent": "Height:"},
{"id": "svginfo_icons", "textContent": "Icon size"},
{"id": "svginfo_image_props", "textContent": "Image Properties"},
{"id": "svginfo_lang", "textContent": "Language"},
{"id": "svginfo_title", "textContent": "Title"},
{"id": "svginfo_width", "textContent": "Width:"},
{"id": "text", "title": "Change text contents"},
{"id": "tool_add_subpath", "title": "Add sub-path"},
{"id": "tool_alignbottom", "title": "Align Bottom"},
{"id": "tool_aligncenter", "title": "Align Center"},
{"id": "tool_alignleft", "title": "Align Left"},
{"id": "tool_alignmiddle", "title": "Align Middle"},
{"id": "tool_alignright", "title": "Align Right"},
{"id": "tool_aligntop", "title": "Align Top"},
{"id": "tool_blur", "title": "Change gaussian blur value"},
{"id": "tool_bold", "title": "Bold Text"},
{"id": "tool_circle", "title": "Circle"},
{"id": "tool_clear", "textContent": "New Image"},
{"id": "tool_clone", "title": "Clone Element"},
{"id": "tool_clone_multi", "title": "Clone Elements"},
{"id": "tool_delete", "title": "Delete Element"},
{"id": "tool_delete_multi", "title": "Delete Selected Elements"},
{"id": "tool_docprops", "textContent": "Document Properties"},
{"id": "tool_docprops_cancel", "textContent": "Cancel"},
{"id": "tool_docprops_save", "textContent": "OK"},
{"id": "tool_ellipse", "title": "Ellipse"},
{"id": "tool_fhellipse", "title": "Free-Hand Ellipse"},
{"id": "tool_fhpath", "title": "Pencil Tool"},
{"id": "tool_fhrect", "title": "Free-Hand Rectangle"},
{"id": "tool_group", "title": "Group Elements"},
{"id": "tool_image", "title": "Image Tool"},
{"id": "tool_import", "textContent": "Import Image"},
{"id": "tool_italic", "title": "Italic Text"},
{"id": "tool_line", "title": "Line Tool"},
{"id": "tool_move_bottom", "title": "Move to Bottom"},
{"id": "tool_move_top", "title": "Move to Top"},
{"id": "tool_node_clone", "title": "Clone Node"},
{"id": "tool_node_delete", "title": "Delete Node"},
{"id": "tool_node_link", "title": "Link Control Points"},
{"id": "tool_open", "textContent": "Open Image"},
{"id": "tool_openclose_path", "textContent": "Open/close sub-path"},
{"id": "tool_path", "title": "Path Tool"},
{"id": "tool_rect", "title": "Rectangle"},
{"id": "tool_redo", "title": "Redo"},
{"id": "tool_reorient", "title": "Reorient path"},
{"id": "tool_save", "textContent": "Save Image"},
{"id": "tool_select", "title": "Select Tool"},
{"id": "tool_source", "title": "Edit Source"},
{"id": "tool_source_cancel", "textContent": "Cancel"},
{"id": "tool_source_save", "textContent": "Apply Changes"},
{"id": "tool_square", "title": "Square"},
{"id": "tool_text", "title": "Text Tool"},
{"id": "tool_topath", "title": "Convert to Path"},
{"id": "tool_undo", "title": "Undo"},
{"id": "tool_ungroup", "title": "Ungroup Elements"},
{"id": "tool_wireframe", "title": "Wireframe Mode"},
{"id": "tool_zoom", "title": "Zoom Tool"},
{"id": "zoom", "title": "Change zoom level"},
{"id": "zoomLabel", "textContent": "zoom:"},
{"id": "sidepanel_handle", "textContent": "L a y e r s", "title": "Drag left/right to resize side panel"},
{
 "js_strings": {
  "QerrorsRevertToSource": "There were parsing errors in your SVG source.\nRevert back to original SVG source?",
  "QignoreSourceChanges": "Ignore changes made to SVG source?",
  "QmoveElemsToLayer": "Move selected elements to layer '%s'?",
  "QwantToClear": "Do you want to clear the drawing?\nThis will also erase your undo history!",
  "cancel": "Cancel",
  "dupeLayerName": "There is already a layer named that!",
  "enterNewImgURL": "Enter the new image URL",
  "enterNewLayerName": "Please enter the new layer name",
  "enterUniqueLayerName": "Please enter a unique layer name",
  "featNotSupported": "Feature not supported",
  "invalidAttrValGiven": "Invalid value given",
  "key_backspace": "backspace",
  "key_del": "delete",
  "key_down": "down",
  "key_up": "up",
  "layer": "Layer",
  "layerHasThatName": "Layer already has that name",
  "noContentToFitTo": "No content to fit to",
  "ok": "OK",
  "pathCtrlPtTooltip": "Drag control point to adjust curve properties",
  "pathNodeTooltip": "Drag node to move it. Double-click node to change segment type"
 }
}
]<|endoftext|>