<commit_before>// Order Line Body functions
// ------------------------------------------------------------------------
function calculateRate(p1, p2) {
    return 100 * (1 - p1 / p2);
}
function calculateTax(n) {
    return n * 0.0875;
}
function calculatePrice(quantity, price, tax, shipping, discount) {
    return quantity * price + tax + shipping + discount;
}

// Calculate details price changed
function updateBodySection(tr) {

    var unitePrice = parseFloat(tr.find("td:nth-child(2) input").val());
    var quantity   = parseInt(tr.children("td:nth-child(3)").text());
    var tax        = parseFloat(tr.find("td:nth-child(4) input").val());
    var discount   = parseFloat(tr.find("td:nth-child(5) input").val());
    var shipping   = parseFloat(tr.find("td:nth-child(6) input").val());

    var total = calculatePrice(quantity, unitePrice, tax, shipping, discount);

    tr.children("td:last-child").text(total.toFixed(2));

    updateItem(tr.parentsUntil("div.order_line").parent());
}

function unitPriceChanged() {
    var price = parseFloat($(this).val());

    var quantityTag = $(this).parent().next();
    var quantity = parseInt(quantityTag.text());

    var tax = calculateTax(price * quantity).toFixed(2);
    quantityTag.next().children("input").val(tax);

    updateBodySection($(this).parentsUntil("tbody"));
}

function numValueChanged() {
    updateBodySection($(this).parentsUntil("tbody"));
}

// Order Line Header functions
// ------------------------------------------------------------------------

function quantityChanged() {
    var q = $(this).val();

    var bodyTable = $(this).parentsUntil("div.order_line").next();

    var purchaseTr = bodyTable.find("tr.item_purchase");
    var sellTr     = bodyTable.find("tr.item_sell");

    purchaseTr.find("td:nth-child(3)").text(q);
    sellTr.find("td:nth-child(3)").text(q);

    // quantity changed, need to recalculate tax.
    unitPriceChanged.apply(purchaseTr.find("td:nth-child(2) input"));
    unitPriceChanged.apply(sellTr.find("td:nth-child(2) input"));
}

// Update item header
function updateItem(line) {
    var headerTable = line.children(".item_header");
    var totalTag    = headerTable.find(".item_total");
    var profitTag   = headerTable.find(".item_profit");

    var bodyTable        = line.children(".item_body");
    var purchaseTotalTag = bodyTable.find(".total_purchase");
    var sellTotalTag     = bodyTable.find(".total_sell");

    var purchaseTotal = parseFloat(purchaseTotalTag.text());
    var sellTotal     = parseFloat(sellTotalTag.text());
    var profit        = sellTotal - purchaseTotal;

    totalTag.text(sellTotal.toFixed(2));
    profitTag.text(profit.toFixed(2));

    updateOrder();
}

function productChanged() {
    var pid = $(this).val();
    var product = null;
    $.each(products.list, function(i, p) {
        if (p.id == pid) {
            product = p;
            return;
        }
    });

    if (product != null) {
        $(this).next().val(pid);

        var td = $(this).parent();
        td.next().text(product.price);

        var headerTable = $(this).parentsUntil("div.order_line");
        var bodyTable = headerTable.next();

        var p1 = bodyTable.find("tr.item_purchase td:nth-child(2) input");
        p1.val(product.price);
        unitPriceChanged.apply(p1);

        var p2 = bodyTable.find("tr.item_sell td:nth-child(2) input");
        p2.val(product.price);
        unitPriceChanged.apply(p2);

        bodyTable.show();
    }
}

// function updateRate() {
//     var v = parseFloat($(this).val());
//     var rate = 1.0 +  v / 100;
//     var discount = parseFloat($(this).parent().prev().children("input").val());
//     var sellPriceTag = $(this).parent().next().children("input");

//     sellPriceTag.val((discount * rate).toFixed(2));

//     settleAccount($(this).parent().parent());
// }

// Order functions
// ------------------------------------------------------------------------
// recalculate account balance
function updateOrder() {
    var sum = 0.0;
    $(".item_total").each(function(){
        sum += parseFloat($(this).text());
    });
    sum = sum.toFixed(2);
    $("#order_total").text(sum);
    $("#total").val(sum);

    sum = 0.0
    $(".item_profit").each(function(){
        sum += parseFloat($(this).text());
    });
    sum = sum.toFixed(2);
    $("#order_profit").text(sum);
    $("#profit").val(sum);
}

function deleteLine() {
    $(this).parentsUntil("div").parent().remove();

    updateOrderAccount();
}

function addOrderLine(prods) {
    var count = $(".order_line").length;
    var optionlist = "";
    $.each(prods.list, function(i, p) {
        optionlist += $('<option>').text(p.name).attr('value', p.id).prop('outerHTML');
    });

    var str = '
' +
        '
' +
        '
Product	List Price	Quantity	Total	Profit	Note
' +
        '
' +
        '<select name="newItems[0].name" onchange="productChanged.apply(this);">' +
        '<option value="">Select one product ...</option>'  + optionlist +
        '</select><input type="hidden" name="newItems[' + count + '].pid" value="0"/>	' +
        '0.0	' +
        '<input type="number" name="newItems[' + count + '].quantity" value="1" min="1" max="1000" onchange="quantityChanged.apply(this);" /> 	' +
        '0.0	' +
        '0.0	' +
        '<input type="text" name="newItems[' + count + '].note" />	' +
        '<button type="button" onclick="deleteLine.apply(this)">Remove</button>	' +
        '

' +
        '
' +
        '
	Unit Price	Quantity	Tax	Discount	Shipping	Total
' +
        '
' +
        'Purchase	' +
        '<input type="text" name="newItems[' + count + '].purchase.price" value="0.0" onchange="unitPriceChanged.apply(this);"/>	' +
        '1	' +
        '<input type="text" name="newItems[' + count + '].purchase.tax" value="0.0" onchange="numValueChanged.apply(this);"/>	' +
        '<input type="text" name="newItems[' + count + '].purchase.discount" value="-0.0" onchange="numValueChanged.apply(this);"/>	' +
        '<input type="text" name="newItems[' + count + '[.purchase.shipping" value="0.0" onchange="numValueChanged.apply(this);"/>	' +
        '0.0	' +
        '

	<input type="text" size="2">
' +
        '
' +
        'Sell	' +
        '<input type="text" name="newItems[' + count + '].sell.price" value="0.0" onchange="unitPriceChanged.apply(this);">	' +
        '1	' +
        '<input type="text" name="newItems[' + count + '].sell.tax" value="0.0" onchange="numValueChanged.apply(this);">	' +
        '<input type="text" name="newItems[' + count + '].sell.discount" value="-0.0" onchange="numValueChanged.apply(this);"/>	' +
        '<input type="text" name="newItems[' + count + '].sell.shipping" value="0.0" onchange="numValueChanged.apply(this);">	' +
        '0.0	' +
        '

';
    var line = $(str);
    line.find(".item_body").hide();

    $("#items").prepend(line);
}

<commit_msg>fix shipping error<commit_after>// Order Line Body functions
// ------------------------------------------------------------------------
function calculateRate(p1, p2) {
    return 100 * (1 - p1 / p2);
}
function calculateTax(n) {
    return n * 0.0875;
}
function calculatePrice(quantity, price, tax, shipping, discount) {
    return quantity * price + tax + shipping + discount;
}

// Calculate details price changed
function updateBodySection(tr) {

    var unitePrice = parseFloat(tr.find("td:nth-child(2) input").val());
    var quantity   = parseInt(tr.children("td:nth-child(3)").text());
    var tax        = parseFloat(tr.find("td:nth-child(4) input").val());
    var discount   = parseFloat(tr.find("td:nth-child(5) input").val());
    var shipping   = parseFloat(tr.find("td:nth-child(6) input").val());

    var total = calculatePrice(quantity, unitePrice, tax, shipping, discount);

    tr.children("td:last-child").text(total.toFixed(2));

    updateItem(tr.parentsUntil("div.order_line").parent());
}

function unitPriceChanged() {
    var price = parseFloat($(this).val());

    var quantityTag = $(this).parent().next();
    var quantity = parseInt(quantityTag.text());

    var tax = calculateTax(price * quantity).toFixed(2);
    quantityTag.next().children("input").val(tax);

    updateBodySection($(this).parentsUntil("tbody"));
}

function numValueChanged() {
    updateBodySection($(this).parentsUntil("tbody"));
}

// Order Line Header functions
// ------------------------------------------------------------------------

function quantityChanged() {
    var q = $(this).val();

    var bodyTable = $(this).parentsUntil("div.order_line").next();

    var purchaseTr = bodyTable.find("tr.item_purchase");
    var sellTr     = bodyTable.find("tr.item_sell");

    purchaseTr.find("td:nth-child(3)").text(q);
    sellTr.find("td:nth-child(3)").text(q);

    // quantity changed, need to recalculate tax.
    unitPriceChanged.apply(purchaseTr.find("td:nth-child(2) input"));
    unitPriceChanged.apply(sellTr.find("td:nth-child(2) input"));
}

// Update item header
function updateItem(line) {
    var headerTable = line.children(".item_header");
    var totalTag    = headerTable.find(".item_total");
    var profitTag   = headerTable.find(".item_profit");

    var bodyTable        = line.children(".item_body");
    var purchaseTotalTag = bodyTable.find(".total_purchase");
    var sellTotalTag     = bodyTable.find(".total_sell");

    var purchaseTotal = parseFloat(purchaseTotalTag.text());
    var sellTotal     = parseFloat(sellTotalTag.text());
    var profit        = sellTotal - purchaseTotal;

    totalTag.text(sellTotal.toFixed(2));
    profitTag.text(profit.toFixed(2));

    updateOrder();
}

function productChanged() {
    var pid = $(this).val();
    var product = null;
    $.each(products.list, function(i, p) {
        if (p.id == pid) {
            product = p;
            return;
        }
    });

    if (product != null) {
        $(this).next().val(pid);

        var td = $(this).parent();
        td.next().text(product.price);

        var headerTable = $(this).parentsUntil("div.order_line");
        var bodyTable = headerTable.next();

        var p1 = bodyTable.find("tr.item_purchase td:nth-child(2) input");
        p1.val(product.price);
        unitPriceChanged.apply(p1);

        var p2 = bodyTable.find("tr.item_sell td:nth-child(2) input");
        p2.val(product.price);
        unitPriceChanged.apply(p2);

        bodyTable.show();
    }
}

