<commit_before>// ID of the (hidden) form field used to store the JSON representation of the
// object being edited in this page
var sJsonFieldId = 'json_object';

// The memory representation of the object
var oObj = {};

// Mapping between the fields of the form and the attribute of the current object
// If aFieldsMap[2] contains 'foo' it means that oObj.foo corresponds to the field
// of Id 'att_2' in the form
var aFieldsMap = new Array;

// Update the whole object from the form and also update its
// JSON (serialized) representation in the (hidden) field
function UpdateObjectFromForm(aFieldsMap, oObj)
{
	for(i=0; i<aFieldsMap.length; i++)
	{
		var oElement = document.getElementById('att_'+i);
		var sFieldName = aFieldsMap[i];
		oObj['m_aCurrValues'][sFieldName] = oElement.value;
		sJSON = JSON.stringify(oObj);
		var oJSON = document.getElementById(sJsonFieldId);
		oJSON.value = sJSON;
	}
	return oObj;
}

// Update the specified field from the current object
function UpdateFieldFromObject(idField, aFieldsMap, oObj)
{
	var oElement = document.getElementById('att_'+idField);
	oElement.value = oObj['m_aCurrValues'][aFieldsMap[idField]];
}
// Update all the fields of the Form from the current object
function UpdateFormFromObject(aFieldsMap, oObj)
{
	for(i=0; i<aFieldsMap.length; i++)
	{
		UpdateFieldFromForm(i, aFieldsMap, oObj);
	}
}

// This function is meant to be called from the AJAX page
// It reloads the object (oObj) from the JSON representation
// and also updates the form field that contains the JSON
// representation of the object
function ReloadObjectFromServer(sJSON)
{
	//console.log('JSON value:', sJSON);
	var oJSON = document.getElementById(sJsonFieldId);
	oJSON.value = sJSON;
	oObj = JSON.parse( '(' + sJSON + ')' );
	return oObj;
}

function GoToStep(iCurrentStep, iNextStep)
{
	var oCurrentStep = document.getElementById('wizStep'+iCurrentStep);
	if (iNextStep > iCurrentStep)
	{
		// Check the values when moving forward
		if (CheckFields('wizStep'+iCurrentStep))
		{
			oCurrentStep.style.display = 'none';
			ActivateStep(iNextStep);
		}
	}
	else
	{
		oCurrentStep.style.display = 'none';
		ActivateStep(iNextStep);
	}
}

function ActivateStep(iTargetStep)
{
	UpdateObjectFromForm(aFieldsMap, oObj);
	var oNextStep = document.getElementById('wizStep'+(iTargetStep));
	window.location.href='#step'+iTargetStep;
	// If a handler for entering this step exists, call it
	if (typeof(this['OnEnterStep'+iTargetStep]) == 'function')
	{
		eval( 'OnEnterStep'+iTargetStep+'();');
	}
	oNextStep.style.display = '';
	G_iCurrentStep = iTargetStep;
	//$('#wizStep'+(iTargetStep)).block({ message: null });
}

//function AjaxGetValuesDef(oObj, sClass, sAttCode, iFieldId)
//{
//	var oJSON = document.getElementById(sJsonFieldId);
//	$.get('ajax.render.php?class=' + sClass + '&json_obj=' + oJSON.value + '&att_code=' + sAttCode,
//	   { operation: "allowed_values" },
//	   function(data){
//		 //$('#field_'+iFieldId).html(data);
//		}
//	);
//}
//
//function AjaxGetDefaultValue(oObj, sClass, sAttCode, iFieldId)
//{
//	// Asynchronously call the server to provide a default value if the field is
//	// empty
//	if (oObj['m_aCurrValues'][sAttCode] == '')
//	{
//		var oJSON = document.getElementById(sJsonFieldId);
//		$.get('ajax.render.php?class=' + sClass + '&json_obj=' + oJSON.value + '&att_code=' + sAttCode,
//		   { operation: "default_value" },
//		   function(json_data){
//			 var oObj = ReloadObjectFromServer(json_data);
//			 UpdateFieldFromObject(iFieldId, aFieldsMap, oObj)
//			}
//		);
//	}
//}

// Store the result of the form validation... there may be several forms per page, beware
var oFormErrors = { err_form0: 0 };

function CheckFields(sFormId)
{
	$('#'+sFormId+' :submit').attr('disable', 'disabled');
	$('#'+sFormId+' :button[type=submit]').attr('disable', 'disabled');
	firstErrorId = '';

	// The two 'fields' below will be updated when the 'validate' event is processed
	oFormErrors['err_'+sFormId] = 0;		// Number of errors encountered when validating the form
	oFormErrors['input_'+sFormId] = null;	// First 'input' with an error, to set the focus to it
	$('#'+sFormId+' :input').each( function()
	{
		validateEventResult = $(this).trigger('validate', sFormId);
	}
	);
	if(oFormErrors['err_'+sFormId] > 0)
	{
		alert('Please fill-in all mandatory fields before continuing.');
		$('#'+sFormId+' :submit').attr('disable', '');
		$('#'+sFormId+' :button[type=submit]').attr('disable', '');
		if (oFormErrors['input_'+sFormId] != null)
		{
			$('#'+oFormErrors['input_'+sFormId]).focus();
		}
	}
	return (oFormErrors['err_'+sFormId] == 0); // If no error, submit the form
}

function ValidateField(sFieldId, sPattern, bMandatory, sFormId)
{
	var bValid = true;
	var currentVal = $('#'+sFieldId).val();
	if (bMandatory && ((currentVal == '') || (currentVal == 0)))
	{
		bValid = false;
	}
	else if (sPattern != '')
	{
		re = new RegExp(sPattern);
		//console.log('Validating field: '+sFieldId + ' current value: '+currentVal + ' pattern: '+sPattern );
		bValid = re.test(currentVal);
	}
	if (bValid)
	{
		// Visual feedback
		$('#v_'+sFieldId).html('');
	}
	else
	{
		// Report the error...
		oFormErrors['err_'+sFormId]++;
		if (oFormErrors['input_'+sFormId] == null)
		{
			// Let's remember the first input with an error, so that we can put back the focus on it later
			oFormErrors['input_'+sFormId] = sFieldId;
		}
		// Visual feedback
		$('#v_'+sFieldId).html('');
	}
	//console.log('Form: '+sFormId+' Validating field: '+sFieldId + ' current value: '+currentVal+' pattern: '+sPattern+' result: '+bValid );
	return bValid;
}

function UpdateDependentFields(aFieldNames)
{
	//console.log('UpdateDependentFields:');
	//console.log(aFieldNames);
	index = 0;
	oWizardHelper.ResetQuery();
	oWizardHelper.UpdateWizard();
	while(index < aFieldNames.length )
	{
		sAttCode = aFieldNames[index];
		oWizardHelper.RequestAllowedValues(sAttCode);
		index++;
	}
	oWizardHelper.AjaxQueryServer();
}
<commit_msg>- Non mandatory fields can be empty, even if a validation pattern was specified.<commit_after>// ID of the (hidden) form field used to store the JSON representation of the
// object being edited in this page
var sJsonFieldId = 'json_object';

// The memory representation of the object
var oObj = {};

// Mapping between the fields of the form and the attribute of the current object
// If aFieldsMap[2] contains 'foo' it means that oObj.foo corresponds to the field
// of Id 'att_2' in the form
var aFieldsMap = new Array;

// Update the whole object from the form and also update its
// JSON (serialized) representation in the (hidden) field
function UpdateObjectFromForm(aFieldsMap, oObj)
{
	for(i=0; i<aFieldsMap.length; i++)
	{
		var oElement = document.getElementById('att_'+i);
		var sFieldName = aFieldsMap[i];
		oObj['m_aCurrValues'][sFieldName] = oElement.value;
		sJSON = JSON.stringify(oObj);
		var oJSON = document.getElementById(sJsonFieldId);
		oJSON.value = sJSON;
	}
	return oObj;
}

// Update the specified field from the current object
function UpdateFieldFromObject(idField, aFieldsMap, oObj)
{
	var oElement = document.getElementById('att_'+idField);
	oElement.value = oObj['m_aCurrValues'][aFieldsMap[idField]];
}
// Update all the fields of the Form from the current object
function UpdateFormFromObject(aFieldsMap, oObj)
{
	for(i=0; i<aFieldsMap.length; i++)
	{
		UpdateFieldFromForm(i, aFieldsMap, oObj);
	}
}

// This function is meant to be called from the AJAX page
// It reloads the object (oObj) from the JSON representation
// and also updates the form field that contains the JSON
// representation of the object
function ReloadObjectFromServer(sJSON)
{
	//console.log('JSON value:', sJSON);
	var oJSON = document.getElementById(sJsonFieldId);
	oJSON.value = sJSON;
	oObj = JSON.parse( '(' + sJSON + ')' );
	return oObj;
}

function GoToStep(iCurrentStep, iNextStep)
{
	var oCurrentStep = document.getElementById('wizStep'+iCurrentStep);
	if (iNextStep > iCurrentStep)
	{
		// Check the values when moving forward
		if (CheckFields('wizStep'+iCurrentStep))
		{
			oCurrentStep.style.display = 'none';
			ActivateStep(iNextStep);
		}
	}
	else
	{
		oCurrentStep.style.display = 'none';
		ActivateStep(iNextStep);
	}
}

function ActivateStep(iTargetStep)
{
	UpdateObjectFromForm(aFieldsMap, oObj);
	var oNextStep = document.getElementById('wizStep'+(iTargetStep));
	window.location.href='#step'+iTargetStep;
	// If a handler for entering this step exists, call it
	if (typeof(this['OnEnterStep'+iTargetStep]) == 'function')
	{
		eval( 'OnEnterStep'+iTargetStep+'();');
	}
	oNextStep.style.display = '';
	G_iCurrentStep = iTargetStep;
	//$('#wizStep'+(iTargetStep)).block({ message: null });
}

//function AjaxGetValuesDef(oObj, sClass, sAttCode, iFieldId)
//{
//	var oJSON = document.getElementById(sJsonFieldId);
//	$.get('ajax.render.php?class=' + sClass + '&json_obj=' + oJSON.value + '&att_code=' + sAttCode,
//	   { operation: "allowed_values" },
//	   function(data){
//		 //$('#field_'+iFieldId).html(data);
//		}
//	);
//}
//
//function AjaxGetDefaultValue(oObj, sClass, sAttCode, iFieldId)
//{
//	// Asynchronously call the server to provide a default value if the field is
//	// empty
//	if (oObj['m_aCurrValues'][sAttCode] == '')
//	{
//		var oJSON = document.getElementById(sJsonFieldId);
//		$.get('ajax.render.php?class=' + sClass + '&json_obj=' + oJSON.value + '&att_code=' + sAttCode,
//		   { operation: "default_value" },
//		   function(json_data){
//			 var oObj = ReloadObjectFromServer(json_data);
//			 UpdateFieldFromObject(iFieldId, aFieldsMap, oObj)
//			}
//		);
//	}
//}

// Store the result of the form validation... there may be several forms per page, beware
var oFormErrors = { err_form0: 0 };

function CheckFields(sFormId)
{
	$('#'+sFormId+' :submit').attr('disable', 'disabled');
	$('#'+sFormId+' :button[type=submit]').attr('disable', 'disabled');
	firstErrorId = '';

	// The two 'fields' below will be updated when the 'validate' event is processed
	oFormErrors['err_'+sFormId] = 0;		// Number of errors encountered when validating the form
	oFormErrors['input_'+sFormId] = null;	// First 'input' with an error, to set the focus to it
	$('#'+sFormId+' :input').each( function()
	{
		validateEventResult = $(this).trigger('validate', sFormId);
	}
	);
	if(oFormErrors['err_'+sFormId] > 0)
	{
		alert('Please fill-in all mandatory fields before continuing.');
		$('#'+sFormId+' :submit').attr('disable', '');
		$('#'+sFormId+' :button[type=submit]').attr('disable', '');
		if (oFormErrors['input_'+sFormId] != null)
		{
			$('#'+oFormErrors['input_'+sFormId]).focus();
		}
	}
	return (oFormErrors['err_'+sFormId] == 0); // If no error, submit the form
}