// function updateRate() {
//     var v = parseFloat($(this).val());
//     var rate = 1.0 +  v / 100;
//     var discount = parseFloat($(this).parent().prev().children("input").val());
//     var sellPriceTag = $(this).parent().next().children("input");

//     sellPriceTag.val((discount * rate).toFixed(2));

//     settleAccount($(this).parent().parent());
// }

// Order functions
// ------------------------------------------------------------------------
// recalculate account balance
function updateOrder() {
    var sum = 0.0;
    $(".item_total").each(function(){
        sum += parseFloat($(this).text());
    });
    sum = sum.toFixed(2);
    $("#order_total").text(sum);
    $("#total").val(sum);

    sum = 0.0
    $(".item_profit").each(function(){
        sum += parseFloat($(this).text());
    });
    sum = sum.toFixed(2);
    $("#order_profit").text(sum);
    $("#profit").val(sum);
}

function deleteLine() {
    $(this).parentsUntil("div").parent().remove();

    updateOrderAccount();
}

function addOrderLine(prods) {
    var count = $(".order_line").length;
    var optionlist = "";
    $.each(prods.list, function(i, p) {
        optionlist += $('<option>').text(p.name).attr('value', p.id).prop('outerHTML');
    });

    var str = '
' +
        '
' +
        '
Product	List Price	Quantity	Total	Profit	Note
' +
        '
' +
        '<select name="newItems[0].name" onchange="productChanged.apply(this);">' +
        '<option value="">Select one product ...</option>'  + optionlist +
        '</select><input type="hidden" name="newItems[' + count + '].pid" value="0"/>	' +
        '0.0	' +
        '<input type="number" name="newItems[' + count + '].quantity" value="1" min="1" max="1000" onchange="quantityChanged.apply(this);" /> 	' +
        '0.0	' +
        '0.0	' +
        '<input type="text" name="newItems[' + count + '].note" />	' +
        '<button type="button" onclick="deleteLine.apply(this)">Remove</button>	' +
        '

' +
        '
' +
        '
	Unit Price	Quantity	Tax	Discount	Shipping	Total
' +
        '
' +
        'Purchase	' +
        '<input type="text" name="newItems[' + count + '].purchase.price" value="0.0" onchange="unitPriceChanged.apply(this);"/>	' +
        '1	' +
        '<input type="text" name="newItems[' + count + '].purchase.tax" value="0.0" onchange="numValueChanged.apply(this);"/>	' +
        '<input type="text" name="newItems[' + count + '].purchase.discount" value="-0.0" onchange="numValueChanged.apply(this);"/>	' +
        '<input type="text" name="newItems[' + count + '].purchase.shipping" value="0.0" onchange="numValueChanged.apply(this);"/>	' +
        '0.0	' +
        '

	<input type="text" size="2">
' +
        '
' +
        'Sell	' +
        '<input type="text" name="newItems[' + count + '].sell.price" value="0.0" onchange="unitPriceChanged.apply(this);">	' +
        '1	' +
        '<input type="text" name="newItems[' + count + '].sell.tax" value="0.0" onchange="numValueChanged.apply(this);">	' +
        '<input type="text" name="newItems[' + count + '].sell.discount" value="-0.0" onchange="numValueChanged.apply(this);"/>	' +
        '<input type="text" name="newItems[' + count + '].sell.shipping" value="0.0" onchange="numValueChanged.apply(this);">	' +
        '0.0	' +
        '

';
    var line = $(str);
    line.find(".item_body").hide();

    $("#items").prepend(line);
}

<|endoftext|>


<commit_before>/*
 * field parsing and creating utilities
 */

import {makeSentenceCase} from './string';
import stache from 'can-stache';
import DefineMap from 'can-define/map/map';
import assign from 'object-assign';
import dev from 'can-util/js/dev/dev';

const TEMPLATES = {
    text: '<text-field {properties}="." (change)="setField" value="{{getFieldValue(.)}}" {errors}="validationErrors" />',
    select: '<select-field {properties}="." (change)="setField" value="{{getFieldValue(.)}}" {errors}="validationErrors" />',
    file: '<file-field {properties}="." (change)="setField" value="{{getFieldValue(.)}}" {errors}="validationErrors" />',
    json: '<json-field {properties}="." (change)="setField" {value}="getFieldValue(.)" {errors}="validationErrors" />'
};

//precompile templates
for (var type in TEMPLATES) {
    if (TEMPLATES.hasOwnProperty(type)) {
        TEMPLATES[type] = stache(TEMPLATES[type]);
    }
}

export {
    TEMPLATES
};

/**
 * @module util/field.Field FieldDefinition
 * @parent spectre.types
 *
 */
export const Field = DefineMap.extend('Field', {
    /**
     * The name of the property on the object, this field's name
     * @property {String} util/field.Field.name
     */
    name: 'string',
    /**
     * A friendly name for the field used to display to the user
     * The default is to capitalize the name and remove underscores
     * @property {String} util/field.Field.alias
     */
    alias: {
        type: 'string',
        get (alias) {
            if (alias) {
                return alias;
            }
            return makeSentenceCase(this.name);
        }
    },
    /**
     * A friendly name for the field used to display to the user
     * The default is to capitalize the name and remove underscores
     * @property {String} util/field.Field.alias
     */
    type: {
        type: 'string',
        value: 'string'
    },
    /**
     * The type of the form field to use when editing this field. These types
     * are defined in the `util/field.TEMPLATES` constant
     * @property {String} util/field.Field.type
     */
    fieldType: {
        type: 'string',
        value: 'text'
    },
    /**
     * The form field template to use when editing this field. This should be
     * a stache template renderer. By default, this value is set to the
     * template for the given `type` property.
     * @property {Renderer}
     */
    formTemplate: {
        get (template) {
            if (template) {
                return template;
            }
            const fType = this.fieldType;
            if (!TEMPLATES.hasOwnProperty(fType)) {
                dev.warn('No template for the given field type', fType);
                return TEMPLATES.text;
            }
            return TEMPLATES[fType];
        }
    },
    /**
     * Excludes this field from the list-table
     * @property {Boolean}
     */
    excludeListTable: {
        value: false
    },
    /**
     * Excludes this field from the property-table
     * @property {Boolean}
     */
    excludePropertyTable: {
        value: false
    },
    /**
     * Excludes this field from the form-widget
     * @property {Boolean}
     */
    excludeForm: {
        value: false
    },
    /**
     * Formats the property when it is displayed in a property or list table
     * @property {Function}
     */
    formatter: {
        value: null
    },
    /**
     * Validates a property and returns a string if the field is invalid
     * @property {Function}
     */
    validate: {
        value: null
    },
    getFormattedValue (obj) {
        return this.formatter ? this.formatter(obj[this.name], obj) : obj[this.name];
    }
});

/**
 * Converts an array of strings or field json objects into Field objects
 * @function util/field.parseFieldArray
 * @param  {Array<Field | String>} fields An array of either strings or JSON like objects representing Field object properties
 * @return {Array<Field>} The array of fields
 */
export function parseFieldArray (fields) {
    // create field objects
    return fields.map((f) => {
        // if we have a string give it a default name
        if (typeof f === 'string') {
            f = {
                name: f
            };
        }
        // add additional props with field constructor
        return new Field(f);

        // filter fields to exclude any '__' hidden props
    }).filter((f) => {
        return f.name.indexOf('__') === -1;
    });
}

/**
 * Converts a DefineMap to an array of Field objects using the define
 * property or the keys
 * @function mapToFields
 * @param  {Constructor<DefineMap>} defineMap The extended map/constructor to parse
 * @return {Array<Field>} The array of fields
 */
export function mapToFields (defineMap) {
    if (!defineMap) {
        dev.warn('map is undefined, so no fields will be generated');
        return [];
    }
    const define = assign({}, (defineMap._define || defineMap.prototype._define).definitions);
    const fields = [];
    for (var prop in define) {
        if (define.hasOwnProperty(prop)) {
            const fType = typeof define[prop].type === 'function' ? define[prop].type.name : define[prop].type;
            fields.push(assign({}, {
                name: prop,
                type: 'string',
                fieldType: 'text'
            }, define[prop], {
                type: fType
            }));
        }
    }
    return parseFieldArray(fields);
}
<commit_msg>fix: reserved methods shouldn't be created in fields<commit_after>/*
 * field parsing and creating utilities
 */

import {makeSentenceCase} from './string';
import stache from 'can-stache';
import DefineMap from 'can-define/map/map';
import assign from 'object-assign';
import dev from 'can-util/js/dev/dev';

const TEMPLATES = {
    text: '<text-field {properties}="." (change)="setField" value="{{getFieldValue(.)}}" {errors}="validationErrors" />',
    select: '<select-field {properties}="." (change)="setField" value="{{getFieldValue(.)}}" {errors}="validationErrors" />',
    file: '<file-field {properties}="." (change)="setField" value="{{getFieldValue(.)}}" {errors}="validationErrors" />',
    json: '<json-field {properties}="." (change)="setField" {value}="getFieldValue(.)" {errors}="validationErrors" />'
};

//precompile templates
for (var type in TEMPLATES) {
    if (TEMPLATES.hasOwnProperty(type)) {
        TEMPLATES[type] = stache(TEMPLATES[type]);
    }
}

export {TEMPLATES};

export const RESERVED = [
    'get',
    'set',
    'serialize'
];

/**
 * @module util/field.Field FieldDefinition
 * @parent spectre.types
 *
 */
export const Field = DefineMap.extend('Field', {
    /**
     * The name of the property on the object, this field's name
     * @property {String} util/field.Field.name
     */
    name: 'string',
    /**
     * A friendly name for the field used to display to the user
     * The default is to capitalize the name and remove underscores
     * @property {String} util/field.Field.alias
     */
    alias: {
        type: 'string',
        get (alias) {
            if (alias) {
                return alias;
            }
            return makeSentenceCase(this.name);
        }
    },
    /**
     * A friendly name for the field used to display to the user
     * The default is to capitalize the name and remove underscores
     * @property {String} util/field.Field.alias
     */
    type: {
        type: 'string',
        value: 'string'
    },
    /**
     * The type of the form field to use when editing this field. These types
     * are defined in the `util/field.TEMPLATES` constant
     * @property {String} util/field.Field.type
     */
    fieldType: {
        type: 'string',
        value: 'text'
    },
    /**
     * The form field template to use when editing this field. This should be
     * a stache template renderer. By default, this value is set to the
     * template for the given `type` property.
     * @property {Renderer}
     */
    formTemplate: {
        get (template) {
            if (template) {
                return template;
            }
            const fType = this.fieldType;
            if (!TEMPLATES.hasOwnProperty(fType)) {
                dev.warn('No template for the given field type', fType);
                return TEMPLATES.text;
            }
            return TEMPLATES[fType];
        }
    },
    /**
     * Excludes this field from the list-table
     * @property {Boolean}
     */
    excludeListTable: {
        value: false
    },
    /**
     * Excludes this field from the property-table
     * @property {Boolean}
     */
    excludePropertyTable: {
        value: false
    },
    /**
     * Excludes this field from the form-widget
     * @property {Boolean}
     */
    excludeForm: {
        value: false
    },
    /**
     * Formats the property when it is displayed in a property or list table
     * @property {Function}
     */
    formatter: {
        value: null
    },
    /**
     * Validates a property and returns a string if the field is invalid
     * @property {Function}
     */
    validate: {
        value: null
    },
    getFormattedValue (obj) {
        return this.formatter ? this.formatter(obj[this.name], obj) : obj[this.name];
    }
});

/**
 * Converts an array of strings or field json objects into Field objects
 * @function util/field.parseFieldArray
 * @param  {Array<Field | String>} fields An array of either strings or JSON like objects representing Field object properties
 * @return {Array<Field>} The array of fields
 */
export function parseFieldArray (fields) {
    // create field objects
    return fields.map((f) => {
        // if we have a string give it a default name
        if (typeof f === 'string') {
            f = {
                name: f
            };
        }
        // add additional props with field constructor
        return new Field(f);

        // filter fields to exclude any '__' hidden props
    }).filter((f) => {
        return f.name.indexOf('__') === -1;
    });
}

/**
 * Converts a DefineMap to an array of Field objects using the define
 * property or the keys
 * @function mapToFields
 * @param  {Constructor<DefineMap>} defineMap The extended map/constructor to parse
 * @return {Array<Field>} The array of fields
 */
export function mapToFields (defineMap) {
    if (!defineMap) {
        dev.warn('map is undefined, so no fields will be generated');
        return [];
    }
    const define = assign({}, (defineMap._define || defineMap.prototype._define).definitions);
    const fields = [];
    for (var prop in define) {
        if (define.hasOwnProperty(prop)) {
            const fType = typeof define[prop].type === 'function' ? define[prop].type.name : define[prop].type;

            // remove reserved properties
            const clone = assign({}, define[prop]);
            RESERVED.forEach((r) => {
                delete clone[r];
            });

            fields.push(assign({}, {
                name: prop,
                type: 'string',
                fieldType: 'text'
            }, clone, {
                type: fType
            }));
        }
    }
    return parseFieldArray(fields);
}
<|endoftext|>


<commit_before>/**
 * Type
 *
 * A library of functions that take a file path and return a boolean
 * representing whether the given file path is valid within the
 * BIDS specification requirements.
 */
var anatSuffixes = ["T1w", "T2w", "T1map", "T2map", "T1rho", "FLAIR", "PD", "PDT2", "inplaneT1", "inplaneT2","angio",
    "defacemask", "SWImagandphase", "T2star", "FLASH", "PDmap"];

module.exports = {

    /**
     * Is BIDS
     *
     * Check if a given path is valid within the
     * bids spec.
     */
    isBIDS: function (path) {
        return (
            this.isTopLevel(path) ||
            this.isStimuliData(path) ||
            this.isSessionLevel(path) ||
            this.isSubjectLevel(path) ||
            this.isAnat(path) ||
            this.isDWI(path) ||
            this.isFunc(path) ||
            this.isBehavioral(path) ||
            this.isCont(path) ||
            this.isFieldMap(path) ||
            this.isPhenotypic(path)
        );
    },

    /**
     * Check if the file has appropriate name for a top level file
     */
    isTopLevel: function (path) {
        var fixedTopLevelNames = ["/README", "/CHANGES", "/dataset_description.json", "/participants.tsv",
            "/participants.json", "/phasediff.json", "/phase1.json", "/phase2.json", "/fieldmap.json"];

        var funcTopRe = new RegExp('^\\/(?:ses-[a-zA-Z0-9]+_)?(?:recording-[a-zA-Z0-9]+_)?task-[a-zA-Z0-9]+(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?(?:_echo-[0-9]+)?'
            + '(_bold.json|_sbref.json|_events.json|_events.tsv|_physio.json|_stim.json|_beh.json)$');

        var anatTopRe = new RegExp('^\\/(?:ses-[a-zA-Z0-9]+_)?(?:acq-[a-zA-Z0-9]+_)?(?:rec-[a-zA-Z0-9]+_)?(?:run-[0-9]+_)?'
            + '(' + anatSuffixes.join("|") + ').json$');

        var dwiTopRe = new RegExp('^\\/(?:ses-[a-zA-Z0-9]+_)?(?:acq-[a-zA-Z0-9]+_)?(?:rec-[a-zA-Z0-9]+_)?(?:run-[0-9]+_)?'
            + 'dwi.(?:json|bval|bvec)$');

        var multiDirFieldmapRe = new RegExp('^\\/(?:dir-[a-zA-Z0-9]+)_epi.json$');

        var otherTopFiles = new RegExp('^\\/(?:ses-[a-zA-Z0-9]+_)?(?:recording-[a-zA-Z0-9]+_)?(?:task-[a-zA-Z0-9]+_)?(?:acq-[a-zA-Z0-9]+_)?(?:rec-[a-zA-Z0-9]+_)?(?:run-[0-9]+_)?'
            + '(physio.json|stim.json)$');

        return (fixedTopLevelNames.indexOf(path) != -1 || funcTopRe.test(path) || dwiTopRe.test(path) ||
        anatTopRe.test(path) || multiDirFieldmapRe.test(path) || otherTopFiles.test(path));
    },

    /**
     * Check if file is appropriate associated data.
     */
    isAssociatedData: function (path) {
        var associatedData = new RegExp('^\\/(?:code|derivatives|sourcedata|stimuli|[.]git)\\/(?:.*)$');
        return associatedData.test(path);
    },

    isStimuliData: function (path) {
        var stimuliDataRe = new RegExp('^\\/(?:stimuli)\\/(?:.*)$');
        return stimuliDataRe.test(path);
    },

    /**
     * Check if file is phenotypic data.
     */
    isPhenotypic: function (path) {
        var phenotypicData = new RegExp('^\\/(?:phenotype)\\/(?:.*.tsv|.*.json)$');
        return phenotypicData.test(path);
    },

    /**
     * Check if the file has appropriate name for a session level
     */
    isSessionLevel: function (path) {
        var scansRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?\\1(_\\2)?(_scans.tsv|_scans.json)$');

        var funcSesRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?\\1(_\\2)?task-[a-zA-Z0-9]+(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?(?:_echo-[0-9]+)?'
            + '(_bold.json|_sbref.json|_events.json|_events.tsv|_physio.json|_stim.json)$');

        var anatSesRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?\\1(_\\2)?(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+_)?'
            + '(' + anatSuffixes.join("|") + ').json$');

        var dwiSesRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?\\1(_\\2)?(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?(?:_)?'
            + 'dwi.(?:json|bval|bvec)$');

        return conditionalMatch(scansRe, path) || conditionalMatch(funcSesRe, path) ||
            conditionalMatch(anatSesRe, path) || conditionalMatch(dwiSesRe, path);
    },

    /**
     * Check if the file has appropriate name for a subject level
     */
    isSubjectLevel: function (path) {
        var scansRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/\\1(_sessions.tsv|_sessions.json)$');
        return scansRe.test(path);
    },

    /**
     * Check if the file has a name appropriate for an anatomical scan
     */
    isAnat: function (path) {
        var anatRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?anat' +
            '\\/\\1(_\\2)?(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?_(?:'
            + anatSuffixes.join("|")
            + ').(nii.gz|nii|json)$');
        return conditionalMatch(anatRe, path);
    },

    /**
     * Check if the file has a name appropriate for a diffusion scan
     */
    isDWI: function (path) {
        var suffixes = ["dwi", "sbref"];
        var anatRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?dwi' +
            '\\/\\1(_\\2)?(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?_(?:'
            + suffixes.join("|")
            + ').(nii.gz|nii|json|bvec|bval)$');
        return conditionalMatch(anatRe, path);
    },

    /**
     * Check if the file has a name appropriate for a fieldmap scan
     */
    isFieldMap: function (path) {
        var suffixes = ["phasediff", "phase1", "phase2", "magnitude1", "magnitude2", "magnitude", "fieldmap", "epi"];
        var anatRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?fmap' +
            '\\/\\1(_\\2)?(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_dir-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?_(?:'
            + suffixes.join("|")
            + ').(nii.gz|nii|json)$');
        return conditionalMatch(anatRe, path);
    },

    isFieldMapMainNii: function (path) {
        var suffixes = ["phasediff", "phase1", "phase2", "fieldmap", "epi"];
        var anatRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?fmap' +
            '\\/\\1(_\\2)?(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_dir-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?_(?:'
            + suffixes.join("|")
            + ').(nii.gz|nii)$');
        return conditionalMatch(anatRe, path);
    },

    /**
     * Check if the file has a name appropriate for a functional scan
     */
    isFunc: function (path) {
        var funcRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?func' +
            '\\/\\1(_\\2)?_task-[a-zA-Z0-9]+(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?(?:_echo-[0-9]+)?'
            + '(?:_bold.nii.gz|_bold.nii|_bold.json|_sbref.nii.gz|_sbref.nii|_sbref.json|_events.json|_events.tsv|_physio.tsv.gz|_stim.tsv.gz|_physio.json|_stim.json|_defacemask.nii.gz|_defacemask.nii)$');
        return conditionalMatch(funcRe, path);
    },

    isBehavioral: function (path) {
        var funcBeh = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?beh' +
            '\\/\\1(_\\2)?_task-[a-zA-Z0-9]+(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?'
            + '(?:_beh.json|_events.json|_events.tsv|_physio.tsv.gz|_stim.tsv.gz|_physio.json|_stim.json)$');
        return conditionalMatch(funcBeh, path);
    },

    isFuncBold: function (path) {
        var funcRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?func' +
            '\\/\\1(_\\2)?_task-[a-zA-Z0-9]+(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?(?:_echo-[0-9]+)?'
            + '(?:_bold.nii.gz|_bold.nii|_sbref.nii.gz|_sbref.nii)$');
        return conditionalMatch(funcRe, path);
    },

    isCont: function (path) {
        var contRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?(?:func|beh)' +
            '\\/\\1(_\\2)?_task-[a-zA-Z0-9]+(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?' +
            '(?:_recording-[a-zA-Z0-9]+)?'
            + '(?:_physio.tsv.gz|_stim.tsv.gz|_physio.json|_stim.json)$');
        return conditionalMatch(contRe, path);
    },

    isNumber: function (n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    },

    /**
     * Get Path Values
     *
     * Takes a file path and returns and values
     * found for the following path keys.
     * sub-
     * ses-
     */
    getPathValues: function (path) {
        var values = {}, match;

        // capture subject
        match = (/^\/sub-([a-zA-Z0-9]+)/).exec(path);
        values.sub = match && match[1] ? match[1] : null;

        // capture session
        match = (/^\/sub-[a-zA-Z0-9]+\/ses-([a-zA-Z0-9]+)/).exec(path);
        values.ses = match && match[1] ? match[1] : null;

        return values;
    }

};