function ValidateField(sFieldId, sPattern, bMandatory, sFormId)
{
	var bValid = true;
	var currentVal = $('#'+sFieldId).val();
	if (bMandatory && ((currentVal == '') || (currentVal == 0)))
	{
		bValid = false;
	}
	else if ((currentVal == '') || (currentVal == 0))
	{
		// An empty field is Ok...
		bValid = true;
	}
	else if (sPattern != '')
	{
		re = new RegExp(sPattern);
		//console.log('Validating field: '+sFieldId + ' current value: '+currentVal + ' pattern: '+sPattern );
		bValid = re.test(currentVal);
	}
	if (bValid)
	{
		// Visual feedback
		$('#v_'+sFieldId).html('');
	}
	else
	{
		// Report the error...
		oFormErrors['err_'+sFormId]++;
		if (oFormErrors['input_'+sFormId] == null)
		{
			// Let's remember the first input with an error, so that we can put back the focus on it later
			oFormErrors['input_'+sFormId] = sFieldId;
		}
		// Visual feedback
		$('#v_'+sFieldId).html('');
	}
	//console.log('Form: '+sFormId+' Validating field: '+sFieldId + ' current value: '+currentVal+' pattern: '+sPattern+' result: '+bValid );
	return bValid;
}

function UpdateDependentFields(aFieldNames)
{
	//console.log('UpdateDependentFields:');
	//console.log(aFieldNames);
	index = 0;
	oWizardHelper.ResetQuery();
	oWizardHelper.UpdateWizard();
	while(index < aFieldNames.length )
	{
		sAttCode = aFieldNames[index];
		oWizardHelper.RequestAllowedValues(sAttCode);
		index++;
	}
	oWizardHelper.AjaxQueryServer();
}
<|endoftext|>


<commit_before>var fs = require("fs");

var winston = require('winston');
var parse = require("./parser").parse;
var _ = require("underscore");
var_ = _.extend(_, require("underscore.string"));

winston.level = 'debug';
winston.prettyPrint = true
var debug = _.partial(winston.log, 'debug');

var globalEnv = {
    'sub1': x => x - 1,
    'add1': x => x + 1,
    'zero?': x => x === 0,
    '+': (x, y) => x + y,
    '*': (x, y) => x * y,
    'eq?': (x, y) => toString(x) === toString(y),
    'car': x => {
        debug("_.first x[1]: ", _.first(x[1]) )
        return quoteUnwrap(["'", _.first(x[1])])
    },
    'cdr': x => quoteUnwrap(["'", _.rest(x[1])]),
    'cons': (x, y) => {
        if(_.isArray(x)){
            y[1].unshift(x[1]);
        }else{
            y[1].unshift(x);
        }
        return [y[0], y[1]]

    },
    'null?': x => x[0] === "'" && x[1].length === 0,
    'pair?': x => x[0] === "'" && _.isArray(x[1]) && x[1].length !== 0,
    'symbol?': _.isString,
    'not': x => !x,
    'and': (x, y) => x && y,
    'or': (x, y) => x || y,
    'print': x => debug,

}

function importFile(rktFile, callback){
    // everything with define
    // may implement provide ?
    fs.readFile(rktFile, "utf-8", function (err, expr) {
        if (err) {
            throw err;
        }
        var parsedData = parse(_removeComment(expr));
        var defineExpr = [];
        _.each(parsedData, function(ele, index, list){
            _defineEnv(ele, globalEnv);
        })
        callback && callback();
    })

}

function _removeComment(expr){
    var data = expr.split(/\r?\n/);
    return _.filter(data, x => _.ltrim(x)[0] !== ";").join("\n")
}

function interpreter(expr){
    var parsedData = parse(_removeComment(expr));
    var env = {};
    var defineExpr = [];
    var bodyExpr = [];
    _.each(parsedData, function(ele, index, list){
        if(ele[0] === 'define'){
            defineExpr.push(ele);
        }else{
            bodyExpr.push(ele);
        }
    })
    _.each(defineExpr, function(ele, index, list){
        _defineEnv(ele, env);
    })

    return toString(_inter(bodyExpr, env));
}

function _defineEnv(expr, env){
    // assgin to env
    // if env has the value then throw error
    if(_.has(env, expr[1])){
        throw new Error(expr[1] + " already defined");
    }

    env[expr[1]] = _inter(expr[2], env);
}

function lookup(symbol, env){
    if(_.has(env, symbol)){
        return env[symbol]
    }
    // console.log(env);

    if(_.has(globalEnv, symbol)){
        return globalEnv[symbol]
    }

    throw new Error('no ' + symbol + " in env");
}

function quoteUnwrap(expr, quoteStart){
    var quoteStart = quoteStart || false;
    debug("<======== quoteUnwrap .. start =================>")
    debug(expr)
    debug(quoteStart)
    debug("<======== quoteUnwrap .. end =================>")
    if(_.isArray(expr)){
        if (expr[0] === "'"){
            if(quoteStart){
                return quoteUnwrap(expr[1], true)
            }

            if(_.isNumber(expr[1]) || _.isBoolean(expr[1])){
                return expr[1];
            }

        }

        return expr.map(x => quoteUnwrap(x, quoteStart))
    }

    if(_.isNumber(expr[1]) || _.isBoolean(expr[1])){
        return expr[1];
    }
    return expr;
}

function _inter(expr, env){
    debug("========start==========")
    debug(expr);
    debug(env);
    debug("========end==========")
    if(_.isArray(expr)){
        if(expr.length === 1){ // can be removed when deal with define
            return _inter(expr[0], env)
        }
        if(_.isString(expr[0])){
            if(expr[0] === 'lambda'){
                return function(){
                    var args = arguments;
                    var extendEnv = _.create({}, env)
                    _.each(expr[1], function(ele, index, list){
                        extendEnv[ele] = args[index];
                    })

                    return _inter(expr[2], extendEnv);
                }
            }

            if(expr[0] === 'let'){
                var newEnv = _.create({}, env)
                _.each(expr[1], function(ele, index, list){
                    newEnv[ele[0]] = _inter(ele[1], env);
                });

                // console.log("newEnv:");
                // console.log(newEnv);
                var result;
                _.each(expr.slice(2), function(ele, index, list){
                    result = _inter(ele, newEnv);
                })
                return result;
            }

            if(expr[0] === 'letrec'){
                var newEnv = _.create({}, env);
                _.each(expr[1], function(ele, index, list){
                    newEnv[ele[0]] = _inter(ele[1], newEnv);
                });

                var result;
                _.each(expr.slice(2), function(ele, index, list){
                    result = _inter(ele, newEnv);
                })
                return result;
            }

            if(expr[0] === 'if'){
                var cond = _inter(expr[1], env);
                if(cond){
                    return _inter(expr[2], env);
                }else{
                    return _inter(expr[3], env)
                }
            }

            if(expr[0] === 'begin'){
                var result;
                _.each(expr.slice(1), function(ele, index, list){
                    result = _inter(ele, env);
                })
                return result;
            }

            if(expr[0] === 'set!'){
                env[expr[1]] = _inter(expr[2], env);
                return ;
            }

            if(expr[0] === "'"){
                return quoteUnwrap(expr);
            }

            if(expr[0] === "cond"){
                var restCondition = expr.slice(1);
                var condexpr = restCondition[0];
                if(condexpr[0] === 'else'){
                    return _inter(condexpr[1], env);
                }
                var condition = _inter(condexpr[0], env);
                debug("condition: ", condition)
                if(restCondition.length === 1){
                    debug("condition: ", condition)
                    if(condition){
                        return _inter(condexpr[1], env);
                    }else{
                        return ;
                    }
                }else{
                    if(condition){
                        return _inter(condexpr[1], env);
                    }else{
                        restCondition.shift()
                        return _inter(['cond'].concat(restCondition), env);
                    }
                }
            }

            if(expr[0] === 'list'){
                expr.shift()
                debug("result:", quoteUnwrap(expr,true))
                var newExpr = expr.map(x => _inter(x, env))
                return _inter(["'", quoteUnwrap(newExpr,true)], env)
            }

        }

        // '(e1 e2)  ==> application
        var lambda = _inter(expr[0], env);
        var args = [];
        _.each(expr.slice(1), function(ele, index, list){
            args.push(_inter(ele, env));
        })
        debug("args: ", args)
        args.push(env);
        return lambda.apply({}, args);

    }

    if(_.isNumber(expr) || _.isBoolean(expr)){
        return expr;
    }

    if(_.isString(expr)){
        return lookup(expr, env)
    }

}

function toString(expr){
    if(_.isArray(expr)){
        if(expr[0] === "'"){
            if(_.isNumber(expr[1]) || _.isBoolean(expr[1])){
                return toString(expr[1]);
            }

            return "'" + expr.slice(1).map(x => toString(x)).join(" ")
        }

        return "(" + expr.map(x => toString(x)).join(" ") + ")";
    }

    return expr;
}

module.exports = {
    interpreter: interpreter,
    importFile: importFile,
    toString: toString,
    ".globalEnv": globalEnv, //
}

<commit_msg>fix symbol?<commit_after>var fs = require("fs");

var winston = require('winston');
var parse = require("./parser").parse;
var _ = require("underscore");
var_ = _.extend(_, require("underscore.string"));

winston.level = 'error';
winston.prettyPrint = true
var debug = _.partial(winston.log, 'debug');
var error = _.partial(winston.log, 'error');

var globalEnv = {
    'sub1': x => x - 1,
    'add1': x => x + 1,
    'zero?': x => x === 0,
    '+': (x, y) => x + y,
    '*': (x, y) => x * y,
    'eq?': (x, y) => toString(x) === toString(y),
    'car': x => {
        debug("_.first x[1]: ", _.first(x[1]) )
        return quoteUnwrap(["'", _.first(x[1])])
    },
    'cdr': x => quoteUnwrap(["'", _.rest(x[1])]),
    'cons': (x, y) => {
        if(_.isArray(x)){
            y[1].unshift(x[1]);
        }else{
            y[1].unshift(x);
        }
        return [y[0], y[1]]

    },
    'null?': x => x[0] === "'" && x[1].length === 0,
    'pair?': x => x[0] === "'" && _.isArray(x[1]) && x[1].length !== 0,
    'symbol?': x => {
        if(_.isArray(x) && x[0] === "'"){
            var unquoteData = quoteUnwrap(x);
            return _.isArray(unquoteData)
                    && unquoteData[0] === "'"
                    && _.isString(unquoteData[1]);
        }
        return false;
    },
    'not': x => !x,
    'and': (x, y) => x && y,
    'or': (x, y) => x || y,
    'print': x => debug,

}

function importFile(rktFile, callback){
    // everything with define
    // may implement provide ?
    fs.readFile(rktFile, "utf-8", function (err, expr) {
        if (err) {
            throw err;
        }
        var parsedData = parse(_removeComment(expr));
        var defineExpr = [];
        _.each(parsedData, function(ele, index, list){
            _defineEnv(ele, globalEnv);
        })
        callback && callback();
    })

}

function _removeComment(expr){
    var data = expr.split(/\r?\n/);
    return _.filter(data, x => _.ltrim(x)[0] !== ";").join("\n")
}