function conditionalMatch (expression, path) {
    var match = expression.exec(path);

        // we need to do this because JS does not support conditional groups
        if (match){
            if ((match[2] && match[3]) || !match[2]) {
                return true;
            }
        }
        return false;
}
<commit_msg>redo the regexp to allow for '/bold.json'<commit_after>/**
 * Type
 *
 * A library of functions that take a file path and return a boolean
 * representing whether the given file path is valid within the
 * BIDS specification requirements.
 */
var anatSuffixes = ["T1w", "T2w", "T1map", "T2map", "T1rho", "FLAIR", "PD", "PDT2", "inplaneT1", "inplaneT2","angio",
    "defacemask", "SWImagandphase", "T2star", "FLASH", "PDmap"];

module.exports = {

    /**
     * Is BIDS
     *
     * Check if a given path is valid within the
     * bids spec.
     */
    isBIDS: function (path) {
        return (
            this.isTopLevel(path) ||
            this.isStimuliData(path) ||
            this.isSessionLevel(path) ||
            this.isSubjectLevel(path) ||
            this.isAnat(path) ||
            this.isDWI(path) ||
            this.isFunc(path) ||
            this.isBehavioral(path) ||
            this.isCont(path) ||
            this.isFieldMap(path) ||
            this.isPhenotypic(path)
        );
    },

    /**
     * Check if the file has appropriate name for a top level file
     */
    isTopLevel: function (path) {
        var fixedTopLevelNames = ["/README", "/CHANGES", "/dataset_description.json", "/participants.tsv",
            "/participants.json", "/phasediff.json", "/phase1.json", "/phase2.json", "/fieldmap.json"];

        var funcTopRe = new RegExp('^\\/(?:ses-[a-zA-Z0-9]+_)?(?:recording-[a-zA-Z0-9]+_)?(?:task-[a-zA-Z0-9]+_)?(?:acq-[a-zA-Z0-9]+_)?(?:rec-[a-zA-Z0-9]+_)?(?:run-[0-9]+_)?(?:echo-[0-9]+_)?'
            + '(bold.json|sbref.json|events.json|events.tsv|physio.json|stim.json|beh.json)$');

        var anatTopRe = new RegExp('^\\/(?:ses-[a-zA-Z0-9]+_)?(?:acq-[a-zA-Z0-9]+_)?(?:rec-[a-zA-Z0-9]+_)?(?:run-[0-9]+_)?'
            + '(' + anatSuffixes.join("|") + ').json$');

        var dwiTopRe = new RegExp('^\\/(?:ses-[a-zA-Z0-9]+_)?(?:acq-[a-zA-Z0-9]+_)?(?:rec-[a-zA-Z0-9]+_)?(?:run-[0-9]+_)?'
            + 'dwi.(?:json|bval|bvec)$');

        var multiDirFieldmapRe = new RegExp('^\\/(?:dir-[a-zA-Z0-9]+)_epi.json$');

        var otherTopFiles = new RegExp('^\\/(?:ses-[a-zA-Z0-9]+_)?(?:recording-[a-zA-Z0-9]+_)?(?:task-[a-zA-Z0-9]+_)?(?:acq-[a-zA-Z0-9]+_)?(?:rec-[a-zA-Z0-9]+_)?(?:run-[0-9]+_)?'
            + '(physio.json|stim.json)$');

        return (fixedTopLevelNames.indexOf(path) != -1 || funcTopRe.test(path) || dwiTopRe.test(path) ||
        anatTopRe.test(path) || multiDirFieldmapRe.test(path) || otherTopFiles.test(path));
    },

    /**
     * Check if file is appropriate associated data.
     */
    isAssociatedData: function (path) {
        var associatedData = new RegExp('^\\/(?:code|derivatives|sourcedata|stimuli|[.]git)\\/(?:.*)$');
        return associatedData.test(path);
    },

    isStimuliData: function (path) {
        var stimuliDataRe = new RegExp('^\\/(?:stimuli)\\/(?:.*)$');
        return stimuliDataRe.test(path);
    },

    /**
     * Check if file is phenotypic data.
     */
    isPhenotypic: function (path) {
        var phenotypicData = new RegExp('^\\/(?:phenotype)\\/(?:.*.tsv|.*.json)$');
        return phenotypicData.test(path);
    },

    /**
     * Check if the file has appropriate name for a session level
     */
    isSessionLevel: function (path) {
        var scansRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?\\1(_\\2)?(_scans.tsv|_scans.json)$');

        var funcSesRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?\\1(_\\2)?task-[a-zA-Z0-9]+(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?(?:_echo-[0-9]+)?'
            + '(_bold.json|_sbref.json|_events.json|_events.tsv|_physio.json|_stim.json)$');

        var anatSesRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?\\1(_\\2)?(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+_)?'
            + '(' + anatSuffixes.join("|") + ').json$');

        var dwiSesRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?\\1(_\\2)?(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?(?:_)?'
            + 'dwi.(?:json|bval|bvec)$');

        return conditionalMatch(scansRe, path) || conditionalMatch(funcSesRe, path) ||
            conditionalMatch(anatSesRe, path) || conditionalMatch(dwiSesRe, path);
    },

    /**
     * Check if the file has appropriate name for a subject level
     */
    isSubjectLevel: function (path) {
        var scansRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/\\1(_sessions.tsv|_sessions.json)$');
        return scansRe.test(path);
    },

    /**
     * Check if the file has a name appropriate for an anatomical scan
     */
    isAnat: function (path) {
        var anatRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?anat' +
            '\\/\\1(_\\2)?(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?_(?:'
            + anatSuffixes.join("|")
            + ').(nii.gz|nii|json)$');
        return conditionalMatch(anatRe, path);
    },

    /**
     * Check if the file has a name appropriate for a diffusion scan
     */
    isDWI: function (path) {
        var suffixes = ["dwi", "sbref"];
        var anatRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?dwi' +
            '\\/\\1(_\\2)?(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?_(?:'
            + suffixes.join("|")
            + ').(nii.gz|nii|json|bvec|bval)$');
        return conditionalMatch(anatRe, path);
    },

    /**
     * Check if the file has a name appropriate for a fieldmap scan
     */
    isFieldMap: function (path) {
        var suffixes = ["phasediff", "phase1", "phase2", "magnitude1", "magnitude2", "magnitude", "fieldmap", "epi"];
        var anatRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?fmap' +
            '\\/\\1(_\\2)?(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_dir-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?_(?:'
            + suffixes.join("|")
            + ').(nii.gz|nii|json)$');
        return conditionalMatch(anatRe, path);
    },

    isFieldMapMainNii: function (path) {
        var suffixes = ["phasediff", "phase1", "phase2", "fieldmap", "epi"];
        var anatRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?fmap' +
            '\\/\\1(_\\2)?(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_dir-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?_(?:'
            + suffixes.join("|")
            + ').(nii.gz|nii)$');
        return conditionalMatch(anatRe, path);
    },

    /**
     * Check if the file has a name appropriate for a functional scan
     */
    isFunc: function (path) {
        var funcRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?func' +
            '\\/\\1(_\\2)?_task-[a-zA-Z0-9]+(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?(?:_echo-[0-9]+)?'
            + '(?:_bold.nii.gz|_bold.nii|_bold.json|_sbref.nii.gz|_sbref.nii|_sbref.json|_events.json|_events.tsv|_physio.tsv.gz|_stim.tsv.gz|_physio.json|_stim.json|_defacemask.nii.gz|_defacemask.nii)$');
        return conditionalMatch(funcRe, path);
    },

    isBehavioral: function (path) {
        var funcBeh = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?beh' +
            '\\/\\1(_\\2)?_task-[a-zA-Z0-9]+(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?'
            + '(?:_beh.json|_events.json|_events.tsv|_physio.tsv.gz|_stim.tsv.gz|_physio.json|_stim.json)$');
        return conditionalMatch(funcBeh, path);
    },

    isFuncBold: function (path) {
        var funcRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?func' +
            '\\/\\1(_\\2)?_task-[a-zA-Z0-9]+(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?(?:_echo-[0-9]+)?'
            + '(?:_bold.nii.gz|_bold.nii|_sbref.nii.gz|_sbref.nii)$');
        return conditionalMatch(funcRe, path);
    },

    isCont: function (path) {
        var contRe = new RegExp('^\\/(sub-[a-zA-Z0-9]+)' +
            '\\/(?:(ses-[a-zA-Z0-9]+)' +
            '\\/)?(?:func|beh)' +
            '\\/\\1(_\\2)?_task-[a-zA-Z0-9]+(?:_acq-[a-zA-Z0-9]+)?(?:_rec-[a-zA-Z0-9]+)?(?:_run-[0-9]+)?' +
            '(?:_recording-[a-zA-Z0-9]+)?'
            + '(?:_physio.tsv.gz|_stim.tsv.gz|_physio.json|_stim.json)$');
        return conditionalMatch(contRe, path);
    },

    isNumber: function (n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    },

    /**
     * Get Path Values
     *
     * Takes a file path and returns and values
     * found for the following path keys.
     * sub-
     * ses-
     */
    getPathValues: function (path) {
        var values = {}, match;

        // capture subject
        match = (/^\/sub-([a-zA-Z0-9]+)/).exec(path);
        values.sub = match && match[1] ? match[1] : null;

        // capture session
        match = (/^\/sub-[a-zA-Z0-9]+\/ses-([a-zA-Z0-9]+)/).exec(path);
        values.ses = match && match[1] ? match[1] : null;

        return values;
    }

};