function interpreter(expr){
    var parsedData = parse(_removeComment(expr));
    var env = {};
    var defineExpr = [];
    var bodyExpr = [];
    _.each(parsedData, function(ele, index, list){
        if(ele[0] === 'define'){
            defineExpr.push(ele);
        }else{
            bodyExpr.push(ele);
        }
    })
    _.each(defineExpr, function(ele, index, list){
        _defineEnv(ele, env);
    })
    return toString(_inter(bodyExpr, env));
}

function _defineEnv(expr, env){
    // assgin to env
    // if env has the value then throw error
    if(_.has(env, expr[1])){
        throw new Error(expr[1] + " already defined");
    }

    env[expr[1]] = _inter(expr[2], env);
}

function lookup(symbol, env){
    if(_.has(env, symbol)){
        return env[symbol]
    }
    // console.log(env);

    if(_.has(globalEnv, symbol)){
        return globalEnv[symbol]
    }

    throw new Error('no ' + symbol + " in env");
}

function quoteUnwrap(expr, quoteStart){
    var quoteStart = quoteStart || false;
    debug("<======== quoteUnwrap .. start =================>")
    debug(expr)
    debug(quoteStart)
    debug("<======== quoteUnwrap .. end =================>")
    if(_.isArray(expr)){
        if (expr[0] === "'"){
            if(quoteStart){
                return quoteUnwrap(expr[1], true)
            }

            if(_.isNumber(expr[1]) || _.isBoolean(expr[1])){
                return expr[1];
            }

        }

        return expr.map(x => quoteUnwrap(x, quoteStart))
    }

    if(_.isNumber(expr[1]) || _.isBoolean(expr[1])){
        return expr[1];
    }
    return expr;
}

function _inter(expr, env){
    debug("========start==========")
    debug(expr);
    debug(env);
    debug("========end==========")
    if(_.isArray(expr)){
        if(expr.length === 1){ // can be removed when deal with define
            return _inter(expr[0], env)
        }
        if(_.isString(expr[0])){
            if(expr[0] === 'lambda'){
                return function(){
                    var args = arguments;
                    var extendEnv = _.create({}, env)
                    _.each(expr[1], function(ele, index, list){
                        extendEnv[ele] = args[index];
                    })

                    return _inter(expr[2], extendEnv);
                }
            }

            if(expr[0] === 'let'){
                var newEnv = _.create({}, env)
                _.each(expr[1], function(ele, index, list){
                    newEnv[ele[0]] = _inter(ele[1], env);
                });

                // console.log("newEnv:");
                // console.log(newEnv);
                var result;
                _.each(expr.slice(2), function(ele, index, list){
                    result = _inter(ele, newEnv);
                })
                return result;
            }

            if(expr[0] === 'letrec'){
                var newEnv = _.create({}, env);
                _.each(expr[1], function(ele, index, list){
                    newEnv[ele[0]] = _inter(ele[1], newEnv);
                });

                var result;
                _.each(expr.slice(2), function(ele, index, list){
                    result = _inter(ele, newEnv);
                })
                return result;
            }

            if(expr[0] === 'if'){
                var cond = _inter(expr[1], env);
                if(cond){
                    return _inter(expr[2], env);
                }else{
                    return _inter(expr[3], env)
                }
            }

            if(expr[0] === 'begin'){
                var result;
                _.each(expr.slice(1), function(ele, index, list){
                    result = _inter(ele, env);
                })
                return result;
            }

            if(expr[0] === 'set!'){
                env[expr[1]] = _inter(expr[2], env);
                return ;
            }

            if(expr[0] === "'"){
                return quoteUnwrap(expr);
            }

            if(expr[0] === "cond"){
                var restCondition = expr.slice(1);
                var condexpr = restCondition[0];
                if(condexpr[0] === 'else'){
                    return _inter(condexpr[1], env);
                }
                var condition = _inter(condexpr[0], env);
                debug("condition: ", condition)
                if(restCondition.length === 1){
                    debug("condition: ", condition)
                    if(condition){
                        return _inter(condexpr[1], env);
                    }else{
                        return ;
                    }
                }else{
                    if(condition){
                        return _inter(condexpr[1], env);
                    }else{
                        restCondition.shift()
                        return _inter(['cond'].concat(restCondition), env);
                    }
                }
            }

            if(expr[0] === 'list'){
                expr.shift()
                debug("result:", quoteUnwrap(expr,true))
                var newExpr = expr.map(x => _inter(x, env))
                return _inter(["'", quoteUnwrap(newExpr,true)], env)
            }

        }

        // '(e1 e2)  ==> application
        var lambda = _inter(expr[0], env);
        var args = [];
        _.each(expr.slice(1), function(ele, index, list){
            args.push(_inter(ele, env));
        })
        debug("args: ", args)
        args.push(env);
        return lambda.apply({}, args);

    }

    if(_.isNumber(expr) || _.isBoolean(expr)){
        return expr;
    }

    if(_.isString(expr)){
        return lookup(expr, env)
    }

}

function toString(expr){
    if(_.isArray(expr)){
        if(expr[0] === "'"){
            if(_.isNumber(expr[1]) || _.isBoolean(expr[1])){
                return toString(expr[1]);
            }

            return "'" + expr.slice(1).map(x => toString(x)).join(" ")
        }

        return "(" + expr.map(x => toString(x)).join(" ") + ")";
    }

    return expr;
}

module.exports = {
    interpreter: interpreter,
    importFile: importFile,
    toString: toString,
    ".globalEnv": globalEnv, //
}

<|endoftext|>


<commit_before>/**
 * This is a reducer, a pure function with (state, action) => state signature.
 * It describes how an action transforms the state into the next state.
 *
 * The shape of the state is up to you: it can be a primitive, an array, an object,
 * or even an Immutable.js data structure. The only important part is that you should
 * not mutate the state object, but return a new object if the state changes.
 *
 * In this example, we use a `switch` statement and strings, but you can use a helper that
 * follows a different convention (such as function maps) if it makes sense for your
 * project.
 */

import set from 'js/utils/set'

// Load project data from package.json, and strip down to only necessary items.
import npmData from '../../../package.json'

// Load all generic data needed.
import appData from 'js/appData'

// TODO load from localStorage if exists? merge? clobber?
// This is default character data structure for a new character,
// before random allocations.
import charData from 'data/char.json'

const initialState = {
  // 'package' is a reserved word.
  npm: npmData,
  app: appData,
  char: charData
}

/**
 * Work with arrays of strings or arrays of options.
 * TODO Handle non-singular results (i.e. 20 options for a 1d20 roll).
 */
const getRandomItem = (array) => {
  let val = array[Math.floor(Math.random() * array.length)]
  return val.value || val
}

const assignCharacteristic = (charAttributes, value, baseAttributeNames) => {
  // If the value is a straight up attribute name, return that.
  if (baseAttributeNames.indexOf(value) !== -1) {
    return charAttributes[value]
  }

  // Check for addition values, like intellect+1.
  let arr = value.split('+')
  if (arr.length === 2) {
    return charAttributes[arr[0]] + parseInt(arr[1])
  }

  // Do nothing by default, quarterHealth we're leaving to later.
  return value
}

const calcHealingRate = (health, healingRate) => {
  if (healingRate === 'quarterHealth') {
    return Math.floor(parseInt(health) / 4)
  } else {
    throw Error('Unknown healingRate value: ' + healingRate)
  }
}

// TODO Split this out ito a reducer.
// Called when ancestry changes, so basically a "new" character.
const assignCharacteristics = (char, ancestryCharacteristics, baseAttributeNames) => {
  // Iterate through ancestryCharacteristics object, creating char characteristics.
  let newCharacteristics = Object.keys(ancestryCharacteristics).reduce((obj, key) => {
    let value = ancestryCharacteristics[key]
    let err = Error('Unsupported value for key: ' + key)

    switch (typeof value) {
      case 'number':
        // plain old number. e.g. speed, insanity, etc.
        obj[key] = value
        break
      case 'string':
        // more complex. defer to function.
        obj[key] = assignCharacteristic(char.attributes, value, baseAttributeNames)
        break
      case 'object':
        if (value instanceof Array) {
          // Default to first item.
          obj[key] = value.join(' or ')
        } else {
          throw err
        }
        break
      default:
        throw err
    }

    return obj
  }, {})

  // Handle quarterHealth, after health has been assigned.
  newCharacteristics.healingRate = calcHealingRate(newCharacteristics.health, ancestryCharacteristics.healingRate)

  return newCharacteristics
}

const assignAncestryToCharacter = (state, ancestryData) => {
  state.char.name = getRandomItem(ancestryData.commonNames)

  // Aspects are per-ancestry. Chosen randomly.
  state.char.aspects = {}
  Object.keys(ancestryData.aspects).forEach(key => {
    let aspect = ancestryData.aspects[key]
    state.char.aspects[key] = getRandomItem(aspect.values)
  })

  state.char.attributes = Object.assign({}, ancestryData.attributes)
  state.char.characteristics = assignCharacteristics(state.char, ancestryData.characteristics, state.app.attributes)
}

const initRandomCharacter = (state) => {
  console.error('FIXME forcing ancestry to Human/Orc during dev.')
  state.char.ancestry = getRandomItem(['Human', 'Orc'])
  // state.char.ancestry = getRandomItem(state.app.ancestries)
  assignAncestryToCharacter(state, getAncestryDefaultData(state))
}

/**
 * Needs to adjust scores rather than reset them.
 */
const increaseOneAttribute = (attributes, attribute) => {
  let att = Object.assign({}, attributes)
  if (att.oneIncreased) {
    // Previously increased attribute - decrement first.
    att[attribute] -= 1
  }
  // Increase attribute score by 1 and set flag to remember choice.
  att[attribute] += 1
  att.oneIncreased = attribute
  return att
}

/**
 * TODO Don't adjust util both values are set?
 */
const adjustOneAttribute = (attributes, from, to) => {
  let att = Object.assign({}, attributes)

  if (att.oneAdjustFrom && att.oneAdjustFrom !== from) {
    // Previous selection needs to be adjusted. Increase old value by 1.
    att[att.oneAdjustFrom] += 1
    delete att.oneAdjustFrom
  }

  if (from && from !== att.oneAdjustFrom) {
    // If from attribute selected, decrement it now.
    att[from] -= 1
    att.oneAdjustFrom = from
  }

  if (att.oneAdjustTo && att.oneAdjustTo !== to) {
    // Previous selection needs to be adjusted. Increase old value by 1.
    att[att.oneAdjustTo] -= 1
    delete att.oneAdjustTo
  }

  if (to && att.oneAdjustTo !== to) {
    // If to attribute selected, increment it now.
    att[to] += 1
    att.oneAdjustTo = to
  }

  return att
}

const getAncestryDefaultData = state => {
  return state.app[state.char.ancestry.toLowerCase()]
}

// Init new characters with random values.
initRandomCharacter(initialState)