function conditionalMatch (expression, path) {
    var match = expression.exec(path);

        // we need to do this because JS does not support conditional groups
        if (match){
            if ((match[2] && match[3]) || !match[2]) {
                return true;
            }
        }
        return false;
}
<|endoftext|>


<commit_before>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

"use strict";

exports.userExists = function(dbConnection, username, callback, table)
{
	if (!table) table = 'uniusers';
	dbConnection.query(
		// Seems unsafe? It is.
		// But escaper doesn't know that table name and column value are different things.
		'SELECT count(*) AS result FROM `'+table+'` WHERE user = ?',
		[username],
		function (error, result){
			if (!!error)
			{
				callback(error, undefined);
			}
			else
			{
				callback(undefined, (result.rows[0].result > 0));
			}
		}
	);
};

exports.idExists = function(dbConnection, id, callback) {
	dbConnection.query(
		'SELECT count(*) FROM `uniusers` WHERE `id`= ?',
		[id],
		function (error, result) {
			if (!!error) {
				callback(error, undefined);
			}
			else {
				callback(undefined, (result.rows[0].result > 0));
			}
		}
	);
};

exports.mailExists = function(dbConnection, mail, callback) {
	dbConnection.query(
		'SELECT count(*) FROM `uniusers` WHERE `mail` = ?',
		[mail],
		function (error, result) {
			if (!!error) {
				callback(error, undefined);
			}
			else {
				callback(undefined, (result.rows[0].result > 0));
			}
		}
	);
};

exports.sessionExists = function(dbConnection, sess, callback) {
	dbConnection.query(
		'SELECT count(*) FROM `uniusers` WHERE `sessid` = ?',
		[sess],
		function (error, result) {
			if (!!error) {
				callback(error, undefined);
			}
			else {
				callback(undefined, (result.rows[0].result > 0));
			}
		}
	);
};

exports.sessionActive = function(dbConnection, sess, callback) {
	dbConnection.query(
		'SELECT `sessexpire` > NOW() FROM `uniusers` WHERE `sessid` = ?',
		[sess],
		function (error, result) {
			if (!!error) {
				callback(error, undefined);
			}
			else {
				callback(undefined, (result.rows[0].result > 0));
			}
		}
	);
};

exports.generateSessId = function(dbConnection, sess_length, callback) {
	//here random sessid must be checked for uniqueness
	callback(undefined, exports.createSalt(sess_length));
};

exports.userBySession = function(dbConnection, sess, callback) {
	dbConnection.query(
		'SELECT `user` FROM `uniusers` WHERE `sessid` = ?',
		[sess],
		function (error, result) {
			if (!!error) {
				callback(error, undefined);
			}
			else {
				callback(undefined, result.rows[0].result);
			}
		}
	);
};

exports.idBySession = function(dbConnection, sess, callback) {
	dbConnection.query(
		'SELECT `id` FROM `uniusers` WHERE `sessid` = ?',
		[sess],
		function (error, result) {
			if (!!error) {
				callback(error, undefined);
			}
			else {
				callback(undefined, result.rows[0].result);
			}
		}
	);
};

exports.refreshSession = function(dbConnection, sess, sess_timeexpire) {
	dbConnection.query(
		'UPDATE `uniusers` SET `sessexpire` = NOW() + INTERVAL ? SECOND WHERE `sessid` = ?',
		[sess_timeexpire, sess]
	);
};

exports.closeSession = function(dbConnection, sess) {
	dbConnection.query(
		'UPDATE `uniusers` SET `sessexpire` = NOW() - INTERVAL 1 SECOND WHERE `sessid` = ?',
		[sess]
	);
};

exports.createSalt = function(sess_length) {
	var salt = '';
	var a = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
	for(var i=0; i<sess_length; i++) { salt += a[Math.floor(Math.random() * a.length)]; }
	return salt;
};

exports.registerUser = function(dbConnection, user, password, permissions, callback) {
	var salt = createSalt(16);
	dbConnection.query(
		'INSERT INTO `uniusers` '+
		'(`user`, `salt`, `hash`, `sessid`, `reg_time`, `sessexpire`, `location`, `permissions`) VALUES '+
		'(?, ?, ?, ?, NOW(), NOW() + INTERVAL ? SECOND, (SELECT `id` FROM `locations` WHERE `default` = 1), ?)',
		[user, salt, myCrypt(password, salt), exports.generateSessId(), require('config.js').sessionExpireTime, permissions]
	);
};
<commit_msg>registerUser() implementation.<commit_after>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

"use strict";

var config = require('../config.js');

var crypto = require('crypto');
var async = require('async');

exports.userExists = function(dbConnection, username, callback, table)
{
	if (!table) table = 'uniusers';
	dbConnection.query(
		// Seems unsafe? It is.
		// But escaper doesn't know that table name and column value are different things.
		'SELECT count(*) AS result FROM `'+table+'` WHERE user = ?',
		[username],
		function (error, result){
			if (!!error)
			{
				callback(error, undefined);
			}
			else
			{
				callback(undefined, (result.rows[0].result > 0));
			}
		}
	);
};

exports.idExists = function(dbConnection, id, callback) {
	dbConnection.query(
		'SELECT count(*) FROM `uniusers` WHERE `id`= ?',
		[id],
		function (error, result) {
			if (!!error) {
				callback(error, undefined);
			}
			else {
				callback(undefined, (result.rows[0].result > 0));
			}
		}
	);
};

exports.mailExists = function(dbConnection, mail, callback) {
	dbConnection.query(
		'SELECT count(*) FROM `uniusers` WHERE `mail` = ?',
		[mail],
		function (error, result) {
			if (!!error) {
				callback(error, undefined);
			}
			else {
				callback(undefined, (result.rows[0].result > 0));
			}
		}
	);
};

exports.sessionExists = function(dbConnection, sess, callback) {
	dbConnection.query(
		'SELECT count(*) FROM `uniusers` WHERE `sessid` = ?',
		[sess],
		function (error, result) {
			if (!!error) {
				callback(error, undefined);
			}
			else {
				callback(undefined, (result.rows[0].result > 0));
			}
		}
	);
};

exports.sessionActive = function(dbConnection, sess, callback) {
	dbConnection.query(
		'SELECT `sessexpire` > NOW() FROM `uniusers` WHERE `sessid` = ?',
		[sess],
		function (error, result) {
			if (!!error) {
				callback(error, undefined);
			}
			else {
				callback(undefined, (result.rows[0].result > 0));
			}
		}
	);
};

exports.generateSessId = function(dbConnection, sess_length, callback) {
	//here random sessid must be checked for uniqueness
	callback(undefined, exports.createSalt(sess_length));
};

exports.userBySession = function(dbConnection, sess, callback) {
	dbConnection.query(
		'SELECT `user` FROM `uniusers` WHERE `sessid` = ?',
		[sess],
		function (error, result) {
			if (!!error) {
				callback(error, undefined);
			}
			else {
				callback(undefined, result.rows[0].result);
			}
		}
	);
};

exports.idBySession = function(dbConnection, sess, callback) {
	dbConnection.query(
		'SELECT `id` FROM `uniusers` WHERE `sessid` = ?',
		[sess],
		function (error, result) {
			if (!!error) {
				callback(error, undefined);
			}
			else {
				callback(undefined, result.rows[0].result);
			}
		}
	);
};

exports.refreshSession = function(dbConnection, sess, sess_timeexpire) {
	dbConnection.query(
		'UPDATE `uniusers` SET `sessexpire` = NOW() + INTERVAL ? SECOND WHERE `sessid` = ?',
		[sess_timeexpire, sess]
	);
};

exports.closeSession = function(dbConnection, sess) {
	dbConnection.query(
		'UPDATE `uniusers` SET `sessexpire` = NOW() - INTERVAL 1 SECOND WHERE `sessid` = ?',
		[sess]
	);
};

exports.createSalt = function(sess_length) {
	var salt = '';
	var a = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
	for(var i=0; i<sess_length; i++) { salt += a[Math.floor(Math.random() * a.length)]; }
	return salt;
};

exports.registerUser = function(dbConnection, user, password, permissions, callback) {
	var salt = this.createSalt(16);
	/*crypto.pbkdf2(password, salt, 4096, 256, function(error, cryptedPassword){
		if (!!error) callback(error, undefined);
		dbConnection.query(
			'INSERT INTO `uniusers` '+
			'(`user`, `salt`, `hash`, `sessid`, `reg_time`, `sessexpire`, `location`, `permissions`) VALUES '+
			'(?, ?, ?, ?, NOW(), NOW() + INTERVAL ? SECOND, (SELECT `id` FROM `locations` WHERE `default` = 1), ?)',
			[user, salt, cryptedPassword, exports.generateSessId(), config.sessionExpireTime, permissions],
			function(error, queryResult){
				if (!!error)
				{
					callback(error, undefined);
				}
				else
				{
					callback(undefined, queryResult);
				}
			}
		);
	});*/
	async.waterfall([
			function(innerCallback){
				crypto.pbkdf2(password, salt, 4096, 256, innerCallback);
			},
			function(previousResult, innerCallback){
				dbConnection.query(
					'INSERT INTO `uniusers` '+
					'(`user`, `salt`, `hash`, `sessid`, `reg_time`, `sessexpire`, `location`, `permissions`) '+
					'VALUES '+
					'(?, ?, ?, ?, NOW(), NOW() + INTERVAL ? SECOND, '+
						'(SELECT `id` FROM `locations` WHERE `default` = 1), '+
					'?)',
					[user, salt, previousResult.toString(), exports.generateSessId(), config.sessionExpireTime,
						permissions],
					innerCallback);
			},
		],
		function(error, result){
			callback(error, result);
		}
	);
};
<|endoftext|>


<commit_before>/**
 * Manage meteor_login_token cookie
 * Necessary for authentication when the
 * Authorization header is not set
 *
 * E.g on first page loading
 */
import Cookies from 'universal-cookie';

import { Meteor } from 'meteor/meteor';

const cookie = new Cookies();

function setToken(loginToken, expires) {
  if (loginToken && expires !== -1) {
    cookie.set('meteor_login_token', loginToken, {
      path: '/',
      expires,
    });
  } else {
    cookie.remove('meteor_login_token', {
      path: '/',
    });
  }
}

function initToken() {
  const loginToken = global.localStorage['Meteor.loginToken'];
  const loginTokenExpires = new Date(global.localStorage['Meteor.loginTokenExpires']);

  if (loginToken) {
    setToken(loginToken, loginTokenExpires);
  } else {
    setToken(null, -1);
  }
}

Meteor.startup(() => {
  console.log('init token');
  initToken();
});

// TODO: cleanup
// This part of the code overrides the default localStorage function,
// so that when Meteor.loginToken is set, it is also automatically
// stored as a cookie (necessary for SSR to work as expected for all HTTP requests)
const originalSetItem = Meteor._localStorage.setItem;
Meteor._localStorage.setItem = function setItem(key, value) {
  if (key === 'Meteor.loginToken') {
    Meteor.defer(initToken);
  }
  originalSetItem.call(Meteor._localStorage, key, value);
};
const originalRemoveItem = Meteor._localStorage.removeItem;
Meteor._localStorage.removeItem = function removeItem(key) {
  if (key === 'Meteor.loginToken') {
    Meteor.defer(initToken);
  }
  originalRemoveItem.call(Meteor._localStorage, key);
};
<commit_msg>Remove console.log<commit_after>/**
 * Manage meteor_login_token cookie
 * Necessary for authentication when the
 * Authorization header is not set
 *
 * E.g on first page loading
 */
import Cookies from 'universal-cookie';

import { Meteor } from 'meteor/meteor';

const cookie = new Cookies();

function setToken(loginToken, expires) {
  if (loginToken && expires !== -1) {
    cookie.set('meteor_login_token', loginToken, {
      path: '/',
      expires,
    });
  } else {
    cookie.remove('meteor_login_token', {
      path: '/',
    });
  }
}

function initToken() {
  const loginToken = global.localStorage['Meteor.loginToken'];
  const loginTokenExpires = new Date(global.localStorage['Meteor.loginTokenExpires']);

  if (loginToken) {
    setToken(loginToken, loginTokenExpires);
  } else {
    setToken(null, -1);
  }
}

Meteor.startup(() => {
  initToken();
});

// TODO: cleanup
// This part of the code overrides the default localStorage function,
// so that when Meteor.loginToken is set, it is also automatically
// stored as a cookie (necessary for SSR to work as expected for all HTTP requests)
const originalSetItem = Meteor._localStorage.setItem;
Meteor._localStorage.setItem = function setItem(key, value) {
  if (key === 'Meteor.loginToken') {
    Meteor.defer(initToken);
  }
  originalSetItem.call(Meteor._localStorage, key, value);
};
const originalRemoveItem = Meteor._localStorage.removeItem;
Meteor._localStorage.removeItem = function removeItem(key) {
  if (key === 'Meteor.loginToken') {
    Meteor.defer(initToken);
  }
  originalRemoveItem.call(Meteor._localStorage, key);
};
<|endoftext|>


<commit_before>/**
 * Helper for changing tabs when a user clicks a link
 * @method
 *   @param {$.Event} event - jQuery Event instance
 */
var changeTabs = function (event) {
    event.stopPropagation(); event.preventDefault();

    var parent = document.querySelector('section.mainSection.work'),
        active = parent.querySelectorAll('.active'),
        article, i = 0, j = active.length;

    // Remove active class from all elements under parent
    for (; i < j; ++i)
        active[i].classList.remove('active');

    // Make article with same ID as tab active
    article = parent.querySelector('article[data-id="' +
        event.currentTarget.dataset.id + '"]');

    // don't do anything if there's no article
    if (!article) return;

    article.classList.add('active');

    // Make tab active, too
    event.currentTarget.classList.add('active');

    // Is this is a submenu item?
    parent = event.currentTarget.parentElement.parentElement;
    if ( parent.nodeName.toLowerCase() === 'li' ) {
        // Make active
        parent.classList.add('active');
    }
    if ( parent.nodeName.toLocaleLowerCase() === 'li'){
        document.body.classList.add('mobile-article-open');
    }
},

/**
 * Sections collection getter
 * @function
 *   @param {string} field - The name of the field whose value to return
 *   @param {string} id    - The Mongo DB ID of the document to return.
 *      Optional.  Uses `this` otherwise.
 *   @returns {string}
 */
getValue = function (field) {
    check(field, String);                // Must be a String

    // Meteor template helpers expect a function with a single variable
    return function (id) {
        check(id, Match.Maybe(String));  // String or undefined

        var result;
        id = id || this.id || this._id;

        if (!id) return '';

        result = Thriver.sections.get( id, [field] );

        if (result)
            return result[field];
        else
            return '';
    };
};

// Tabs
Template.workNav.helpers({
    name: getValue('name'),
    icon: getValue('icon'),
    hasChildren: function (id) {
        var result;
        id = id || this.id;

        result = Thriver.sections.get(id, ['tabs']);

        if (result && result.tabs && result.tabs.length)
            return true;

        return false;
    },
    tabs: getValue('tabs')
});

// Navigation
Template.workListItem.helpers({
    icon:     getValue('icon'),
    name:     getValue('name'),
    tabs:     getValue('tabs'),
    tabName:  getValue('name')
});

// Content Container
Template.workContentContainer.helpers({
    tabs:     getValue('tabs'),
    template: getValue('template'),
    subtabs:  getValue('tabs')
});
// Content
Template.workContent.helpers({
    content:  getValue('content'),
    icon:     getValue('icon'),
    name:     getValue('name')
});

// Helper for changing tabs
Template.workNav.events({
    'click li': changeTabs,
});

Template.workContent.events({
    'click footer.truncate button': function (event) {
        event.preventDefault();
        $("body").addClass("workReadingAnimate").delay(2000).queue(function(){
            $("body").removeClass("workReadingAnimate").dequeue();
            $("body").addClass("workReading").dequeue();
        });
    },
    'click button.backToTopWork': function (event) {
        offset = $('[id="what-we-do"]').offset().top + 228;
        $('body').animate({ scrollTop: offset }, 750);
    },
    'click .backToPrevious': function (event) {
        document.body.classList.remove('mobile-article-open');
    }
});

// Eoghan's stuff
Template.work.onRendered(function () {
    // Handle adding and removing the Body 'workActive' class
    $('.workNav > ul > li > h2 > a:not(.backToIndexWorkA), .workNav ul > li > ul > li > a').click(function (event) {
        event.preventDefault();
        if($("body").hasClass('openNavItem')){
            $("body").removeClass('openNavItem');
        }
        if(!$("body").hasClass('workActive')){
            $("body").removeClass("workBack");
            $("body").addClass('openNavItem');
            $("body").addClass("workFadeIn").delay(250).queue(function(){
                $(this).removeClass("workFadeIn").dequeue();
                $(this).addClass("workActive").dequeue();
            });
            //$('body').addClass('workFadeOut');
        }
    });

    $('.workNav li.backToIndexWork').click(function (event) {
        event.preventDefault();
        $("body").addClass("workFadeOut").delay(175).queue(function(){
        $('body').removeClass('workActive');
        $('body').removeClass('workReading');
        $(this).removeClass("workFadeOut").dequeue();
        });
        //offset = $('[id="what-we-do"]').offset().top - 125;
        //$('body').animate({ scrollTop: offset }, 350);
    });

});

/**
 * Dynamically Generate Tertiary menu
 * @method
 */
Template.workContent.onRendered(function () {
    var that = this;
    Deps.autorun(function (c) {
        var tertiary = that.firstNode.parentElement.querySelector('.workTertiary'),
            content  = tertiary.parentElement.querySelectorAll('h3'),
            ul       = document.createElement('ul'),
            li, a, i = 0, j = content.length;

        // Wait until ready
        if (!j) return;

        // Quit if there already is a menu (this method executes a dozen times
        // for some reason...)
        if (tertiary.children[0] instanceof Element) return;

        // We're ready now
        c.stop();

        for (; i < j; ++i) {
            // Create list and anchor elements
            li = document.createElement('li');
            a  = document.createElement('a');

            // Create link details
            a.href = '#' + content[i].id;
            a.textContent = content[i].textContent;

            // Add elements
            ul.appendChild(li).appendChild(a);
        }

        tertiary.appendChild(ul);
    });
});

/**
 * @summary Register Deep-linking
 * @method
 */
Template.work.onRendered(function () {
    // Get db ID from current instance
    var instanceName = this.data.name;

    // Register
    Thriver.history.registry.insert({
        element: Thriver.sections.generateId(instanceName),
        /** Handle deep-linking */
        callback: function (path) {
            console.debug('Deep-link:', path);
        }
    });
});
<commit_msg>Resolved tertiary menu problem.  Fixes issue #104.<commit_after>/**
 * Helper for changing tabs when a user clicks a link
 * @method
 *   @param {$.Event} event - jQuery Event instance
 */
var changeTabs = function (event) {
    event.stopPropagation(); event.preventDefault();

    var parent = document.querySelector('section.mainSection.work'),
        active = parent.querySelectorAll('.active'),
        article, i = 0, j = active.length;

    // Remove active class from all elements under parent
    for (; i < j; ++i)
        active[i].classList.remove('active');

    // Make article with same ID as tab active
    article = parent.querySelector('article[data-id="' +
        event.currentTarget.dataset.id + '"]');

    // don't do anything if there's no article
    if (!article) return;

    article.classList.add('active');

    // Make tab active, too
    event.currentTarget.classList.add('active');

    // Is this is a submenu item?
    parent = event.currentTarget.parentElement.parentElement;
    if ( parent.nodeName.toLowerCase() === 'li' ) {
        // Make active
        parent.classList.add('active');
    }
    if ( parent.nodeName.toLocaleLowerCase() === 'li'){
        document.body.classList.add('mobile-article-open');
    }
},

/**
 * Sections collection getter
 * @function
 *   @param {string} field - The name of the field whose value to return
 *   @param {string} id    - The Mongo DB ID of the document to return.
 *      Optional.  Uses `this` otherwise.
 *   @returns {string}
 */