// root reducer
// TODO split this up into smaller parts as I develop the app.
export default function charGen (state = initialState, action) {
  // Create copy of current state, to ensure UI updates.
  let newState = {
    npm: Object.assign({}, state.npm),
    app: Object.assign({}, state.app),
    char: Object.assign({}, state.char)
  }

  switch (action.type) {
    case 'CREATE_RANDOM':
      initRandomCharacter(newState)
      break
    case 'CHANGE_ANCESTRY':
      newState.char.ancestry = action.value
      assignAncestryToCharacter(newState, getAncestryDefaultData(newState))
      break
    case 'CHANGE_SIMPLE_VALUE':
      set(newState.char, action.name, action.value)
      break
    case 'INCREASE_ONE':
      newState.char.attributes = increaseOneAttribute(newState.char.attributes, action.value)
      break
    case 'ADJUST_ONE_FROM':
      newState.char.attributes = adjustOneAttribute(newState.char.attributes, action.value, newState.char.attributes.oneAdjustTo)
      break
    case 'ADJUST_ONE_TO':
      newState.char.attributes = adjustOneAttribute(newState.char.attributes, newState.char.attributes.oneAdjustFrom, action.value)
      break
  }

  // TODO Debug helper. Remove later.
  console.log('newState:', newState)

  return newState
}
<commit_msg>[#34] Update root reducer to use getRandomAspect.<commit_after>/**
 * This is a reducer, a pure function with (state, action) => state signature.
 * It describes how an action transforms the state into the next state.
 *
 * The shape of the state is up to you: it can be a primitive, an array, an object,
 * or even an Immutable.js data structure. The only important part is that you should
 * not mutate the state object, but return a new object if the state changes.
 *
 * In this example, we use a `switch` statement and strings, but you can use a helper that
 * follows a different convention (such as function maps) if it makes sense for your
 * project.
 */

import set from 'js/utils/set'
import getRandomAspect from 'js/utils/getRandomAspect'

// Load project data from package.json, and strip down to only necessary items.
import npmData from '../../../package.json'

// Load all generic data needed.
import appData from 'js/appData'

// TODO load from localStorage if exists? merge? clobber?
// This is default character data structure for a new character,
// before random allocations.
import charData from 'data/char.json'

const initialState = {
  // 'package' is a reserved word.
  npm: npmData,
  app: appData,
  char: charData
}

/**
 * Work with arrays of strings or arrays of options.
 * TODO Handle non-singular results (i.e. 20 options for a 1d20 roll).
 */
const getRandomItem = (array) => {
  let val = array[Math.floor(Math.random() * array.length)]
  return val.value || val
}

const assignCharacteristic = (charAttributes, value, baseAttributeNames) => {
  // If the value is a straight up attribute name, return that.
  if (baseAttributeNames.indexOf(value) !== -1) {
    return charAttributes[value]
  }

  // Check for addition values, like intellect+1.
  let arr = value.split('+')
  if (arr.length === 2) {
    return charAttributes[arr[0]] + parseInt(arr[1])
  }

  // Do nothing by default, quarterHealth we're leaving to later.
  return value
}

const calcHealingRate = (health, healingRate) => {
  if (healingRate === 'quarterHealth') {
    return Math.floor(parseInt(health) / 4)
  } else {
    throw Error('Unknown healingRate value: ' + healingRate)
  }
}

// TODO Split this out ito a reducer.
// Called when ancestry changes, so basically a "new" character.
const assignCharacteristics = (char, ancestryCharacteristics, baseAttributeNames) => {
  // Iterate through ancestryCharacteristics object, creating char characteristics.
  let newCharacteristics = Object.keys(ancestryCharacteristics).reduce((obj, key) => {
    let value = ancestryCharacteristics[key]
    let err = Error('Unsupported value for key: ' + key)

    switch (typeof value) {
      case 'number':
        // plain old number. e.g. speed, insanity, etc.
        obj[key] = value
        break
      case 'string':
        // more complex. defer to function.
        obj[key] = assignCharacteristic(char.attributes, value, baseAttributeNames)
        break
      case 'object':
        if (value instanceof Array) {
          // Default to first item.
          obj[key] = value.join(' or ')
        } else {
          throw err
        }
        break
      default:
        throw err
    }

    return obj
  }, {})

  // Handle quarterHealth, after health has been assigned.
  newCharacteristics.healingRate = calcHealingRate(newCharacteristics.health, ancestryCharacteristics.healingRate)

  return newCharacteristics
}

const assignAncestryToCharacter = (state, ancestryData) => {
  state.char.name = getRandomItem(ancestryData.commonNames)

  // Aspects are per-ancestry. Chosen randomly.
  state.char.aspects = {}
  Object.keys(ancestryData.aspects).forEach(key => {
    let aspectValue = getRandomAspect(ancestryData.aspects[key])
    // TODO effects? Perhaps store aspects as objects, for later use?
    state.char.aspects[key] = aspectValue.value
  })

  state.char.attributes = Object.assign({}, ancestryData.attributes)
  state.char.characteristics = assignCharacteristics(state.char, ancestryData.characteristics, state.app.attributes)
}

const initRandomCharacter = (state) => {
  console.error('FIXME forcing ancestry to Human/Orc during dev.')
  state.char.ancestry = getRandomItem(['Human', 'Orc'])
  // state.char.ancestry = getRandomItem(state.app.ancestries)
  assignAncestryToCharacter(state, getAncestryDefaultData(state))
}

/**
 * Needs to adjust scores rather than reset them.
 */
const increaseOneAttribute = (attributes, attribute) => {
  let att = Object.assign({}, attributes)
  if (att.oneIncreased) {
    // Previously increased attribute - decrement first.
    att[attribute] -= 1
  }
  // Increase attribute score by 1 and set flag to remember choice.
  att[attribute] += 1
  att.oneIncreased = attribute
  return att
}

/**
 * TODO Don't adjust util both values are set?
 */
const adjustOneAttribute = (attributes, from, to) => {
  let att = Object.assign({}, attributes)

  if (att.oneAdjustFrom && att.oneAdjustFrom !== from) {
    // Previous selection needs to be adjusted. Increase old value by 1.
    att[att.oneAdjustFrom] += 1
    delete att.oneAdjustFrom
  }

  if (from && from !== att.oneAdjustFrom) {
    // If from attribute selected, decrement it now.
    att[from] -= 1
    att.oneAdjustFrom = from
  }

  if (att.oneAdjustTo && att.oneAdjustTo !== to) {
    // Previous selection needs to be adjusted. Increase old value by 1.
    att[att.oneAdjustTo] -= 1
    delete att.oneAdjustTo
  }

  if (to && att.oneAdjustTo !== to) {
    // If to attribute selected, increment it now.
    att[to] += 1
    att.oneAdjustTo = to
  }

  return att
}

const getAncestryDefaultData = state => {
  return state.app[state.char.ancestry.toLowerCase()]
}

// Init new characters with random values.
initRandomCharacter(initialState)

// root reducer
// TODO split this up into smaller parts as I develop the app.
export default function charGen (state = initialState, action) {
  // Create copy of current state, to ensure UI updates.
  let newState = {
    npm: Object.assign({}, state.npm),
    app: Object.assign({}, state.app),
    char: Object.assign({}, state.char)
  }

  switch (action.type) {
    case 'CREATE_RANDOM':
      initRandomCharacter(newState)
      break
    case 'CHANGE_ANCESTRY':
      newState.char.ancestry = action.value
      assignAncestryToCharacter(newState, getAncestryDefaultData(newState))
      break
    case 'CHANGE_SIMPLE_VALUE':
      set(newState.char, action.name, action.value)
      break
    case 'INCREASE_ONE':
      newState.char.attributes = increaseOneAttribute(newState.char.attributes, action.value)
      break
    case 'ADJUST_ONE_FROM':
      newState.char.attributes = adjustOneAttribute(newState.char.attributes, action.value, newState.char.attributes.oneAdjustTo)
      break
    case 'ADJUST_ONE_TO':
      newState.char.attributes = adjustOneAttribute(newState.char.attributes, newState.char.attributes.oneAdjustFrom, action.value)
      break
  }

  // TODO Debug helper. Remove later.
  console.log('newState:', newState)

  return newState
}
<|endoftext|>


<commit_before>
(function() {

    /**
     * table view shown in admin
     */
    cdb.admin.TableView = cdb.ui.common.Table.extend({
      classLabel: 'cdb.admin.TableView',
      events: cdb.core.View.extendEvents({
          'click .clearview': '_clearView',
          'click .sqlview .export_query': '_tableFromQuery',
          'click .noRows': 'addEmptyRow'
      }),

      rowView: cdb.admin.RowView,

      initialize: function() {
        var self = this;
        this.elder('initialize');
        this.options.row_header = true;

        var topReached = false;
        this._editorsOpened = null;

        this.initPaginationAndScroll();

        this.initializeBindings();

      },

      /**
       * Append all the bindings needed for this view
       * @return undefined
       */
      initializeBindings: function() {
        var self = this;

        _.bindAll(this, "render", "rowSaving", "addEmptyRow",
          "_checkEmptyTable", "_forceScroll", "_scrollMagic",
          "rowChanged", "rowSynched",
          "rowFailed", "rowDestroyed", "emptyTable");

        this.model.data().bind('newPage', this.newPage, this);
        this.model.data().bind('loadingRows', function(updown) {
          var fn = updown === 'up'? 'prepend': 'append';
          self.$el[fn]("

Loading more rows

Now viewing 120 of 264

");
        });
        this.model.data().bind('endLoadingRows', function() {
          //self.$el.find('.page_loader').remove();
        });

        this.bind('cellDblClick', this._editCell, this);
        this.bind('createRow', function() {
          self._checkEmptyTable();
        });

        this.model.bind('change:dataSource', this._onSQLView, this);
        // when model changes the header is re rendered so the notice should be added
        this.model.bind('change', this._onSQLView, this);
        this.model.bind('dataLoaded', function() {
          self._checkEmptyTable();
          self._forceScroll();
        });
      },

      initPaginationAndScroll: function() {
        var self = this;
        // pagination
        setInterval(function() {
          if(!self.$el.is(":visible") || self.model.data().isFetchingPage()) {
            return;
          }
          var pos = $(this).scrollTop();
          var d = self.model.data();
          // do not let to fetch previous pages
          // until the user dont scroll back a little bit
          if(pos > 2) {
            topReached = false;
          }
          var pageSize = $(window).height() - self.$el.offset().top;
          var tableHeight = this.$('tbody').height();
          var realPos = pos + pageSize;
          if(tableHeight < pageSize) {
            return;
          }
          if(realPos > tableHeight) {
             d.setPage(d.getPage() + 1);
          } else if (pos <= 0) {
            if(!topReached) {
              d.setPage(d.getPage() - 1);
            }
            topReached = true;
          }
        }, 2000);
        // Initialize moving header and loaders when scrolls
        this.scroll_position = { x:$(window).scrollLeft(), y:$(window).scrollTop(), last: 'vertical' };
        $(window).scroll( this._scrollMagic );

      },

      render: function(args) {
        this.elder('render', args);
        this.trigger('render');
      },

      _onSQLView: function() {
        // if we are in a SQL view add a header
        this.$('.sqlview').remove();
        if(this.model.isInSQLView()) {
          var empty = this.isEmptyTable();

          this.$('thead').append(
            this.getTemplate('table/views/sql_view_notice')({ empty: empty })
          );

          this.$('thead > tr').css('height', 64 + 42);
          if(this.isEmptyTable()) {
            this.addEmptySQLIfo();
          }
        }
      },

      _clearView: function(e) {
        e.preventDefault();
        this.trigger('clearSQLView');
        //this.model.useSQLView(null);
        return false;
      },

      _tableFromQuery: function(e) {
        var self = this;
        e.preventDefault();
        //this.model.useSQLView(null);
        var dlg = new cdb.admin.TableNameDialog({
          ok: function(name) {
            var sql = self.model.data().getSQL();
            alert('to be done in backend');
            /*
            var table = cdb.admin.CartoDBTableMetadata.createFromQuery(name, sql);

            table.save(null, {
              success: function() {
              },
              error: function() {
                cdb.log.error("problem creating table from query");
              }
            });
            */
          }
        });
        dlg.appendToBody().show();
        return false;
        /*
        from_query:SELECT * FROM cb_municipios_5000_e limit 1
        name:asdasdasd
        */
      },

      /**
       *  Function to control the scroll in the table (horizontal and vertical)
       */
      _scrollMagic: function(ev) {
        var actual_scroll_position = { x:$(window).scrollLeft(), y:$(window).scrollTop() };

        if (this.scroll_position.x != actual_scroll_position.x) {
          this.scroll_position.x = actual_scroll_position.x;
          this.$el.find("thead").addClass("horizontal");

          // If last change was vertical
          if (this.scroll_position.last == "vertical") {
            this.scroll_position.x = actual_scroll_position.x;
            this.$el.find("thead > tr > th > div").css({"marginLeft": "0px"})
            this.$el.find("thead div > div").css("top", actual_scroll_position.y + "px");
            this.scroll_position.last = "horizontal";
          }

        } else if (this.scroll_position.y != actual_scroll_position.y) {
          this.scroll_position.y = actual_scroll_position.y;
          this.$el.find("thead").removeClass("horizontal");

          // If last change was vertical
          if (this.scroll_position.last == "horizontal") {
            this.$el.find("thead > tr > th > div")
              .css({"marginLeft": "-" + actual_scroll_position.x + "px"})
            this.$el.find("thead > tr > th > div > div").removeAttr('style')
            this.scroll_position.last = "vertical";
          }
        }
      },

      _getEditor: function(columnType, opts) {
        var editors = {
          'string': cdb.admin.EditStringDialog,
          'number': cdb.admin.EditNumberDialog,
          'date': cdb.admin.EditDateDialog,
          'geometry': cdb.admin.EditGeometryDialog,
          'boolean': cdb.admin.EditBooleanDialog
        };

        // clean previous if exits
        var view = this._editorsOpened;
        if(view) {
          view.clean();
        }

        var Editor = editors[columnType];
        this._editorsOpened = new Editor(opts);
        return this._editorsOpened;

      },

      _editCell: function(e, cell, x, y) {
        var self = this;
        if(this.model.data().isReadOnly()) {
          return;
        }
        var column = self.model.columnName(x-1);
        var columnType = this.model.getColumnType(column);

        if(this.model.isReservedColumn(column)) {
          return;
        }

        var row = self.model.data().getRowAt(y);

        var initial_value = '';
        if(self.model.data().getCell(y, column) === 0 || self.model.data().getCell(y, column) === '0') {
          initial_value = '0';
        } else if (self.model.data().getCell(y, column)) {
          initial_value = self.model.data().getCell(y, column);
        }

        // dont let generic editor
        if(column == 'the_geom') {
          columnType = 'geometry'
        }

        var dlg = this._getEditor(columnType, {
          initial_value: initial_value,
          row: row,
          res: function(val) {
            var update = {};
            update[column] = val;
            row.set(update).save();
          }
        });

        if(!dlg) {
          cdb.log.error("editor not defined for column type " + columnType);
          return;
        }

        // auto add to table view
        // Check first if the row is the first or the cell is the last :)
        var $td = $(e.target).closest("td")
          , offset = $td.position()
          , width = $td.outerWidth();

        if ($td.parent().index() == 0) {
          offset.top += 5;
        } else {
          offset.top -= 11;
        }

        if ($td.index() == ($td.size() - 1)) {
          offset.left -= 22;
        } else {
          offset.left -= 11;
        }

        dlg.showAt(offset.left, offset.top, width, true);
      },

      /**
       * called when a new page is loaded
       */
      newPage: function(currentPage, direction) {
         var d = this.model.data();
         var rowspp = d.options.get('rows_per_page');
         var max_items = rowspp*4;
         if(d.size() > max_items) {
           var idx = currentPage*rowspp;
           cdb.log.debug("removing rows: " + d.size() + " " + idx);
           if(direction == 'up') {
             d.remove(d.models.slice(max_items, d.size()));
           } else {
             d.remove(d.models.slice(0, idx));
           }
         }
      },

      headerView: function(column) {
        var self = this;
        if(column[1] !== 'header') {
          var v = new cdb.admin.HeaderView({
            column: column,
            table: this.model,
            sqlView: this.options.sqlView
          }).bind('georeference', function(column) {
              var dlg = new cdb.admin.GeoreferenceDialog({
                model: this.table,
                wizard_option: 1, /** geocode using a column */
                georeference_column: column,
                geocoder: self.options.geocoder
              });
              dlg.appendToBody().open();
          });

          this.addView(v);
          return v.render().el;
        } else {
          return '

';
        }
      },

      /**
      * Checks if the table has any rows, and if not, launch the method for showing the appropiate view elements
      * @method _checkEmptyTable
      */
      _checkEmptyTable: function() {
        if(this.isEmptyTable()) {
          this.addEmptyTableInfo();
        } else {
          this.cleanEmptyTableInfo();
        }
      },

      /**
      * Force the table to be at the beginning
      * @method _forceScroll
      */
      _forceScroll: function(ev){
        $(window).scrollLeft(0);
      },

      /**
      * Adds the view elements associated with no content in the table
      * @method addemptyTableInfo
      */
      addEmptyTableInfo: function() {
        if(this.$('.noRows').length == 0 && !this.model.isInSQLView()) {
          this.elder('addEmptyTableInfo');

          this.$el.hide();
          var columnsNumber = this.model.get('schema').length;
          var columns = '
+	';
          for(var i = 0; i < columnsNumber; i++) {
            columns += '	';
          }
          columns += '
';
          var columnsFooter = '
	';
          for(var i = 0; i < columnsNumber; i++) {
            columnsFooter += '	';
          }
          columnsFooter += '
';

          var $columns = $(columns+columnsFooter)
          this.$el.append($columns);

          this.template_base = cdb.templates.getTemplate('table/views/empty_table');
          var content = this.template_base(this.import_);
          var $footer = $('

' + content + '

');
          this.$el.append($footer);

          this.$el.fadeIn();
        }
      },

      /**
      * Adds the view elements associated with no content in the table when a SQL is applied
      * @method addEmptySQLIfo
      */
      addEmptySQLIfo: function() {
        if(this.model.isInSQLView()) {
          this.elder('addEmptySQLInfo');
          this.$('tbody').html('');
          this.$('tfoot').remove();
          this.$el.hide();

          this.template_base = cdb.templates.getTemplate('table/views/empty_sql');
          var content = this.template_base(this.import_);
          var $footer = $('

' + content + '

');
          this.$el.append($footer);

          this.$el.fadeIn();
        }
      },

      /**
      * Removes from the view the no-content elements
      * @method cleanEmptyTableInfo
      */
      cleanEmptyTableInfo: function() {
        this.$('tfoot').fadeOut('fast', function() {
          $(this).remove();
        })
        this.$('.noRows').slideUp('fast', function() {
            $(this).remove();
        })
      },

      /**
      * Add a new row and removes the empty table view elemetns
      * @method addEmptyRow
      * @todo: (xabel) refactor this to include a "addRow" method in _models[0]
      */
      addEmptyRow: function() {
        this.dataModel.addRow({ at: 0});
        this.cleanEmptyTableInfo();
      },

      /**
      * Captures the saving event from the row and produces a notification
      * @todo (xabel) i'm pretty sure there has to be a less convulted way of doing this, without capturing a event
      * to throw another event in the model to be captured by some view
      */
      rowSaving: function() {
        this.model.notice('Saving your edit', 'load', this);
      },

      /**
      * Captures the change event from the row and produces a notification
      * @method rowSynched
      * @todo (xabel) i'm pretty sure there has to be a less convulted way of doing this, without capturing a event
      * to throw another event in the model to be captured by some view
      */
      rowSynched: function() {
        this.model.notice('Sucessfully saved');
      },

      /**
      * Captures the change event from the row and produces a notification
      * @method rowSynched
      * @todo (xabel) i'm pretty sure there has to be a less convulted way of doing this, without capturing a event
      * to throw another event in the model to be captured by some view
      */
      rowFailed: function() {
        this.model.notice('Oops, there has been an error saving your changes.');
      },

      /**
      * Captures the destroy event from the row and produces a notification
      * @method rowDestroyed
      */

      rowDestroyed: function() {
        this.model.notice('Sucessfully deleted')
        this._checkEmptyTable();
      }

    });

    cdb.admin.TableTab = cdb.core.View.extend({

      className: 'table',
      tabClass: cdb.admin.TableView,

      initialize: function() {
        this.tableView = new this.tabClass({
          dataModel: this.model.data(),
          model: this.model,
          sqlView: this.options.sqlView,
          geocoder: this.options.geocoder
        });

      },

      render: function() {
        this.$el.append(this.tableView.el);
        return this;
      }

    });

    // cdb.admin.PublicTableTab = cdb.admin.TableTab

})();
<commit_msg>refactor refactor (some bindings where being done later than they should)<commit_after>
(function() {

    /**
     * table view shown in admin
     */
    cdb.admin.TableView = cdb.ui.common.Table.extend({
      classLabel: 'cdb.admin.TableView',
      events: cdb.core.View.extendEvents({
          'click .clearview': '_clearView',
          'click .sqlview .export_query': '_tableFromQuery',
          'click .noRows': 'addEmptyRow'
      }),

      rowView: cdb.admin.RowView,

      initialize: function() {
        var self = this;
        this.elder('initialize');
        this.options.row_header = true;

        this._editorsOpened = null;

        this.initializeBindings();

        this.initPaginationAndScroll();

      },

      /**
       * Append all the bindings needed for this view
       * @return undefined
       */
      initializeBindings: function() {
        var self = this;

        _.bindAll(this, "render", "rowSaving", "addEmptyRow",
          "_checkEmptyTable", "_forceScroll", "_scrollMagic",
          "rowChanged", "rowSynched",
          "rowFailed", "rowDestroyed", "emptyTable");

        this.model.data().bind('newPage', this.newPage, this);
        this.model.data().bind('loadingRows', function(updown) {
          var fn = updown === 'up'? 'prepend': 'append';
          self.$el[fn]("

Loading more rows

Now viewing 120 of 264

");
        });
        this.model.data().bind('endLoadingRows', function() {
          //self.$el.find('.page_loader').remove();
        });

        this.bind('cellDblClick', this._editCell, this);
        this.bind('createRow', function() {
          self._checkEmptyTable();
        });

        this.model.bind('change:dataSource', this._onSQLView, this);
        // when model changes the header is re rendered so the notice should be added
        this.model.bind('change', this._onSQLView, this);
        this.model.bind('dataLoaded', function() {
          self._checkEmptyTable();
          self._forceScroll();
        });
      },

      initPaginationAndScroll: function() {
        var self = this;
        var topReached = false;

        // Initialize moving header and loaders when scrolls
        this.scroll_position = { x:$(window).scrollLeft(), y:$(window).scrollTop(), last: 'vertical' };
        $(window).scroll( this._scrollMagic );

        // pagination
        setInterval(function() {
          if(!self.$el.is(":visible") || self.model.data().isFetchingPage()) {
            return;
          }
          var pos = $(this).scrollTop();
          var d = self.model.data();
          // do not let to fetch previous pages
          // until the user dont scroll back a little bit
          if(pos > 2) {
            topReached = false;
          }
          var pageSize = $(window).height() - self.$el.offset().top;
          var tableHeight = this.$('tbody').height();
          var realPos = pos + pageSize;
          if(tableHeight < pageSize) {
            return;
          }
          if(realPos > tableHeight) {
             d.setPage(d.getPage() + 1);
          } else if (pos <= 0) {
            if(!topReached) {
              d.setPage(d.getPage() - 1);
            }
            topReached = true;
          }
        }, 2000);

      },

      render: function(args) {
        this.elder('render', args);
        this.trigger('render');
      },

      _onSQLView: function() {
        // if we are in a SQL view add a header
        this.$('.sqlview').remove();
        if(this.model.isInSQLView()) {
          var empty = this.isEmptyTable();

          this.$('thead').append(
            this.getTemplate('table/views/sql_view_notice')({ empty: empty })
          );

          this.$('thead > tr').css('height', 64 + 42);
          if(this.isEmptyTable()) {
            this.addEmptySQLIfo();
          }
        }
      },

      _clearView: function(e) {
        e.preventDefault();
        this.trigger('clearSQLView');
        //this.model.useSQLView(null);
        return false;
      },

      _tableFromQuery: function(e) {
        var self = this;
        e.preventDefault();
        //this.model.useSQLView(null);
        var dlg = new cdb.admin.TableNameDialog({
          ok: function(name) {
            var sql = self.model.data().getSQL();
            alert('to be done in backend');
            /*
            var table = cdb.admin.CartoDBTableMetadata.createFromQuery(name, sql);

            table.save(null, {
              success: function() {
              },
              error: function() {
                cdb.log.error("problem creating table from query");
              }
            });
            */
          }
        });
        dlg.appendToBody().show();
        return false;
        /*
        from_query:SELECT * FROM cb_municipios_5000_e limit 1
        name:asdasdasd
        */
      },

      /**
       *  Function to control the scroll in the table (horizontal and vertical)
       */
      _scrollMagic: function(ev) {
        var actual_scroll_position = { x:$(window).scrollLeft(), y:$(window).scrollTop() };

        if (this.scroll_position.x != actual_scroll_position.x) {
          this.scroll_position.x = actual_scroll_position.x;
          this.$el.find("thead").addClass("horizontal");

          // If last change was vertical
          if (this.scroll_position.last == "vertical") {
            this.scroll_position.x = actual_scroll_position.x;
            this.$el.find("thead > tr > th > div").css({"marginLeft": "0px"})
            this.$el.find("thead div > div").css("top", actual_scroll_position.y + "px");
            this.scroll_position.last = "horizontal";
          }

        } else if (this.scroll_position.y != actual_scroll_position.y) {
          this.scroll_position.y = actual_scroll_position.y;
          this.$el.find("thead").removeClass("horizontal");

          // If last change was vertical
          if (this.scroll_position.last == "horizontal") {
            this.$el.find("thead > tr > th > div")
              .css({"marginLeft": "-" + actual_scroll_position.x + "px"})
            this.$el.find("thead > tr > th > div > div").removeAttr('style')
            this.scroll_position.last = "vertical";
          }
        }
      },

      _getEditor: function(columnType, opts) {
        var editors = {
          'string': cdb.admin.EditStringDialog,
          'number': cdb.admin.EditNumberDialog,
          'date': cdb.admin.EditDateDialog,
          'geometry': cdb.admin.EditGeometryDialog,
          'boolean': cdb.admin.EditBooleanDialog
        };

        // clean previous if exits
        var view = this._editorsOpened;
        if(view) {
          view.clean();
        }

        var Editor = editors[columnType];
        this._editorsOpened = new Editor(opts);
        return this._editorsOpened;

      },

      _editCell: function(e, cell, x, y) {
        var self = this;
        if(this.model.data().isReadOnly()) {
          return;
        }
        var column = self.model.columnName(x-1);
        var columnType = this.model.getColumnType(column);

        if(this.model.isReservedColumn(column)) {
          return;
        }

        var row = self.model.data().getRowAt(y);

        var initial_value = '';
        if(self.model.data().getCell(y, column) === 0 || self.model.data().getCell(y, column) === '0') {
          initial_value = '0';
        } else if (self.model.data().getCell(y, column)) {
          initial_value = self.model.data().getCell(y, column);
        }

        // dont let generic editor
        if(column == 'the_geom') {
          columnType = 'geometry'
        }

        var dlg = this._getEditor(columnType, {
          initial_value: initial_value,
          row: row,
          res: function(val) {
            var update = {};
            update[column] = val;
            row.set(update).save();
          }
        });

        if(!dlg) {
          cdb.log.error("editor not defined for column type " + columnType);
          return;
        }

        // auto add to table view
        // Check first if the row is the first or the cell is the last :)
        var $td = $(e.target).closest("td")
          , offset = $td.position()
          , width = $td.outerWidth();

        if ($td.parent().index() == 0) {
          offset.top += 5;
        } else {
          offset.top -= 11;
        }

        if ($td.index() == ($td.size() - 1)) {
          offset.left -= 22;
        } else {
          offset.left -= 11;
        }

        dlg.showAt(offset.left, offset.top, width, true);
      },

      /**
       * called when a new page is loaded
       */
      newPage: function(currentPage, direction) {
         var d = this.model.data();
         var rowspp = d.options.get('rows_per_page');
         var max_items = rowspp*4;
         if(d.size() > max_items) {
           var idx = currentPage*rowspp;
           cdb.log.debug("removing rows: " + d.size() + " " + idx);
           if(direction == 'up') {
             d.remove(d.models.slice(max_items, d.size()));
           } else {
             d.remove(d.models.slice(0, idx));
           }
         }
      },

      headerView: function(column) {
        var self = this;
        if(column[1] !== 'header') {
          var v = new cdb.admin.HeaderView({
            column: column,
            table: this.model,
            sqlView: this.options.sqlView
          }).bind('georeference', function(column) {
              var dlg = new cdb.admin.GeoreferenceDialog({
                model: this.table,
                wizard_option: 1, /** geocode using a column */
                georeference_column: column,
                geocoder: self.options.geocoder
              });
              dlg.appendToBody().open();
          });

          this.addView(v);
          return v.render().el;
        } else {
          return '

';
        }
      },

      /**
      * Checks if the table has any rows, and if not, launch the method for showing the appropiate view elements
      * @method _checkEmptyTable
      */
      _checkEmptyTable: function() {
        if(this.isEmptyTable()) {
          this.addEmptyTableInfo();
        } else {
          this.cleanEmptyTableInfo();
        }
      },

      /**
      * Force the table to be at the beginning
      * @method _forceScroll
      */
      _forceScroll: function(ev){
        $(window).scrollLeft(0);
      },

      /**
      * Adds the view elements associated with no content in the table
      * @method addemptyTableInfo
      */
      addEmptyTableInfo: function() {
        if(this.$('.noRows').length == 0 && !this.model.isInSQLView()) {
          this.elder('addEmptyTableInfo');

          this.$el.hide();
          var columnsNumber = this.model.get('schema').length;
          var columns = '
+	';
          for(var i = 0; i < columnsNumber; i++) {
            columns += '	';
          }
          columns += '
';
          var columnsFooter = '
	';
          for(var i = 0; i < columnsNumber; i++) {
            columnsFooter += '	';
          }
          columnsFooter += '
';

          var $columns = $(columns+columnsFooter)
          this.$el.append($columns);

          this.template_base = cdb.templates.getTemplate('table/views/empty_table');
          var content = this.template_base(this.import_);
          var $footer = $('

' + content + '

');
          this.$el.append($footer);

          this.$el.fadeIn();
        }
      },

      /**
      * Adds the view elements associated with no content in the table when a SQL is applied
      * @method addEmptySQLIfo
      */
      addEmptySQLIfo: function() {
        if(this.model.isInSQLView()) {
          this.elder('addEmptySQLInfo');
          this.$('tbody').html('');
          this.$('tfoot').remove();
          this.$el.hide();

          this.template_base = cdb.templates.getTemplate('table/views/empty_sql');
          var content = this.template_base(this.import_);
          var $footer = $('

' + content + '

');
          this.$el.append($footer);

          this.$el.fadeIn();
        }
      },

      /**
      * Removes from the view the no-content elements
      * @method cleanEmptyTableInfo
      */
      cleanEmptyTableInfo: function() {
        this.$('tfoot').fadeOut('fast', function() {
          $(this).remove();
        })
        this.$('.noRows').slideUp('fast', function() {
            $(this).remove();
        })
      },

      /**
      * Add a new row and removes the empty table view elemetns
      * @method addEmptyRow
      * @todo: (xabel) refactor this to include a "addRow" method in _models[0]
      */
      addEmptyRow: function() {
        this.dataModel.addRow({ at: 0});
        this.cleanEmptyTableInfo();
      },

      /**
      * Captures the saving event from the row and produces a notification
      * @todo (xabel) i'm pretty sure there has to be a less convulted way of doing this, without capturing a event
      * to throw another event in the model to be captured by some view
      */
      rowSaving: function() {
        this.model.notice('Saving your edit', 'load', this);
      },

      /**
      * Captures the change event from the row and produces a notification
      * @method rowSynched
      * @todo (xabel) i'm pretty sure there has to be a less convulted way of doing this, without capturing a event
      * to throw another event in the model to be captured by some view
      */
      rowSynched: function() {
        this.model.notice('Sucessfully saved');
      },

      /**
      * Captures the change event from the row and produces a notification
      * @method rowSynched
      * @todo (xabel) i'm pretty sure there has to be a less convulted way of doing this, without capturing a event
      * to throw another event in the model to be captured by some view
      */
      rowFailed: function() {
        this.model.notice('Oops, there has been an error saving your changes.');
      },

      /**
      * Captures the destroy event from the row and produces a notification
      * @method rowDestroyed
      */

      rowDestroyed: function() {
        this.model.notice('Sucessfully deleted')
        this._checkEmptyTable();
      }

    });

    cdb.admin.TableTab = cdb.core.View.extend({

      className: 'table',
      tabClass: cdb.admin.TableView,

      initialize: function() {
        this.tableView = new this.tabClass({
          dataModel: this.model.data(),
          model: this.model,
          sqlView: this.options.sqlView,
          geocoder: this.options.geocoder
        });

      },

      render: function() {
        this.$el.append(this.tableView.el);
        return this;
      }

    });

    // cdb.admin.PublicTableTab = cdb.admin.TableTab

})();
<|endoftext|>


<commit_before>import { event, select } from 'd3-selection';
import { slider } from '@scola/d3-slider';
import 'd3-selection-multi';
import 'd3-transition';
import '@scola/d3-gesture';
import '@scola/d3-media';

export default class Main {
  constructor() {
    this._fade = true;
    this._height = null;
    this._styles = null;
    this._width = null;

    this._gesture = null;
    this._media = null;
    this._slider = null;
    this._menus = new Set();

    this._root = select('body')
      .append('div')
      .remove()
      .classed('scola app', true)
      .styles({
        'position': 'relative',
        'z-index': 0
      });

    this._main = this._root.append('div')
      .classed('scola main', true)
      .styles({
        'height': '100%',
        'left': '0px',
        'position': 'absolute',
        'right': '0px'
      });
  }

  destroy() {
    this._deleteGesture();
    this._deleteMedia();
    this._deleteSlider();

    this._menus.clear();

    this._root.dispatch('destroy');
    this._root.remove();
    this._root = null;
  }

  root() {
    return this._root;
  }

  height() {
    return this._height;
  }

  styles() {
    return this._styles;
  }

  width() {
    return this._width;
  }

  fade(value = null) {
    if (value === null) {
      return this._fade;
    }

    this._fade = value;
    return this;
  }

  gesture(action = null) {
    if (action === null) {
      return this._gesture;
    }

    if (action === false) {
      return this._deleteGesture();
    }

    if (!this._gesture) {
      this._insertGesture();
    }

    return this;
  }

  media(width = '64em', height = '48em', styles = null) {
    if (width === null) {
      return this._media;
    }

    if (width === false) {
      return this._deleteMedia();
    }

    if (styles === null) {
      styles = {
        'border-radius': '1em',
        'overflow': 'hidden',
        'transform': 'scale(1)'
      };
    }

    if (!this._media) {
      this._insertMedia(width, height, styles);
    }

    return this;
  }

  slider(action = true) {
    if (action === false) {
      return this._deleteSlider();
    }

    if (!this._slider) {
      this._insertSlider();
    }

    return this._slider;
  }

  append(menu, action = true) {
    if (action === true) {
      this._appendMenu(menu);
    } else if (action === false) {
      this._removeMenu(menu);
    }

    return this;
  }

  hide(callback = () => {}) {
    if (!this._fade) {
      callback();
      return;
    }

    this._root
      .transition()
      .style('opacity', 0)
      .on('end', callback);
  }

  show(callback = () => {}) {
    if (!this._fade) {
      callback();
      return;
    }

    this._root
      .style('opacity', 0)
      .transition()
      .style('opacity', 1)
      .on('end', callback);
  }

  _insertGesture() {
    this._gesture = this._root
      .gesture()
      .on('tap', () => this._change())
      .on('swiperight', () => this._change('left'))
      .on('swipeleft', () => this._change('right'));

    return this;
  }

  _deleteGesture() {
    if (this._gesture) {
      this._gesture.destroy();
      this._gesture = null;
    }

    return this;
  }

  _insertMedia(width, height, styles) {
    this._width = width;
    this._height = height;
    this._styles = styles;

    this._media = this._root
      .media(`(min-height: ${height}) and (min-width: ${width})`)
      .styles(styles)
      .media(`(min-height: ${height})`)
      .style('height', height)
      .media(`not all and (min-height: ${height})`)
      .style('height', '100%')
      .media(`(min-width: ${width})`)
      .style('width', width)
      .media(`not all and (min-width: ${width})`)
      .styles({
        'width': '100%',
        'height': '100%'
      })
      .start();

    return this;
  }

  _deleteMedia() {
    if (this._media) {
      this._media.destroy();
      this._media = null;
    }

    return this;
  }

  _insertSlider() {
    this._slider = slider()
      .remove(true)
      .rotate(false);

    this._main.node()
      .appendChild(this._slider.root().node());

    return this;
  }

  _deleteSlider() {
    if (this._slider) {
      this._slider.destroy();
      this._slider = null;
    }

    return this;
  }

  _appendMenu(menu) {
    if (menu.position() === 'left') {
      this._root.node()
        .insertBefore(menu.root().node(), this._main.node());
    } else {
      this._root.node()
        .appendChild(menu.root().node());
    }

    this._menus.add(menu);
    this._bindMenu(menu);
    this._fixMenu();
  }

  _removeMenu(menu) {
    menu.root().remove();
    this._menus.delete(menu);
    this._unbindMenu(menu);
  }

  _bindMenu(menu) {
    menu.root().on('fix.scola-app', () => this._fixMenu());
    menu.root().on('unfix.scola-app', () => this._fixMenu());
    menu.root().on('show.scola-app', () => this._showMenu());
    menu.root().on('hide.scola-app', () => this._hideMenu());
  }

  _unbindMenu(menu) {
    menu.root().on('fix.scola-app', null);
    menu.root().on('unfix.scola-app', null);
    menu.root().on('show.scola-app', null);
    menu.root().on('hide.scola-app', null);
  }

  _fixMenu() {
    const style = {
      left: '0px',
      right: '0px'
    };

    this._menus.forEach((menu) => {
      if (menu.fixed()) {
        style[menu.position()] = menu.width();
      } else {
        menu.hide();
      }
    });

    this._main.styles(style);

    return this;
  }

  _showMenu() {
    if (event.detail.menu.mode() === 'over') {
      return this;
    }

    const opposite = this._opposite(event.detail.menu.position());
    let oppositePosition = '-' + event.detail.menu.width();

    this._menus.forEach((menu) => {
      if (menu.position() === opposite && menu.fixed()) {
        oppositePosition = '0px';
      }
    });

    this._main
      .transition()
      .style(event.detail.menu.position(), event.detail.menu.width())
      .style(opposite, oppositePosition);

    return this;
  }

  _hideMenu() {
    if (event.detail.menu.mode() === 'over') {
      return this;
    }

    const opposite = this._opposite(event.detail.menu.position());
    let oppositePosition = '0px';

    this._menus.forEach((menu) => {
      if (menu.position() === opposite && menu.fixed()) {
        oppositePosition = menu.width();
      }
    });

    this._main
      .transition()
      .style(event.detail.menu.position(), '0px')
      .style(opposite, oppositePosition);

    return this;
  }

  _change(position) {
    let found = false;

    this._menus.forEach((menu) => {
      if (menu.position() === position) {
        return;
      }

      found = found || menu.hide();
    });

    if (found) {
      return;
    }

    this._menus.forEach((menu) => {
      if (found || menu.position() !== position) {
        return;
      }

      found = menu.show();
    });
  }

  _opposite(position) {
    return position === 'left' ? 'right' : 'left';
  }
}
<commit_msg>Change append syntax<commit_after>import { event, select } from 'd3-selection';
import { slider } from '@scola/d3-slider';
import 'd3-selection-multi';
import 'd3-transition';
import '@scola/d3-gesture';
import '@scola/d3-media';

export default class Main {
  constructor() {
    this._fade = true;
    this._height = null;
    this._styles = null;
    this._width = null;

    this._gesture = null;
    this._media = null;
    this._slider = null;
    this._menus = new Set();

    this._root = select('body')
      .append('div')
      .remove()
      .classed('scola app', true)
      .styles({
        'position': 'relative',
        'z-index': 0
      });

    this._main = this._root.append('div')
      .classed('scola main', true)
      .styles({
        'height': '100%',
        'left': '0px',
        'position': 'absolute',
        'right': '0px'
      });
  }

  destroy() {
    this._deleteGesture();
    this._deleteMedia();
    this._deleteSlider();

    this._menus.clear();

    this._root.dispatch('destroy');
    this._root.remove();
    this._root = null;
  }

  root() {
    return this._root;
  }

  height() {
    return this._height;
  }

  styles() {
    return this._styles;
  }

  width() {
    return this._width;
  }

  fade(value = null) {
    if (value === null) {
      return this._fade;
    }

    this._fade = value;
    return this;
  }

  gesture(action = null) {
    if (action === null) {
      return this._gesture;
    }

    if (action === false) {
      return this._deleteGesture();
    }

    if (!this._gesture) {
      this._insertGesture();
    }

    return this;
  }

  media(width = '64em', height = '48em', styles = null) {
    if (width === null) {
      return this._media;
    }

    if (width === false) {
      return this._deleteMedia();
    }

    if (styles === null) {
      styles = {
        'border-radius': '1em',
        'overflow': 'hidden',
        'transform': 'scale(1)'
      };
    }

    if (!this._media) {
      this._insertMedia(width, height, styles);
    }

    return this;
  }

  slider(action = true) {
    if (action === false) {
      return this._deleteSlider();
    }

    if (!this._slider) {
      this._insertSlider();
    }

    return this._slider;
  }

  append(menu, action = true) {
    if (action === false) {
      return this._deleteMenu(menu);
    }

    return this._insertMenu(menu);
  }

  hide(callback = () => {}) {
    if (!this._fade) {
      callback();
      return;
    }

    this._root
      .transition()
      .style('opacity', 0)
      .on('end', callback);
  }

  show(callback = () => {}) {
    if (!this._fade) {
      callback();
      return;
    }

    this._root
      .style('opacity', 0)
      .transition()
      .style('opacity', 1)
      .on('end', callback);
  }

  _insertGesture() {
    this._gesture = this._root
      .gesture()
      .on('tap', () => this._change())
      .on('swiperight', () => this._change('left'))
      .on('swipeleft', () => this._change('right'));

    return this;
  }

  _deleteGesture() {
    if (this._gesture) {
      this._gesture.destroy();
      this._gesture = null;
    }

    return this;
  }

  _insertMedia(width, height, styles) {
    this._width = width;
    this._height = height;
    this._styles = styles;

    this._media = this._root
      .media(`(min-height: ${height}) and (min-width: ${width})`)
      .styles(styles)
      .media(`(min-height: ${height})`)
      .style('height', height)
      .media(`not all and (min-height: ${height})`)
      .style('height', '100%')
      .media(`(min-width: ${width})`)
      .style('width', width)
      .media(`not all and (min-width: ${width})`)
      .styles({
        'width': '100%',
        'height': '100%'
      })
      .start();

    return this;
  }

  _deleteMedia() {
    if (this._media) {
      this._media.destroy();
      this._media = null;
    }

    return this;
  }

  _insertSlider() {
    this._slider = slider()
      .remove(true)
      .rotate(false);

    this._main.node()
      .appendChild(this._slider.root().node());

    return this;
  }

  _deleteSlider() {
    if (this._slider) {
      this._slider.destroy();
      this._slider = null;
    }

    return this;
  }

  _insertMenu(menu) {
    if (menu.position() === 'left') {
      this._root.node()
        .insertBefore(menu.root().node(), this._main.node());
    } else {
      this._root.node()
        .appendChild(menu.root().node());
    }

    this._menus.add(menu);
    this._bindMenu(menu);
    this._fixMenu();

    return menu;
  }

  _deleteMenu(menu) {
    menu.root().remove();
    this._menus.delete(menu);
    this._unbindMenu(menu);

    return menu;
  }

  _bindMenu(menu) {
    menu.root().on('fix.scola-app', () => this._fixMenu());
    menu.root().on('unfix.scola-app', () => this._fixMenu());
    menu.root().on('show.scola-app', () => this._showMenu());
    menu.root().on('hide.scola-app', () => this._hideMenu());
  }

  _unbindMenu(menu) {
    menu.root().on('fix.scola-app', null);
    menu.root().on('unfix.scola-app', null);
    menu.root().on('show.scola-app', null);
    menu.root().on('hide.scola-app', null);
  }

  _fixMenu() {
    const style = {
      left: '0px',
      right: '0px'
    };

    this._menus.forEach((menu) => {
      if (menu.fixed()) {
        style[menu.position()] = menu.width();
      } else {
        menu.hide();
      }
    });

    this._main.styles(style);

    return this;
  }

  _showMenu() {
    if (event.detail.menu.mode() === 'over') {
      return this;
    }

    const opposite = this._opposite(event.detail.menu.position());
    let oppositePosition = '-' + event.detail.menu.width();

    this._menus.forEach((menu) => {
      if (menu.position() === opposite && menu.fixed()) {
        oppositePosition = '0px';
      }
    });

    this._main
      .transition()
      .style(event.detail.menu.position(), event.detail.menu.width())
      .style(opposite, oppositePosition);

    return this;
  }

  _hideMenu() {
    if (event.detail.menu.mode() === 'over') {
      return this;
    }

    const opposite = this._opposite(event.detail.menu.position());
    let oppositePosition = '0px';

    this._menus.forEach((menu) => {
      if (menu.position() === opposite && menu.fixed()) {
        oppositePosition = menu.width();
      }
    });

    this._main
      .transition()
      .style(event.detail.menu.position(), '0px')
      .style(opposite, oppositePosition);

    return this;
  }

  _change(position) {
    let found = false;

    this._menus.forEach((menu) => {
      if (menu.position() === position) {
        return;
      }

      found = found || menu.hide();
    });

    if (found) {
      return;
    }

    this._menus.forEach((menu) => {
      if (found || menu.position() !== position) {
        return;
      }

      found = menu.show();
    });
  }

  _opposite(position) {
    return position === 'left' ? 'right' : 'left';
  }
}
<|endoftext|>


<commit_before>'use strict';

const
	$C = require('collection.js'),
	Sugar = require('sugar'),
	path = require('path'),
	fs = require('fs-extra-promise'),
	glob = require('glob-promise');

const
	resolve = require('./resolve'),
	Declaration = require('./declaration'),
	{blockTypeList} = /** @type {{blockTypeList: Array<string>}} */ require('./validators');

const
	filesCache = Object.create(null);

async function getFile(path) {
	const
		{mtime} = await fs.statAsync(path);

	if (!filesCache[path] || !Sugar.Date.is(mtime, filesCache[path].mtime)) {
		filesCache[path] = {
			mtime,
			content: await fs.readFileAsync(path, 'utf-8')
		};
	}

	return filesCache[path].content;
}

class Block {
	/**
	 * Returns a block manifest file by the specified name
	 *
	 * @param {string} name
	 * @returns {!Promise<Block>}
	 */
	static async get(name) {
		const decl = new Declaration(await getFile(
			path.join(await resolve.block(name), 'index.js')
		));

		if (decl.mixin) {
			let
				skip = 1,
				parent;

			while ((parent = resolve.block(this.name, skip))) {
				const
					parentDecl = new Declaration(await getFile(path.join(parent.result, 'index.js')));

				if (!decl.parent && parentDecl.parent) {
					decl.parent = parentDecl.parent;
				}

				decl.dependencies = [...new Set(decl.dependencies.concat(parentDecl.dependencies))];
				decl.libs = [...new Set(decl.libs.concat(parentDecl.libs))];

				if (!parentDecl.mixin) {
					skip = parent.from;
					break;
				}
			}
		}

		return new this(decl);
	}

	/**
	 * Returns block manifests file by the specified names
	 *
	 * @param {Array<string>=} [names]
	 * @returns {!Promise<!Array<!Block>>}
	 */
	static async getAll(names) {
		if (!names) {
			const
				b = `@(${blockTypeList.join('|')})-*`,
				components = `/**/${b}/index.js`,
				virtualComponents = `/**/${b}.index.js`;

			names = await $C([await resolve.block(), ...resolve.dependencies]).parallel().reduce(async (list, root) => {
				const files = await Promise.all([
					glob(path.join(root, components)),
					glob(path.join(root, virtualComponents))
				]);

				return list.concat(
					$C([].concat(...files)).map((nm) => path.dirname(path.relative(root, nm)))
				);
			}, []);

			names = [...new Set(names)];
		}

		return $C(names).parallel().reduce(
			async (res, name, i) => {
				const
					block = await this.get(name);

				if (block) {
					res[i] = block;
				}

				return res;
			},

			[]
		);
	}

	/**
	 * Block name
	 * @returns {string}
	 */
	get name() {
		return this.declaration.name;
	}

	/**
	 * Block type
	 * @returns {string}
	 */
	get type() {
		return this.declaration.type;
	}

	/**
	 * Block parent
	 * @returns {?string}
	 */
	get parent() {
		return this.declaration.parent;
	}

	/**
	 * Block mixin status
	 * @returns {boolean}
	 */
	get mixin() {
		return this.declaration.mixin;
	}

	/**
	 * Block dependencies
	 * @returns {!Array<string>}
	 */
	get dependencies() {
		return this.declaration.dependencies;
	}

	/**
	 * Block libraries
	 * @returns {!Array<string>}
	 */
	get libs() {
		return this.declaration.libs;
	}

	/**
	 * @param declaration - block declaration
	 */
	constructor(declaration) {
		this.declaration = declaration;
	}

	/**
	 * Returns the block parent manifest
	 * @returns {!Promise<Block>}
	 */
	async getParent() {
		return this.parent ? this.constructor.get(this.parent) : null;
	}

	/**
	 * Returns manifests of all block dependencies
	 *
	 * @param {boolean=} [onlyOwn] - if true parent dependencies also included
	 * @returns {!Promise<!Array<!Block>>}
	 */
	async getDependencies(onlyOwn = false) {
		const
			names = this.dependencies.slice();

		if (!onlyOwn) {
			let
				parent = await this.getParent();

			while (parent) {
				Sugar.Array.insert(names, parent.dependencies, 0);
				parent = await parent.getParent();
			}
		}

		return this.constructor.getAll(Sugar.Array.unique(names));
	}

	/**
	 * Returns manifests of all block libraries
	 *
	 * @param {boolean=} [onlyOwn] - if true parent libraries also included
	 * @returns {!Promise<!Array<string>>}
	 */
	async getLibs(onlyOwn = false) {
		const
			libs = this.libs.slice();

		if (!onlyOwn) {
			let
				parent = await this.getParent();

			while (parent) {
				Sugar.Array.insert(libs, parent.libs, 0);
				parent = await parent.getParent();
			}
		}

		return libs;
	}
}

module.exports = Block;
<commit_msg>Improved mixins<commit_after>'use strict';

const
	$C = require('collection.js'),
	Sugar = require('sugar'),
	path = require('path'),
	fs = require('fs-extra-promise'),
	glob = require('glob-promise');

const
	resolve = require('./resolve'),
	Declaration = require('./declaration'),
	{blockTypeList} = /** @type {{blockTypeList: Array<string>}} */ require('./validators');

const
	filesCache = Object.create(null),
	blockCache = Object.create(null);

async function validateCache(path, cache) {
	const
		{mtime} = await fs.statAsync(path);

	return {
		mtime,
		fromCache: Boolean(cache[path] && Sugar.Date.is(mtime, cache[path].mtime))
	};
}

async function getFile(path) {
	const
		cache = await validateCache(path, filesCache);

	if (!cache.fromCache) {
		filesCache[path] = {
			mtime: cache.mtime,
			content: await fs.readFileAsync(path, 'utf-8')
		};
	}

	return filesCache[path].content;
}

class Block {
	/**
	 * Returns a block manifest file by the specified name
	 *
	 * @param {string} name
	 * @returns {!Promise<Block>}
	 */
	static async get(name) {
		const
			blockPath = path.join(await resolve.block(name), 'index.js'),
			cache = await validateCache(blockPath, blockCache);

		if (cache.fromCache) {
			return new this(blockCache[blockPath].decl);
		}

		async function getDecl(url) {
			return new Declaration(await getFile(path.join(await url, 'index.js')));
		}

		const
			decl = await getDecl(resolve.block(name));

		if (decl.mixin) {
			let
				skip = 1,
				parent;

			while ((parent = await resolve.block(decl.name, skip))) {
				const
					parentDecl = await getDecl(parent.path, 1);

				if (!decl.parent && parentDecl.parent) {
					decl.parent = parentDecl.parent;
				}

				decl.dependencies = [...new Set(decl.dependencies.concat(parentDecl.dependencies))];
				decl.libs = [...new Set(decl.libs.concat(parentDecl.libs))];

				if (parentDecl.mixin) {
					skip = parent.from;

				} else {
					break;
				}
			}
		}

		blockCache[blockPath] = {
			mtime: cache.mtime,
			decl
		};

		return new this(decl);
	}

	/**
	 * Returns block manifests file by the specified names
	 *
	 * @param {Array<string>=} [names]
	 * @returns {!Promise<!Array<!Block>>}
	 */
	static async getAll(names) {
		if (!names) {
			const
				b = `@(${blockTypeList.join('|')})-*`,
				components = `/**/${b}/index.js`,
				virtualComponents = `/**/${b}.index.js`;

			names = await $C([await resolve.block(), ...resolve.dependencies]).parallel().reduce(async (list, root) => {
				const files = await Promise.all([
					glob(path.join(root, components)),
					glob(path.join(root, virtualComponents))
				]);

				return list.concat(
					$C([].concat(...files)).map((nm) => path.dirname(path.relative(root, nm)))
				);
			}, []);

			names = [...new Set(names)];
		}

		return $C(names).parallel().reduce(
			async (res, name, i) => {
				const
					block = await this.get(name);

				if (block) {
					res[i] = block;
				}

				return res;
			},

			[]
		);
	}

	/**
	 * Block name
	 * @returns {string}
	 */
	get name() {
		return this.declaration.name;
	}

	/**
	 * Block type
	 * @returns {string}
	 */
	get type() {
		return this.declaration.type;
	}

	/**
	 * Block parent
	 * @returns {?string}
	 */
	get parent() {
		return this.declaration.parent;
	}

	/**
	 * Block mixin status
	 * @returns {boolean}
	 */
	get mixin() {
		return this.declaration.mixin;
	}

	/**
	 * Block dependencies
	 * @returns {!Array<string>}
	 */
	get dependencies() {
		return this.declaration.dependencies;
	}

	/**
	 * Block libraries
	 * @returns {!Array<string>}
	 */
	get libs() {
		return this.declaration.libs;
	}

	/**
	 * @param declaration - block declaration
	 */
	constructor(declaration) {
		this.declaration = declaration;
	}

	/**
	 * Returns the block parent manifest
	 * @returns {!Promise<Block>}
	 */
	async getParent() {
		return this.parent ? this.constructor.get(this.parent) : null;
	}

	/**
	 * Returns manifests of all block dependencies
	 *
	 * @param {boolean=} [onlyOwn] - if true parent dependencies also included
	 * @returns {!Promise<!Array<!Block>>}
	 */
	async getDependencies(onlyOwn = false) {
		const
			names = this.dependencies.slice();

		if (!onlyOwn) {
			let
				parent = await this.getParent();

			while (parent) {
				Sugar.Array.insert(names, parent.dependencies, 0);
				parent = await parent.getParent();
			}
		}

		return this.constructor.getAll(Sugar.Array.unique(names));
	}

	/**
	 * Returns manifests of all block libraries
	 *
	 * @param {boolean=} [onlyOwn] - if true parent libraries also included
	 * @returns {!Promise<!Array<string>>}
	 */
	async getLibs(onlyOwn = false) {
		const
			libs = this.libs.slice();

		if (!onlyOwn) {
			let
				parent = await this.getParent();

			while (parent) {
				Sugar.Array.insert(libs, parent.libs, 0);
				parent = await parent.getParent();
			}
		}

		return libs;
	}
}

module.exports = Block;
<|endoftext|>


<commit_before>import React from 'react'
import { OverlayTrigger, Button, Tooltip, Modal }
  from 'react-bootstrap'

const ModalOverlay = React.createClass({
  getInitialState () {
    return { showModal: false }
  },

  close () {
    this.setState({ showModal: false })
  },

  open () {
    this.setState({ showModal: true })
  },

  render () {
    const tooltip = (
      <Tooltip id='modal-tooltip'>
        wow.
      </Tooltip>
    )

    return (

Demo modal

        <Button
          bsStyle='primary'
          bsSize='large'
          onClick={this.open}
        >
          Launch
        </Button>

        <Modal show={this.state.showModal} onHide={this.close}>
          <Modal.Header closeButton>
            <Modal.Title>Modal heading</Modal.Title>
          </Modal.Header>
          <Modal.Body>

Text in a modal

Duis mollis, est non commodo luctus,
            nisi erat porttitor ligula.

There is a <OverlayTrigger overlay={tooltip}>
              tooltip
              </OverlayTrigger> here

          </Modal.Body>
          <Modal.Footer>
            <Button onClick={this.close} className='btn btn-default'>
            Close</Button>
          </Modal.Footer>
        </Modal>

    )
  }
})

export default ModalOverlay
<commit_msg>WIP: fixed modal linking errors<commit_after>import React from 'react'
import { OverlayTrigger, Button, Tooltip, Modal }
  from 'react-bootstrap'

const ModalOverlay = React.createClass({
  getInitialState () {
    return { showModal: false }
  },

  close () {
    this.setState({ showModal: false })
  },

  open () {
    this.setState({ showModal: true })
  },

  render () {
    const tooltip = (
      <Tooltip id='modal-tooltip'>
        wow.
      </Tooltip>
    )

    return (

Demo modal

        <Button
          bsStyle='primary'
          bsSize='large'
          onClick={this.open}
        >
          Launch
        </Button>

        <Modal show={this.state.showModal} onHide={this.close}>
          <Modal.Header closeButton>
            <Modal.Title>Modal heading</Modal.Title>
          </Modal.Header>
          <Modal.Body>

Text in a modal

Duis mollis, est non commodo luctus,
            nisi erat porttitor ligula.

There is a <OverlayTrigger overlay={tooltip}>
              tooltip
              </OverlayTrigger> here

          </Modal.Body>
          <Modal.Footer>
            <Button onClick={this.close} className='btn btn-default'>
            Close</Button>
          </Modal.Footer>
        </Modal>

    )
  }
})

export default ModalOverlay
<|endoftext|>