getValue = function (field) {
    check(field, String);                // Must be a String

    // Meteor template helpers expect a function with a single variable
    return function (id) {
        check(id, Match.Maybe(String));  // String or undefined

        var result;
        id = id || this.id || this._id;

        if (!id) return '';

        result = Thriver.sections.get( id, [field] );

        if (result)
            return result[field];
        else
            return '';
    };
};

// Tabs
Template.workNav.helpers({
    name: getValue('name'),
    icon: getValue('icon'),
    hasChildren: function (id) {
        var result;
        id = id || this.id;

        result = Thriver.sections.get(id, ['tabs']);

        if (result && result.tabs && result.tabs.length)
            return true;

        return false;
    },
    tabs: getValue('tabs')
});

// Navigation
Template.workListItem.helpers({
    icon:     getValue('icon'),
    name:     getValue('name'),
    tabs:     getValue('tabs'),
    tabName:  getValue('name')
});

// Content Container
Template.workContentContainer.helpers({
    tabs:     getValue('tabs'),
    template: getValue('template'),
    subtabs:  getValue('tabs')
});
// Content
Template.workContent.helpers({
    content:  getValue('content'),
    icon:     getValue('icon'),
    name:     getValue('name')
});

// Helper for changing tabs
Template.workNav.events({
    'click li': changeTabs,
});

Template.workContent.events({
    'click footer.truncate button': function (event) {
        event.preventDefault();
        $("body").addClass("workReadingAnimate").delay(2000).queue(function(){
            $("body").removeClass("workReadingAnimate").dequeue();
            $("body").addClass("workReading").dequeue();
        });
    },
    'click button.backToTopWork': function (event) {
        offset = $('[id="what-we-do"]').offset().top + 228;
        $('body').animate({ scrollTop: offset }, 750);
    },
    'click .backToPrevious': function (event) {
        document.body.classList.remove('mobile-article-open');
    }
});

// Eoghan's stuff
Template.work.onRendered(function () {
    // Handle adding and removing the Body 'workActive' class
    $('.workNav > ul > li > h2 > a:not(.backToIndexWorkA), .workNav ul > li > ul > li > a').click(function (event) {
        event.preventDefault();
        if($("body").hasClass('openNavItem')){
            $("body").removeClass('openNavItem');
        }
        if(!$("body").hasClass('workActive')){
            $("body").removeClass("workBack");
            $("body").addClass('openNavItem');
            $("body").addClass("workFadeIn").delay(250).queue(function(){
                $(this).removeClass("workFadeIn").dequeue();
                $(this).addClass("workActive").dequeue();
            });
            //$('body').addClass('workFadeOut');
        }
    });

    $('.workNav li.backToIndexWork').click(function (event) {
        event.preventDefault();
        $("body").addClass("workFadeOut").delay(175).queue(function(){
        $('body').removeClass('workActive');
        $('body').removeClass('workReading');
        $(this).removeClass("workFadeOut").dequeue();
        });
        //offset = $('[id="what-we-do"]').offset().top - 125;
        //$('body').animate({ scrollTop: offset }, 350);
    });

});

/**
 * Dynamically Generate Tertiary menu
 * @method
 */
Template.workContent.onRendered(function () { console.debug('work rendered', this);
    var that = this;
    Deps.autorun(function (c) {
        var tertiary = that.firstNode.querySelector('.workTertiary'),
            content  = tertiary.parentElement.querySelectorAll('h3'),
            ul       = document.createElement('ul'),
            li, a, i = 0, j = content.length;

        // Wait until ready
        if (!j) return;

        // Quit if there already is a menu (this method executes a dozen times
        // for some reason...)
        if (tertiary.children[0] instanceof Element) return;

        // We're ready now
        c.stop();

        for (; i < j; ++i) {
            // Create list and anchor elements
            li = document.createElement('li');
            a  = document.createElement('a');

            // Create link details
            a.href = '#' + content[i].id;
            a.textContent = content[i].textContent;

            // Add elements
            ul.appendChild(li).appendChild(a);
        }

        tertiary.appendChild(ul);
    });
});

/**
 * @summary Register Deep-linking
 * @method
 */
Template.work.onRendered(function () {
    // Get db ID from current instance
    var instanceName = this.data.name;

    // Register
    Thriver.history.registry.insert({
        element: Thriver.sections.generateId(instanceName),
        /** Handle deep-linking */
        callback: function (path) {
            console.debug('Deep-link:', path);
        }
    });
});
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/**
 * @fileOverview
 * Directive Parser
 */

import { bind, typof } from '../util/index'
import Watcher from '../core/watcher'
import config from '../config'

const { nativeComponentMap } = config

const SETTERS = {
  attr: 'setAttr',
  style: 'setStyle',
  event: 'addEvent'
}

/**
 * apply the native component's options(specified by template.type)
 * to the template
 */
export function applyNaitveComponentOptions (template) {
  const { type } = template
  const options = nativeComponentMap[type]

  if (typeof options === 'object') {
    for (const key in options) {
      if (template[key] == null) {
        template[key] = options[key]
      }
      else if (typof(template[key]) === 'object' &&
        typof(options[key]) === 'object') {
        for (const subkey in options[key]) {
          if (template[key][subkey] == null) {
            template[key][subkey] = options[key][subkey]
          }
        }
      }
    }
  }
}

/**
 * bind all id, attr, classnames, style, events to an element
 */
export function bindElement (vm, el, template) {
  setId(vm, el, template.id, vm)
  setAttr(vm, el, template.attr)
  setClass(vm, el, template.classList)
  setStyle(vm, el, template.style)
  bindEvents(vm, el, template.events)
}

/**
 * bind all props to sub vm and bind all style, events to the root element
 * of the sub vm if it doesn't have a replaced multi-node fragment
 */
export function bindSubVm (vm, subVm, template, repeatItem) {
  subVm = subVm || {}
  template = template || {}

  const options = subVm._options || {}

  // bind props
  let props = options.props

  if (Array.isArray(props)) {
    props = props.reduce((result, value) => {
      result[value] = true
      return result
    }, {})
  }

  mergeProps(repeatItem, props, vm, subVm)
  mergeProps(template.attr, props, vm, subVm)
}

/**
 * merge class and styles from vm to sub vm.
 */
export function bindSubVmAfterInitialized (vm, subVm, template, target = {}) {
  mergeClassStyle(template.classList, vm, subVm)
  mergeStyle(template.style, vm, subVm)

  // bind subVm to the target element
  if (target.children) {
    target.children[target.children.length - 1]._vm = subVm
  }
  else {
    target._vm = subVm
  }
}

/**
 * Bind props from vm to sub vm and watch their updates.
 */
function mergeProps (target, props, vm, subVm) {
  if (!target) {
    return
  }
  for (const key in target) {
    if (!props || props[key]) {
      const value = target[key]
      if (typeof value === 'function') {
        const returnValue = watch(vm, value, function (v) {
          subVm[key] = v
        })
        subVm[key] = returnValue
      }
      else {
        subVm[key] = value
      }
    }
  }
}

/**
 * Bind style from vm to sub vm and watch their updates.
 */
function mergeStyle (target, vm, subVm) {
  for (const key in target) {
    const value = target[key]
    if (typeof value === 'function') {
      const returnValue = watch(vm, value, function (v) {
        if (subVm._rootEl) {
          subVm._rootEl.setStyle(key, v)
        }
      })
      subVm._rootEl.setStyle(key, returnValue)
    }
    else {
      if (subVm._rootEl) {
        subVm._rootEl.setStyle(key, value)
      }
    }
  }
}

/**
 * Bind class & style from vm to sub vm and watch their updates.
 */
function mergeClassStyle (target, vm, subVm) {
  const css = vm._options && vm._options.style || {}

  /* istanbul ignore if */
  if (!subVm._rootEl) {
    return
  }

  const className = '@originalRootEl'
  css[className] = subVm._rootEl.classStyle

  function addClassName (list, name) {
    if (typof(list) === 'array') {
      list.unshift(name)
    }
  }

  if (typeof target === 'function') {
    const value = watch(vm, target, v => {
      addClassName(v, className)
      setClassStyle(subVm._rootEl, css, v)
    })
    addClassName(value, className)
    setClassStyle(subVm._rootEl, css, value)
  }
  else if (target != null) {
    addClassName(target, className)
    setClassStyle(subVm._rootEl, css, target)
  }
}

/**
 * bind id to an element
 * each id is unique in a whole vm
 */
export function setId (vm, el, id, target) {
  const map = Object.create(null)

  Object.defineProperties(map, {
    vm: {
      value: target,
      writable: false,
      configurable: false
    },
    el: {
      get: () => el || target._rootEl,
      configurable: false
    }
  })

  if (typeof id === 'function') {
    const handler = id
    id = handler.call(vm)
    if (id || id === 0) {
      vm._ids[id] = map
    }
    watch(vm, handler, (newId) => {
      if (newId) {
        vm._ids[newId] = map
      }
    })
  }
  else if (id && typeof id === 'string') {
    vm._ids[id] = map
  }
}

/**
 * bind attr to an element
 */
function setAttr (vm, el, attr) {
  bindDir(vm, el, 'attr', attr)
}

function setClassStyle (el, css, classList) {
  const classStyle = {}
  const length = classList.length

  for (let i = 0; i < length; i++) {
    const style = css[classList[i]]
    if (style) {
      Object.keys(style).forEach((key) => {
        classStyle[key] = style[key]
      })
    }
  }
  el.setClassStyle(classStyle)
}

/**
 * bind classnames to an element
 */
function setClass (vm, el, classList) {
  if (typeof classList !== 'function' && !Array.isArray(classList)) {
    return
  }
  if (Array.isArray(classList) && !classList.length) {
    el.setClassStyle({})
    return
  }

  const style = vm._options && vm._options.style || {}
  if (typeof classList === 'function') {
    const value = watch(vm, classList, v => {
      setClassStyle(el, style, v)
    })
    setClassStyle(el, style, value)
  }
  else {
    setClassStyle(el, style, classList)
  }
}

/**
 * bind style to an element
 */
function setStyle (vm, el, style) {
  bindDir(vm, el, 'style', style)
}

/**
 * add an event type and handler to an element and generate a dom update
 */
function setEvent (vm, el, type, handler) {
  el.addEvent(type, bind(handler, vm))
}

/**
 * add all events of an element
 */
function bindEvents (vm, el, events) {
  if (!events) {
    return
  }
  const keys = Object.keys(events)
  let i = keys.length
  while (i--) {
    const key = keys[i]
    let handler = events[key]
    if (typeof handler === 'string') {
      handler = vm[handler]
      /* istanbul ignore if */
      if (!handler) {
        console.warn(`[JS Framework] The event handler "${handler}" is not defined.`)
      }
    }
    setEvent(vm, el, key, handler)
  }
}

/**
 * set a series of members as a kind of an element
 * for example: style, attr, ...
 * if the value is a function then bind the data changes
 */
function bindDir (vm, el, name, data) {
  if (!data) {
    return
  }
  const keys = Object.keys(data)
  let i = keys.length
  while (i--) {
    const key = keys[i]
    const value = data[key]
    if (typeof value === 'function') {
      bindKey(vm, el, name, key, value)
    }
    else {
      el[SETTERS[name]](key, value)
    }
  }
}

/**
 * bind data changes to a certain key to a name series in an element
 */
function bindKey (vm, el, name, key, calc) {
  const methodName = SETTERS[name]
  // watch the calc, and returns a value by calc.call()
  const value = watch(vm, calc, (value) => {
    function handler () {
      el[methodName](key, value)
    }
    const differ = vm && vm._app && vm._app.differ
    if (differ) {
      differ.append('element', el.depth, el.ref, handler)
    }
    else {
      handler()
    }
  })

  el[methodName](key, value)
}

/**
 * watch a calc function and callback if the calc value changes
 */
export function watch (vm, calc, callback) {
  if (vm._static) {
    return calc.call(vm, vm)
  }
  const watcher = new Watcher(vm, calc, function (value, oldValue) {
    /* istanbul ignore if */
    if (typeof value !== 'object' && value === oldValue) {
      return
    }
    callback(value)
  })

  return watcher.value
}
<commit_msg>* [jsfm] fix the computed classList issue in legacy framework<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/**
 * @fileOverview
 * Directive Parser
 */

import { bind, typof } from '../util/index'
import Watcher from '../core/watcher'
import config from '../config'

const { nativeComponentMap } = config

const SETTERS = {
  attr: 'setAttr',
  style: 'setStyle',
  event: 'addEvent'
}

/**
 * apply the native component's options(specified by template.type)
 * to the template
 */
export function applyNaitveComponentOptions (template) {
  const { type } = template
  const options = nativeComponentMap[type]

  if (typeof options === 'object') {
    for (const key in options) {
      if (template[key] == null) {
        template[key] = options[key]
      }
      else if (typof(template[key]) === 'object' &&
        typof(options[key]) === 'object') {
        for (const subkey in options[key]) {
          if (template[key][subkey] == null) {
            template[key][subkey] = options[key][subkey]
          }
        }
      }
    }
  }
}

/**
 * bind all id, attr, classnames, style, events to an element
 */
export function bindElement (vm, el, template) {
  setId(vm, el, template.id, vm)
  setAttr(vm, el, template.attr)
  setClass(vm, el, template.classList)
  setStyle(vm, el, template.style)
  bindEvents(vm, el, template.events)
}

/**
 * bind all props to sub vm and bind all style, events to the root element
 * of the sub vm if it doesn't have a replaced multi-node fragment
 */
export function bindSubVm (vm, subVm, template, repeatItem) {
  subVm = subVm || {}
  template = template || {}

  const options = subVm._options || {}

  // bind props
  let props = options.props

  if (Array.isArray(props)) {
    props = props.reduce((result, value) => {
      result[value] = true
      return result
    }, {})
  }

  mergeProps(repeatItem, props, vm, subVm)
  mergeProps(template.attr, props, vm, subVm)
}

/**
 * merge class and styles from vm to sub vm.
 */
export function bindSubVmAfterInitialized (vm, subVm, template, target = {}) {
  mergeClassStyle(template.classList, vm, subVm)
  mergeStyle(template.style, vm, subVm)

  // bind subVm to the target element
  if (target.children) {
    target.children[target.children.length - 1]._vm = subVm
  }
  else {
    target._vm = subVm
  }
}

/**
 * Bind props from vm to sub vm and watch their updates.
 */
function mergeProps (target, props, vm, subVm) {
  if (!target) {
    return
  }
  for (const key in target) {
    if (!props || props[key]) {
      const value = target[key]
      if (typeof value === 'function') {
        const returnValue = watch(vm, value, function (v) {
          subVm[key] = v
        })
        subVm[key] = returnValue
      }
      else {
        subVm[key] = value
      }
    }
  }
}

/**
 * Bind style from vm to sub vm and watch their updates.
 */
function mergeStyle (target, vm, subVm) {
  for (const key in target) {
    const value = target[key]
    if (typeof value === 'function') {
      const returnValue = watch(vm, value, function (v) {
        if (subVm._rootEl) {
          subVm._rootEl.setStyle(key, v)
        }
      })
      subVm._rootEl.setStyle(key, returnValue)
    }
    else {
      if (subVm._rootEl) {
        subVm._rootEl.setStyle(key, value)
      }
    }
  }
}

/**
 * Bind class & style from vm to sub vm and watch their updates.
 */
function mergeClassStyle (target, vm, subVm) {
  const css = vm._options && vm._options.style || {}

  /* istanbul ignore if */
  if (!subVm._rootEl) {
    return
  }

  const className = '@originalRootEl'
  css[className] = subVm._rootEl.classStyle

  function addClassName (list, name) {
    if (typof(list) === 'array') {
      list.unshift(name)
    }
  }

  if (typeof target === 'function') {
    const value = watch(vm, target, v => {
      addClassName(v, className)
      setClassStyle(subVm._rootEl, css, v)
    })
    addClassName(value, className)
    setClassStyle(subVm._rootEl, css, value)
  }
  else if (target != null) {
    addClassName(target, className)
    setClassStyle(subVm._rootEl, css, target)
  }
}

/**
 * bind id to an element
 * each id is unique in a whole vm
 */
export function setId (vm, el, id, target) {
  const map = Object.create(null)

  Object.defineProperties(map, {
    vm: {
      value: target,
      writable: false,
      configurable: false
    },
    el: {
      get: () => el || target._rootEl,
      configurable: false
    }
  })

  if (typeof id === 'function') {
    const handler = id
    id = handler.call(vm)
    if (id || id === 0) {
      vm._ids[id] = map
    }
    watch(vm, handler, (newId) => {
      if (newId) {
        vm._ids[newId] = map
      }
    })
  }
  else if (id && typeof id === 'string') {
    vm._ids[id] = map
  }
}

/**
 * bind attr to an element
 */
function setAttr (vm, el, attr) {
  bindDir(vm, el, 'attr', attr)
}

function setClassStyle (el, css, classList) {
  if (typeof classList === 'string') {
    classList = classList.split(/\s+/)
  }
  classList.forEach((name, i) => {
    classList.splice(i, 1, ...name.split(/\s+/))
  })
  const classStyle = {}
  const length = classList.length

  for (let i = 0; i < length; i++) {
    const style = css[classList[i]]
    if (style) {
      Object.keys(style).forEach((key) => {
        classStyle[key] = style[key]
      })
    }
  }
  el.setClassStyle(classStyle)
}

/**
 * bind classnames to an element
 */
function setClass (vm, el, classList) {
  if (typeof classList !== 'function' && !Array.isArray(classList)) {
    return
  }
  console.log(` => setClass:`, classList)
  if (Array.isArray(classList) && !classList.length) {
    el.setClassStyle({})
    return
  }

  const style = vm._options && vm._options.style || {}
  if (typeof classList === 'function') {
    const value = watch(vm, classList, v => {
      setClassStyle(el, style, v)
    })
    setClassStyle(el, style, value)
  }
  else {
    setClassStyle(el, style, classList)
  }
}

/**
 * bind style to an element
 */
function setStyle (vm, el, style) {
  bindDir(vm, el, 'style', style)
}

/**
 * add an event type and handler to an element and generate a dom update
 */
function setEvent (vm, el, type, handler) {
  el.addEvent(type, bind(handler, vm))
}

/**
 * add all events of an element
 */
function bindEvents (vm, el, events) {
  if (!events) {
    return
  }
  const keys = Object.keys(events)
  let i = keys.length
  while (i--) {
    const key = keys[i]
    let handler = events[key]
    if (typeof handler === 'string') {
      handler = vm[handler]
      /* istanbul ignore if */
      if (!handler) {
        console.warn(`[JS Framework] The event handler "${handler}" is not defined.`)
      }
    }
    setEvent(vm, el, key, handler)
  }
}

/**
 * set a series of members as a kind of an element
 * for example: style, attr, ...
 * if the value is a function then bind the data changes
 */
function bindDir (vm, el, name, data) {
  if (!data) {
    return
  }
  const keys = Object.keys(data)
  let i = keys.length
  while (i--) {
    const key = keys[i]
    const value = data[key]
    if (typeof value === 'function') {
      bindKey(vm, el, name, key, value)
    }
    else {
      el[SETTERS[name]](key, value)
    }
  }
}

/**
 * bind data changes to a certain key to a name series in an element
 */
function bindKey (vm, el, name, key, calc) {
  const methodName = SETTERS[name]
  // watch the calc, and returns a value by calc.call()
  const value = watch(vm, calc, (value) => {
    function handler () {
      el[methodName](key, value)
    }
    const differ = vm && vm._app && vm._app.differ
    if (differ) {
      differ.append('element', el.depth, el.ref, handler)
    }
    else {
      handler()
    }
  })

  el[methodName](key, value)
}

/**
 * watch a calc function and callback if the calc value changes
 */
export function watch (vm, calc, callback) {
  if (vm._static) {
    return calc.call(vm, vm)
  }
  const watcher = new Watcher(vm, calc, function (value, oldValue) {
    /* istanbul ignore if */
    if (typeof value !== 'object' && value === oldValue) {
      return
    }
    callback(value)
  })

  return watcher.value
}
<|endoftext|>


<commit_before>const SETTINGS = {
  useFixtures: false,
  ezLogin: false,
  yellowBox: __DEV__,
  reduxLogging: true,
  includeExamples: __DEV__
}

export default SETTINGS
<commit_msg>Set logging to __DEV__<commit_after>const SETTINGS = {
  useFixtures: false,
  ezLogin: false,
  yellowBox: __DEV__,
  reduxLogging: __DEV__,
  includeExamples: __DEV__
}

export default SETTINGS
<|endoftext|>