<commit_before>import Q from 'q';
import _ from 'lodash';
import Request from './sessionSuperagent';

import StoreSingleton from '../redux/storeSingleton';

import { kGlobalConstants } from '../GlobalConstants';
import * as uploadActions from '../redux/actions/uploadActions';
import * as sessionActions from '../redux/actions/sessionActions';

Q.longStackSupport = true;

const availablePairs = ['appropriations-program_activity', 'program_activity-award_financial',
    'award_financial-award_procurement', 'award_financial-award'];
export const globalFileData = {
    appropriations: {
        name: 'Appropriations Account',
        letter: 'A'
    },
    program_activity: {
        name: 'Program Activity and Object Class Data',
        letter: 'B'
    },
    award_financial: {
        name: 'Award Financial',
        letter: 'C'
    },
    award_procurement: {
        name: 'Award Procurement',
        letter: 'D1'
    },
    award: {
        name: 'Financial Assistance Award',
        letter: 'D2'
    }
};

const determineExpectedPairs = () => {
    const output = [];

    availablePairs.forEach((keyName) => {
        const firstKey = keyName.split('-')[0];
        const secondKey = keyName.split('-')[1];

        const item = {
            key: keyName,
            firstType: globalFileData[firstKey].letter,
            firstName: globalFileData[firstKey].name,
            firstKey,
            secondType: globalFileData[secondKey].letter,
            secondName: globalFileData[secondKey].name,
            secondKey
        };

        output.push(item);
    });

    return output;
};

export const fetchStatus = (submissionId) => {
    const deferred = Q.defer();

    const startTime = new Date().getTime();
    const store = new StoreSingleton().store;

    Request.post(kGlobalConstants.API + 'check_status/')
        .send({ submission_id: submissionId })
        .end((errFile, res) => {
            // calculate how long the API call took
            const endTime = new Date().getTime();
            const duration = endTime - startTime;
            // log the API call duration
            const action = sessionActions.setApiMeta({
                time: duration
            });
            store.dispatch(action);

            if (errFile) {
                let detail = '';
                if (res.body !== null && res.body.hasOwnProperty('message')) {
                    detail = res.body.message;
                }

                deferred.reject({
                    reason: res.statusCode,
                    error: errFile,
                    detail
                });
            }
            else {
                // return only jobs related to CSV validation
                const response = Object.assign({}, res.body);
                store.dispatch(uploadActions.setSubmissionPublishStatus(response.publish_status));
                const csvJobs = [];
                let crossFileJob = {};
                response.jobs.forEach((job) => {
                    if (job.job_type === 'csv_record_validation') {
                        csvJobs.push(job);
                    }
                    else if (job.job_type === 'validation') {
                        crossFileJob = job;
                    }
                });

                response.jobs = csvJobs;
                response.crossFile = crossFileJob;
                deferred.resolve(response);
            }
        });

    return deferred.promise;
};

export const fetchErrorReports = (submissionId) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'submission_error_reports/')
        .send({ submission_id: submissionId })
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

export const fetchWarningReports = (submissionId) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'submission_warning_reports/')
        .send({ submission_id: submissionId })
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

const getFileStates = (status) => {
    const output = {};

    status.jobs.forEach((item) => {
        output[item.file_type] = item;
        output[item.file_type].report = null;
        output[item.file_type].error_count = 0;
        output[item.file_type].warning_count = 0;

        // force an error_data array if no field is passed back in the JSON
        if (!item.hasOwnProperty('error_data')) {
            output[item.file_type].error_data = [];
        }
        else {
            let count = 0;
            item.error_data.forEach((error) => {
                count += parseInt(error.occurrences, 10);
            });

            output[item.file_type].error_count = count;
        }

        // do the same for warnings
        if (!item.hasOwnProperty('warning_data')) {
            output[item.file_type].warning_data = [];
        }
        else {
            let count = 0;
            item.warning_data.forEach((warning) => {
                count += parseInt(warning.occurrences, 10);
            });
            output[item.file_type].warning_count = count;
        }
    });

    return output;
};

const getCrossFileData = (data, type, validKeys) => {
    const output = {};

    let dataType = 'error_data';
    if (type === 'warnings') {
        dataType = 'warning_data';
    }

    for (const index in data.crossFile[dataType]) {
        if (data.crossFile[dataType].hasOwnProperty(index)) {
            // fetch the error object
            const item = data.crossFile[dataType][index];

            // generate possible key names for this pair of target/source files
            const keyNames = [item.target_file + '-' + item.source_file, item.source_file + '-' + item.target_file];
            // determine which is the correct name
            let key = keyNames[0];
            if (_.indexOf(validKeys, key) === -1) {
                key = keyNames[1];
            }

            // check if the key is a valid cross-file pairing we care about
            if (_.indexOf(validKeys, key) === -1) {
                // not a valid pair
                continue;
            }

            // check if we've already seen an error for this pairing
            if (output.hasOwnProperty(key)) {
                // this pair already exists, so append the error to the pair's array
                output[key].push(item);
            }
            else {
                // doesn't exist yet, so create an array with this error
                output[key] = [item];
            }
        }
    }

    return output;
};

const getFileReports = (status, reports) => {
    for (const key in status) {
        if (status.hasOwnProperty(key)) {
            const item = status[key];
            item.report = reports['job_' + item.job_id + '_error_url'];

            // alphabetize any missing and duplicated headers
            item.missing_headers = _.sortBy(item.missing_headers);
            item.duplicated_headers = _.sortBy(item.duplicated_headers);
        }
    }

    return status;
};

const getFileWarningReports = (status, reports) => {
    for (const key in status) {
        if (status.hasOwnProperty(key)) {
            const item = status[key];
            item.warning_report = reports['job_' + item.job_id + '_warning_url'];

            // alphabetize any missing and duplicated headers
            item.missing_headers = _.sortBy(item.missing_headers);
            item.duplicated_headers = _.sortBy(item.duplicated_headers);
        }
    }

    return status;
};

const getCrossFileReports = (type, crossFile, reports) => {
    const crossFileReports = {};

    let keyPrefix = 'cross_';
    if (type === 'warnings') {
        keyPrefix = 'cross_warning_';
    }

    for (const key in crossFile) {
        if (crossFile.hasOwnProperty(key)) {
            crossFileReports[key] = reports[keyPrefix + key];
        }
    }

    return crossFileReports;
};

export const validateSubmission = (submissionId) => {
    const deferred = Q.defer();

    // set the submission ID
    const store = new StoreSingleton().store;
    store.dispatch(uploadActions.setSubmissionId(submissionId));
    // determine the expected cross file validation keys and metadata
    const possiblePairs = determineExpectedPairs();
    store.dispatch(uploadActions.setExpectedCrossPairs(possiblePairs));

    const validKeys = [];
    possiblePairs.forEach((pair) => {
        validKeys.push(pair.key);
    });

    let status;
    let agencyName;
    let crossFile;
    let crossFileState = {};
    let crossFileErrorReports;
    let crossFileWarningReports;

    fetchStatus(submissionId)
        .then((statusRes) => {
            status = getFileStates(statusRes);
            agencyName = statusRes.agency_name;
            crossFile = {
                errors: getCrossFileData(statusRes, 'errors', validKeys),
                warnings: getCrossFileData(statusRes, 'warnings', validKeys)
            };

            crossFileState = {
                job: statusRes.crossFile.job_status,
                file: statusRes.crossFile.file_status
            };

            return fetchErrorReports(submissionId);
        })
        .then((reports) => {
            getFileReports(status, reports);
            crossFileErrorReports = getCrossFileReports('errors', crossFile.errors, reports);

            return fetchWarningReports(submissionId);
        })
        .then((reports) => {
            getFileWarningReports(status, reports);
            crossFileWarningReports = getCrossFileReports('warnings', crossFile.warnings, reports);

            deferred.resolve({
                file: status,
                agencyName,
                crossFile: {
                    state: crossFileState,
                    reports: {
                        errors: crossFileErrorReports,
                        warnings: crossFileWarningReports
                    },
                    data: crossFile
                }
            });
        })
        .catch((err) => {
            const response = Object.assign({}, err.body);
            response.httpStatus = err.status;
            deferred.reject(response);
        });

    return deferred.promise;
};

export const validateDetachedSubmission = (submissionId) => {
    const deferred = Q.defer();

    // set the submission ID
    const store = new StoreSingleton().store;
    store.dispatch(uploadActions.setSubmissionId(submissionId));

    let status;
    fetchStatus(submissionId)
        .then((statusRes) => {
            status = getFileStates(statusRes);
            deferred.resolve(status);
        })
        .catch((err) => {
            const response = Object.assign({}, err.body);
            response.httpStatus = err.status;
            deferred.reject(response);
        });

    return deferred.promise;
};

export const listUsers = () => {
    const deferred = Q.defer();

    Request.get(kGlobalConstants.API + 'list_user_emails/')
        .send({ status: "approved" })
        .end((err, res) => {
            if (err) {
                deferred.reject(err);
            }
            else {
                deferred.resolve(res.body.users);
            }
        });

    return deferred.promise;
};

export const sendNotification = (users, id) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'email_users/')
        .send({
            users,
            email_template: 'review_submission',
            submission_id: id
        })
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile);
            }
            else {
                deferred.resolve(res.body);
            }
        });
    return deferred.promise;
};

export const fetchObligations = (submissionId) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'get_obligations/')
        .send({ submission_id: submissionId })
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

export const submissionReport = (submissionId, warning, fileType, crossType) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'submission/' + submissionId + '/report_url')
        .send({
            warning,
            file_type: fileType,
            cross_type: crossType
        })
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile, res);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

export const fetchSubmissionNarrative = (submissionId) => {
    const deferred = Q.defer();

    Request.get(kGlobalConstants.API + 'submission/' + submissionId + '/narrative')
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

export const saveNarrative = (submissionId, narrative) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'submission/' + submissionId + '/narrative')
        .send(narrative)
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

export const certifySubmission = (submissionId) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'certify_submission/')
        .send({ submission_id: submissionId })
        .end((err, res) => {
            if (err) {
                const response = Object.assign({}, res.body);
                response.httpStatus = res.status;
                deferred.reject(response);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

export const deleteSubmission = (submissionId) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'delete_submission/')
        .send({ submission_id: submissionId })
        .end((err, res) => {
            if (err) {
                deferred.reject(res.body);
            }
            else {
                const output = {
                    message: res.body.message
                };
                deferred.resolve(output);
            }
        });

    return deferred.promise;
};

export const revalidateSubmission = (submissionId) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'restart_validation/')
        .send({ submission_id: submissionId })
        .end((err, res) => {
            if (err) {
                deferred.reject(err);
            }
            else {
                const output = {
                    message: res.body.message
                };
                deferred.resolve(output);
            }
        });

    return deferred.promise;
};

export const isWindow = () => {
    const deferred = Q.defer();

    Request.get(kGlobalConstants.API + 'window/')
        .end((err, res) => {
            if (err) {
                const response = Object.assign({}, res.body);
                response.httpStatus = res.status;
                deferred.reject(response);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};
<commit_msg>parameter was never passed to the api<commit_after>import Q from 'q';
import _ from 'lodash';
import Request from './sessionSuperagent';

import StoreSingleton from '../redux/storeSingleton';

import { kGlobalConstants } from '../GlobalConstants';
import * as uploadActions from '../redux/actions/uploadActions';
import * as sessionActions from '../redux/actions/sessionActions';

Q.longStackSupport = true;

const availablePairs = ['appropriations-program_activity', 'program_activity-award_financial',
    'award_financial-award_procurement', 'award_financial-award'];
export const globalFileData = {
    appropriations: {
        name: 'Appropriations Account',
        letter: 'A'
    },
    program_activity: {
        name: 'Program Activity and Object Class Data',
        letter: 'B'
    },
    award_financial: {
        name: 'Award Financial',
        letter: 'C'
    },
    award_procurement: {
        name: 'Award Procurement',
        letter: 'D1'
    },
    award: {
        name: 'Financial Assistance Award',
        letter: 'D2'
    }
};

const determineExpectedPairs = () => {
    const output = [];

    availablePairs.forEach((keyName) => {
        const firstKey = keyName.split('-')[0];
        const secondKey = keyName.split('-')[1];

        const item = {
            key: keyName,
            firstType: globalFileData[firstKey].letter,
            firstName: globalFileData[firstKey].name,
            firstKey,
            secondType: globalFileData[secondKey].letter,
            secondName: globalFileData[secondKey].name,
            secondKey
        };

        output.push(item);
    });

    return output;
};

export const fetchStatus = (submissionId) => {
    const deferred = Q.defer();

    const startTime = new Date().getTime();
    const store = new StoreSingleton().store;

    Request.post(kGlobalConstants.API + 'check_status/')
        .send({ submission_id: submissionId })
        .end((errFile, res) => {
            // calculate how long the API call took
            const endTime = new Date().getTime();
            const duration = endTime - startTime;
            // log the API call duration
            const action = sessionActions.setApiMeta({
                time: duration
            });
            store.dispatch(action);

            if (errFile) {
                let detail = '';
                if (res.body !== null && res.body.hasOwnProperty('message')) {
                    detail = res.body.message;
                }

                deferred.reject({
                    reason: res.statusCode,
                    error: errFile,
                    detail
                });
            }
            else {
                // return only jobs related to CSV validation
                const response = Object.assign({}, res.body);
                store.dispatch(uploadActions.setSubmissionPublishStatus(response.publish_status));
                const csvJobs = [];
                let crossFileJob = {};
                response.jobs.forEach((job) => {
                    if (job.job_type === 'csv_record_validation') {
                        csvJobs.push(job);
                    }
                    else if (job.job_type === 'validation') {
                        crossFileJob = job;
                    }
                });

                response.jobs = csvJobs;
                response.crossFile = crossFileJob;
                deferred.resolve(response);
            }
        });

    return deferred.promise;
};

export const fetchErrorReports = (submissionId) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'submission_error_reports/')
        .send({ submission_id: submissionId })
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

export const fetchWarningReports = (submissionId) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'submission_warning_reports/')
        .send({ submission_id: submissionId })
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

const getFileStates = (status) => {
    const output = {};

    status.jobs.forEach((item) => {
        output[item.file_type] = item;
        output[item.file_type].report = null;
        output[item.file_type].error_count = 0;
        output[item.file_type].warning_count = 0;

        // force an error_data array if no field is passed back in the JSON
        if (!item.hasOwnProperty('error_data')) {
            output[item.file_type].error_data = [];
        }
        else {
            let count = 0;
            item.error_data.forEach((error) => {
                count += parseInt(error.occurrences, 10);
            });

            output[item.file_type].error_count = count;
        }

        // do the same for warnings
        if (!item.hasOwnProperty('warning_data')) {
            output[item.file_type].warning_data = [];
        }
        else {
            let count = 0;
            item.warning_data.forEach((warning) => {
                count += parseInt(warning.occurrences, 10);
            });
            output[item.file_type].warning_count = count;
        }
    });

    return output;
};

const getCrossFileData = (data, type, validKeys) => {
    const output = {};

    let dataType = 'error_data';
    if (type === 'warnings') {
        dataType = 'warning_data';
    }

    for (const index in data.crossFile[dataType]) {
        if (data.crossFile[dataType].hasOwnProperty(index)) {
            // fetch the error object
            const item = data.crossFile[dataType][index];

            // generate possible key names for this pair of target/source files
            const keyNames = [item.target_file + '-' + item.source_file, item.source_file + '-' + item.target_file];
            // determine which is the correct name
            let key = keyNames[0];
            if (_.indexOf(validKeys, key) === -1) {
                key = keyNames[1];
            }

            // check if the key is a valid cross-file pairing we care about
            if (_.indexOf(validKeys, key) === -1) {
                // not a valid pair
                continue;
            }

            // check if we've already seen an error for this pairing
            if (output.hasOwnProperty(key)) {
                // this pair already exists, so append the error to the pair's array
                output[key].push(item);
            }
            else {
                // doesn't exist yet, so create an array with this error
                output[key] = [item];
            }
        }
    }

    return output;
};

const getFileReports = (status, reports) => {
    for (const key in status) {
        if (status.hasOwnProperty(key)) {
            const item = status[key];
            item.report = reports['job_' + item.job_id + '_error_url'];

            // alphabetize any missing and duplicated headers
            item.missing_headers = _.sortBy(item.missing_headers);
            item.duplicated_headers = _.sortBy(item.duplicated_headers);
        }
    }

    return status;
};

const getFileWarningReports = (status, reports) => {
    for (const key in status) {
        if (status.hasOwnProperty(key)) {
            const item = status[key];
            item.warning_report = reports['job_' + item.job_id + '_warning_url'];

            // alphabetize any missing and duplicated headers
            item.missing_headers = _.sortBy(item.missing_headers);
            item.duplicated_headers = _.sortBy(item.duplicated_headers);
        }
    }

    return status;
};

const getCrossFileReports = (type, crossFile, reports) => {
    const crossFileReports = {};

    let keyPrefix = 'cross_';
    if (type === 'warnings') {
        keyPrefix = 'cross_warning_';
    }

    for (const key in crossFile) {
        if (crossFile.hasOwnProperty(key)) {
            crossFileReports[key] = reports[keyPrefix + key];
        }
    }

    return crossFileReports;
};

export const validateSubmission = (submissionId) => {
    const deferred = Q.defer();

    // set the submission ID
    const store = new StoreSingleton().store;
    store.dispatch(uploadActions.setSubmissionId(submissionId));
    // determine the expected cross file validation keys and metadata
    const possiblePairs = determineExpectedPairs();
    store.dispatch(uploadActions.setExpectedCrossPairs(possiblePairs));

    const validKeys = [];
    possiblePairs.forEach((pair) => {
        validKeys.push(pair.key);
    });

    let status;
    let agencyName;
    let crossFile;
    let crossFileState = {};
    let crossFileErrorReports;
    let crossFileWarningReports;

    fetchStatus(submissionId)
        .then((statusRes) => {
            status = getFileStates(statusRes);
            agencyName = statusRes.agency_name;
            crossFile = {
                errors: getCrossFileData(statusRes, 'errors', validKeys),
                warnings: getCrossFileData(statusRes, 'warnings', validKeys)
            };

            crossFileState = {
                job: statusRes.crossFile.job_status,
                file: statusRes.crossFile.file_status
            };

            return fetchErrorReports(submissionId);
        })
        .then((reports) => {
            getFileReports(status, reports);
            crossFileErrorReports = getCrossFileReports('errors', crossFile.errors, reports);

            return fetchWarningReports(submissionId);
        })
        .then((reports) => {
            getFileWarningReports(status, reports);
            crossFileWarningReports = getCrossFileReports('warnings', crossFile.warnings, reports);

            deferred.resolve({
                file: status,
                agencyName,
                crossFile: {
                    state: crossFileState,
                    reports: {
                        errors: crossFileErrorReports,
                        warnings: crossFileWarningReports
                    },
                    data: crossFile
                }
            });
        })
        .catch((err) => {
            const response = Object.assign({}, err.body);
            response.httpStatus = err.status;
            deferred.reject(response);
        });

    return deferred.promise;
};

export const validateDetachedSubmission = (submissionId) => {
    const deferred = Q.defer();

    // set the submission ID
    const store = new StoreSingleton().store;
    store.dispatch(uploadActions.setSubmissionId(submissionId));

    let status;
    fetchStatus(submissionId)
        .then((statusRes) => {
            status = getFileStates(statusRes);
            deferred.resolve(status);
        })
        .catch((err) => {
            const response = Object.assign({}, err.body);
            response.httpStatus = err.status;
            deferred.reject(response);
        });

    return deferred.promise;
};

export const listUsers = () => {
    const deferred = Q.defer();

    Request.get(kGlobalConstants.API + 'list_user_emails/')
        .send({ status: "approved" })
        .end((err, res) => {
            if (err) {
                deferred.reject(err);
            }
            else {
                deferred.resolve(res.body.users);
            }
        });

    return deferred.promise;
};

export const sendNotification = (users, id) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'email_users/')
        .send({
            users,
            email_template: 'review_submission',
            submission_id: id
        })
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile);
            }
            else {
                deferred.resolve(res.body);
            }
        });
    return deferred.promise;
};

export const fetchObligations = (submissionId) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'get_obligations/')
        .send({ submission_id: submissionId })
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

export const submissionReport = (submissionId, warning, fileType, crossType) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'submission/' + submissionId + '/report_url')
        .send({
            warning,
            file_type: fileType,
            cross_type: crossType
        })
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile, res);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

export const fetchSubmissionNarrative = (submissionId) => {
    const deferred = Q.defer();

    Request.get(kGlobalConstants.API + 'submission/' + submissionId + '/narrative')
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

export const saveNarrative = (submissionId, narrative) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'submission/' + submissionId + '/narrative')
        .send(narrative)
        .end((errFile, res) => {
            if (errFile) {
                deferred.reject(errFile);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

export const certifySubmission = (submissionId) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'certify_submission/')
        .send({ submission_id: submissionId })
        .end((err, res) => {
            if (err) {
                const response = Object.assign({}, res.body);
                response.httpStatus = res.status;
                deferred.reject(response);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};

export const deleteSubmission = (submissionId) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'delete_submission/')
        .send({ submission_id: submissionId })
        .end((err, res) => {
            if (err) {
                deferred.reject(res.body);
            }
            else {
                const output = {
                    message: res.body.message
                };
                deferred.resolve(output);
            }
        });

    return deferred.promise;
};

export const revalidateSubmission = (submissionId, d2_submission=false) => {
    const deferred = Q.defer();

    Request.post(kGlobalConstants.API + 'restart_validation/')
        .send({
            submission_id: submissionId,
            d2_submission: d2_submission
        })
        .end((err, res) => {
            if (err) {
                deferred.reject(err);
            }
            else {
                const output = {
                    message: res.body.message
                };
                deferred.resolve(output);
            }
        });

    return deferred.promise;
};

export const isWindow = () => {
    const deferred = Q.defer();

    Request.get(kGlobalConstants.API + 'window/')
        .end((err, res) => {
            if (err) {
                const response = Object.assign({}, res.body);
                response.httpStatus = res.status;
                deferred.reject(response);
            }
            else {
                deferred.resolve(res.body);
            }
        });

    return deferred.promise;
};
<|endoftext|>


<commit_before>import { entries } from '../utils/object-utils';

export const VALID_ATTRIBUTES = [
  'data-md-text-align'
];

/*
 * A "mixin" to add section attribute support
 * to markup and list sections.
 */
export function attributable(ctx) {
  ctx.attributes = {};

  ctx.setAttribute = (key, value) => {
    if (!VALID_ATTRIBUTES.includes(key)) {
      throw new Error(`Invalid attribute "${key}" was passed. Constrain attributes to the spec-compliant whitelist.`);
    }
    ctx.attributes[key] = value;
  };
  ctx.removeAttribute = key => {
    delete ctx.attributes[key];
  };
  ctx.getAttribute = key => ctx.attributes[key];
  ctx.eachAttribute = cb => {
    entries(ctx.attributes).forEach(([k,v]) => cb(k,v));
  };
}
<commit_msg>Replace Array.includes with utility function for IE11 compat 🐲<commit_after>import { entries } from '../utils/object-utils';
import { contains } from '../utils/array-utils';

export const VALID_ATTRIBUTES = [
  'data-md-text-align'
];

/*
 * A "mixin" to add section attribute support
 * to markup and list sections.
 */
export function attributable(ctx) {
  ctx.attributes = {};

  ctx.setAttribute = (key, value) => {
    if (!contains(VALID_ATTRIBUTES, key)) {
      throw new Error(`Invalid attribute "${key}" was passed. Constrain attributes to the spec-compliant whitelist.`);
    }
    ctx.attributes[key] = value;
  };
  ctx.removeAttribute = key => {
    delete ctx.attributes[key];
  };
  ctx.getAttribute = key => ctx.attributes[key];
  ctx.eachAttribute = cb => {
    entries(ctx.attributes).forEach(([k,v]) => cb(k,v));
  };
}
<|endoftext|>


<commit_before>/*jslint nomen: true */
/*globals define: true */

define(function(require, exports, module) {
  'use strict';

  // Libs
  var $ = require('jquery');
  var _ = require('lib/lodash');
  var Backbone = require('backbone');
  var L = require('lib/leaflet/leaflet.tilejson');

  // LD
  var settings = require('settings');

  var MIN_GRID_ZOOM = 14; // furthest out we'll have interactive grids.

  /**
   * Creates a map view that can support multiple raster and grid layers and
   * is agnostic of the source/type of those layers.
   * TODO: Make this the base class the LocalData-specific dashboard map.
   */
  module.exports = Backbone.View.extend({
    map: null,
    selectedLayer: null,
    markers: {},

    initialize: function(options) {
      L.Icon.Default.imagePath = '/js/lib/leaflet/images';
      _.bindAll(this,
        'render',
        'fitBounds',
        'selectObject',
        'deselectObject',
        'addTileLayer'
      );

      if (options.baselayer) {
        this.baselayer = options.baselayer;
      }

      this.defaultStyle = settings.farZoomStyle;
      this.defaultPointToLayer = function (feature, latlng) {
        return L.circleMarker(latlng, settings.circleMarker);
      };

      this.config = options.config;
    },

    addTileLayer: function (layer) {
      this.map.addLayer(layer);
    },

    removeTileLayer: function (layer) {
      this.map.removeLayer(layer);
    },

    addGridLayer: function (layer, alwaysAddGrid) {
      // Make sure the grid layer is on top.
      if (alwaysAddGrid) {
        this.map.addLayer(layer);
      } else if (this.map.getZoom() >= MIN_GRID_ZOOM) {
        this.map.addLayer(layer);
      }
    },

    getZoom: function() {
      return this.map.getZoom();
    },

    removeGridLayer: function (layer) {
      this.map.removeLayer(layer);
    },

    loading: function() {
      $('.map-loading').show();
    },

    done: function() {
      $('.map-loading').hide();
    },

    fitBounds: function (latLngBounds) {
      if (latLngBounds[0][0] === latLngBounds[1][0] ||
          latLngBounds[0][1] === latLngBounds[1][1]) {
        this.map.setView(latLngBounds[0], 15);
      } else {
        this.map.fitBounds(latLngBounds, {
          reset: true,
          maxZoom: 18
        });
      }
    },

    render: function() {
      // Initialize the map
      var options = {
        zoom: this.config.zoom,
        center: [_.last(this.config.center), _.first(this.config.center)],
        zoomControl: false
      };

      if(_.has(this.config, 'scrollWheelZoom')) {
        options.scrollWheelZoom = this.config.scrollWheelZoom;
      }
      this.map = new L.map(this.$('#map')[0], options);

      // Remove the default "Leaflet |" attribution
      this.map.attributionControl.setPrefix('');

      this.map.on('click', this.clickHandler, this);
      this.map.on('zoomend', this.zoomHandler, this);

      this.map.addControl(L.control.zoom({ position: 'topright' }));

      // Set up the base maps
      this.baseLayer = L.tileLayer(this.baselayer || settings.baseLayer);
      this.satelliteLayer = L.tileLayer(settings.satelliteLayer);
      this.printLayer = L.tileLayer(settings.printLayer);
      this.map.addLayer(this.baseLayer, { attribution: 'LocalData' });
      var baseMaps = {
        "Streets": this.baseLayer,
        "Satellite": this.satelliteLayer,
        "Print": this.printLayer
      };

      // Add the layer control
      // Make sure the layer stays in the background
      L.control.layers(baseMaps).addTo(this.map);
      this.baseLayer.bringToBack();
      this.map.on('baselayerchange', function(event) {
        event.layer.bringToBack();
      });

      return this;
    },

    clickHandler: function (event) {
      this.trigger('click', event);
    },

    zoomHandler: function(event) {
      this.trigger('zoomend', event);
    },

    hasLayer: function(layer) {
      return this.map.hasLayer(layer);
    },

    /**
     * Highlight a selected object
     * @param  {Object} event
     */
    selectObject: function (feature) {
      this.deselectObject();

      // Add a layer with a visual selection
      this.selectedLayer = new L.GeoJSON(feature, {
        pointToLayer: this.defaultPointToLayer,
        style: settings.selectedStyle
      });

      this.map.addLayer(this.selectedLayer);
      this.selectedLayer.bringToFront();
    },

    /**
     * Un-highlight any previously selected object
     */
    deselectObject: function() {
      if (this.selectedLayer !== null) {
        this.map.removeLayer(this.selectedLayer);
        delete this.selectedLayer;
      }
    },

    goToLatLng: function (latlng) {
      // Remove any existing location marker
      if (this.markers.location !== undefined) {
        this.map.removeLayer(this.markers.location);
      }

      this.map.setView(latlng, 18);
      var marker = L.marker(latlng).addTo(this.map);
      this.markers.location = marker;
    }
  });
});
<commit_msg>Fix issue with project pages where basemap changes would cover parcels<commit_after>/*jslint nomen: true */
/*globals define: true */

/*
Used on project pages
*/
define(function(require, exports, module) {
  'use strict';

  // Libs
  var $ = require('jquery');
  var _ = require('lib/lodash');
  var Backbone = require('backbone');
  var L = require('lib/leaflet/leaflet.tilejson');

  // LD
  var settings = require('settings');

  var MIN_GRID_ZOOM = 14; // furthest out we'll have interactive grids.

  /**
   * Creates a map view that can support multiple raster and grid layers and
   * is agnostic of the source/type of those layers.
   * TODO: Make this the base class the LocalData-specific dashboard map.
   */
  module.exports = Backbone.View.extend({
    map: null,
    selectedLayer: null,
    markers: {},

    initialize: function(options) {
      L.Icon.Default.imagePath = '/js/lib/leaflet/images';
      _.bindAll(this,
        'render',
        'fitBounds',
        'selectObject',
        'deselectObject',
        'addTileLayer'
      );

      if (options.baselayer) {
        this.baselayer = options.baselayer;
      }

      this.defaultStyle = settings.farZoomStyle;
      this.defaultPointToLayer = function (feature, latlng) {
        return L.circleMarker(latlng, settings.circleMarker);
      };

      this.config = options.config;
    },

    addTileLayer: function (layer) {
      this.map.addLayer(layer);
    },

    removeTileLayer: function (layer) {
      this.map.removeLayer(layer);
    },

    addGridLayer: function (layer, alwaysAddGrid) {
      // Make sure the grid layer is on top.
      if (alwaysAddGrid) {
        this.map.addLayer(layer);
      } else if (this.map.getZoom() >= MIN_GRID_ZOOM) {
        this.map.addLayer(layer);
      }
    },

    getZoom: function() {
      return this.map.getZoom();
    },

    removeGridLayer: function (layer) {
      this.map.removeLayer(layer);
    },

    loading: function() {
      $('.map-loading').show();
    },

    done: function() {
      $('.map-loading').hide();
    },

    fitBounds: function (latLngBounds) {
      if (latLngBounds[0][0] === latLngBounds[1][0] ||
          latLngBounds[0][1] === latLngBounds[1][1]) {
        this.map.setView(latLngBounds[0], 15);
      } else {
        this.map.fitBounds(latLngBounds, {
          reset: true,
          maxZoom: 18
        });
      }
    },

    render: function() {
      // Initialize the map
      var options = {
        zoom: this.config.zoom,
        center: [_.last(this.config.center), _.first(this.config.center)],
        zoomControl: false
      };

      if(_.has(this.config, 'scrollWheelZoom')) {
        options.scrollWheelZoom = this.config.scrollWheelZoom;
      }
      this.map = new L.map(this.$('#map')[0], options);

      // Remove the default "Leaflet |" attribution
      this.map.attributionControl.setPrefix('');

      this.map.on('click', this.clickHandler, this);
      this.map.on('zoomend', this.zoomHandler, this);

      this.map.addControl(L.control.zoom({ position: 'topright' }));

      // Set up the base maps
      this.baseLayer = L.tileLayer(this.baselayer || settings.baseLayer);
      this.satelliteLayer = L.tileLayer(settings.satelliteLayer);
      this.printLayer = L.tileLayer(settings.printLayer);
      this.map.addLayer(this.baseLayer, { attribution: 'LocalData' });
      var baseMaps = {
        "Streets": this.baseLayer,
        "Satellite": this.satelliteLayer,
        "Print": this.printLayer
      };

      // Add the layer control
      // Make sure the layer stays in the background
      L.control.layers(baseMaps).addTo(this.map);
      this.baseLayer.bringToBack();
      this.map.on('baselayerchange', function(event) {
        event.layer.bringToBack();

        // The rendered parcel layer is sometimes not set with a z-index,
        // so we need to manually set the baselayer back further
        event.layer.setZIndex(-10);
      });

      return this;
    },

    clickHandler: function (event) {
      this.trigger('click', event);
    },

    zoomHandler: function(event) {
      this.trigger('zoomend', event);
    },

    hasLayer: function(layer) {
      return this.map.hasLayer(layer);
    },

    /**
     * Highlight a selected object
     * @param  {Object} event
     */
    selectObject: function (feature) {
      this.deselectObject();

      // Add a layer with a visual selection
      this.selectedLayer = new L.GeoJSON(feature, {
        pointToLayer: this.defaultPointToLayer,
        style: settings.selectedStyle
      });

      this.map.addLayer(this.selectedLayer);
      this.selectedLayer.bringToFront();
    },

    /**
     * Un-highlight any previously selected object
     */
    deselectObject: function() {
      if (this.selectedLayer !== null) {
        this.map.removeLayer(this.selectedLayer);
        delete this.selectedLayer;
      }
    },

    goToLatLng: function (latlng) {
      // Remove any existing location marker
      if (this.markers.location !== undefined) {
        this.map.removeLayer(this.markers.location);
      }

      this.map.setView(latlng, 18);
      var marker = L.marker(latlng).addTo(this.map);
      this.markers.location = marker;
    }
  });
});
<|endoftext|>


<commit_before>var rasp2c = require('rasp2c');

// i2c address of the 7-segment display.
// You can find this by running:
// i2cdetect -y 1
var display_address = '0x70';

// The list of available addresses for the visible elements of the display.
var addresses = ['0x00', '0x02', '0x04', '0x06', '0x08'];

// Hexadecimal character lookup table (row 1 = 0..9, row 2 = A..F)
var digits = [ 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,
  0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71 ];

// Base registers for system control.
var HT16K33_REGISTER_DISPLAY_SETUP        = 0x80;
var HT16K33_REGISTER_SYSTEM_SETUP         = 0x20;
var HT16K33_REGISTER_DIMMING              = 0xE0; // Base address for changing brightness.

// Blink rate
var HT16K33_BLINKRATE_OFF    = 0x00;
var HT16K33_BLINKRATE_2HZ    = 0x01;
var HT16K33_BLINKRATE_1HZ    = 0x02;
var HT16K33_BLINKRATE_HALFHZ = 0x03;

var clear = function () {
  addresses.forEach(function (address) {
    rasp2c.set(display_address, address, '0x00', function (err, result) {
      if (err) {
        throw (err);
      }
    });
  });
};

// Set digit at to value.
var setDigit = function (pos, digit) {
  if (isNaN(digit)) {
    console.log("Not attempting to set invalid digit '" + digit + "'");
    return;
  }

  rasp2c.set(display_address, addresses[pos], digits[digit], function (err, result) {
    if (err) {
      console.error('Error in setDigit with pos=' + pos + ' and digit=' + digit);
      throw (err);
    }
  });
};

// Set digit at pos to value.
var setRaw = function (pos, rawValue) {
  rasp2c.set(display_address, addresses[pos], rawValue, function (err, result) {
    if (err) {
      console.error('Error in setRaw with pos=' + pos + ' and rawValue=' + rawValue);
      throw (err);
    }
  });
};

// Set digit at address to value.
var setRawAddress = function (address, rawValue) {
  rasp2c.set(display_address, address, rawValue, function (err, result) {
    if (err) {
      throw (err.stack);
    }
  });
};

// Enable the colon character.
function enableColon() {
  // The colon character is at position index 2.
  rasp2c.set(display_address, addresses[2], '0x02', function (err, result) {
    if (err) {
      throw (err);
    }
  });
}

// Disable the colon character.
function disableColon() {
  // The colon character is at position index 2.
  rasp2c.set(display_address, addresses[2], 0, function (err, result) {
    if (err) {
      throw (err);
    }
  });
}

// Sets the brightness level from 0 to 15.
var setBrightness = function (brightness) {
  if (brightness > 15) {
    brightness = 15;
  }

  setRawAddress(HT16K33_REGISTER_DIMMING | brightness, 0x00);
};

// Sets the blink rate.
var setBlinkRate = function (blinkRate) {
  if (blinkRate > HT16K33_BLINKRATE_HALFHZ) {
    blinkRate = HT16K33_BLINKRATE_OFF;
  }

  setRawAddress(HT16K33_REGISTER_DISPLAY_SETUP | 0x01 | (blinkRate << 1), 0x00);
};

// Sends the system setup message.
exports.init = function (callback) {
  rasp2c.init(() => {
    setRawAddress(HT16K33_REGISTER_SYSTEM_SETUP | 0x01, 0x00);
    callback(null);
  });
}

exports.setDigit              = setDigit;
exports.setRaw                = setRaw;
exports.setRawAddress         = setRawAddress;
exports.clear                 = clear;
exports.enableColon           = enableColon;
exports.disableColon          = disableColon;
exports.setBrightness         = setBrightness;
exports.setBlinkRate          = setBlinkRate;

exports.MAX_BRIGHTNESS     = 15;
exports.BLINKRATE_OFF      = HT16K33_BLINKRATE_OFF;
exports.BLINKRATE_2HZ      = HT16K33_BLINKRATE_2HZ;
exports.BLINKRATE_1HZ      = HT16K33_BLINKRATE_1HZ;
exports.BLINKRATE_HALFHZ   = HT16K33_BLINKRATE_HALFHZ;
<commit_msg>Whitespace<commit_after>var rasp2c = require('rasp2c');

// i2c address of the 7-segment display.
// You can find this by running:
// i2cdetect -y 1
var display_address = '0x70';

// The list of available addresses for the visible elements of the display.
var addresses = ['0x00', '0x02', '0x04', '0x06', '0x08'];

// Hexadecimal character lookup table (row 1 = 0..9, row 2 = A..F)
var digits = [ 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,
  0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71 ];

// Base registers for system control.
var HT16K33_REGISTER_DISPLAY_SETUP        = 0x80;
var HT16K33_REGISTER_SYSTEM_SETUP         = 0x20;
var HT16K33_REGISTER_DIMMING              = 0xE0; // Base address for changing brightness.

// Blink rate
var HT16K33_BLINKRATE_OFF    = 0x00;
var HT16K33_BLINKRATE_2HZ    = 0x01;
var HT16K33_BLINKRATE_1HZ    = 0x02;
var HT16K33_BLINKRATE_HALFHZ = 0x03;

var clear = function () {
  addresses.forEach(function (address) {
    rasp2c.set(display_address, address, '0x00', function (err, result) {
      if (err) {
        throw (err);
      }
    });
  });
};

// Set digit at to value.
var setDigit = function (pos, digit) {
  if (isNaN(digit)) {
    console.log("Not attempting to set invalid digit '" + digit + "'");
    return;
  }

  rasp2c.set(display_address, addresses[pos], digits[digit], function (err, result) {
    if (err) {
      console.error('Error in setDigit with pos=' + pos + ' and digit=' + digit);
      throw (err);
    }
  });
};

// Set digit at pos to value.
var setRaw = function (pos, rawValue) {
  rasp2c.set(display_address, addresses[pos], rawValue, function (err, result) {
    if (err) {
      console.error('Error in setRaw with pos=' + pos + ' and rawValue=' + rawValue);
      throw (err);
    }
  });
};

// Set digit at address to value.
var setRawAddress = function (address, rawValue) {
  rasp2c.set(display_address, address, rawValue, function (err, result) {
    if (err) {
      throw (err.stack);
    }
  });
};

// Enable the colon character.
function enableColon() {
  // The colon character is at position index 2.
  rasp2c.set(display_address, addresses[2], '0x02', function (err, result) {
    if (err) {
      throw (err);
    }
  });
}

// Disable the colon character.
function disableColon() {
  // The colon character is at position index 2.
  rasp2c.set(display_address, addresses[2], 0, function (err, result) {
    if (err) {
      throw (err);
    }
  });
}

// Sets the brightness level from 0 to 15.
var setBrightness = function (brightness) {
  if (brightness > 15) {
    brightness = 15;
  }

  setRawAddress(HT16K33_REGISTER_DIMMING | brightness, 0x00);
};

// Sets the blink rate.
var setBlinkRate = function (blinkRate) {
  if (blinkRate > HT16K33_BLINKRATE_HALFHZ) {
    blinkRate = HT16K33_BLINKRATE_OFF;
  }

  setRawAddress(HT16K33_REGISTER_DISPLAY_SETUP | 0x01 | (blinkRate << 1), 0x00);
};

// Sends the system setup message.
exports.init = function (callback) {
  rasp2c.init(() => {
    setRawAddress(HT16K33_REGISTER_SYSTEM_SETUP | 0x01, 0x00);
    callback(null);
  });
}

exports.setDigit              = setDigit;
exports.setRaw                = setRaw;
exports.setRawAddress         = setRawAddress;
exports.clear                 = clear;
exports.enableColon           = enableColon;
exports.disableColon          = disableColon;
exports.setBrightness         = setBrightness;
exports.setBlinkRate          = setBlinkRate;

exports.MAX_BRIGHTNESS     = 15;
exports.BLINKRATE_OFF      = HT16K33_BLINKRATE_OFF;
exports.BLINKRATE_2HZ      = HT16K33_BLINKRATE_2HZ;
exports.BLINKRATE_1HZ      = HT16K33_BLINKRATE_1HZ;
exports.BLINKRATE_HALFHZ   = HT16K33_BLINKRATE_HALFHZ;
<|endoftext|>


<commit_before>(function(){
	"use strict";

	luga.namespace("luga.data.region.traits");

	/**
	 * @typedef {Object} luga.data.region.traits.options
	 *
	 * @property {jQuery}                                 node          A jQuery object wrapping the Region's node. Required
	 * @property {luga.data.DataSet|luga.data.DetailSet}  dataSource    DataSource. Required
	 */

	var CONST = {
		CUSTOM_ATTRIBUTES: {
			SELECT: "data-lugaregion-select",
			SET_ROW_ID: "data-lugaregion-setrowid",
			SET_ROW_INDEX: "data-lugaregion-setrowindex",
			SORT: "data-lugaregion-sort"
		},
		SELECTORS: {
			SELECT: "*[data-lugaregion-select]",
			SET_ROW_ID: "*[data-lugaregion-setrowid]",
			SET_ROW_INDEX: "*[data-lugaregion-setrowindex]",
			SORT: "*[data-lugaregion-sort]"
		}
	};

	/**
	 * Handles data-lugaregion-select
	 * @param {luga.data.region.traits.options} options
	 */
	luga.data.region.traits.select = function(options){
		var nodes = options.node.find(CONST.SELECTORS.SELECT);
		if(nodes.length > 0){
			if(options.dataSource.getCurrentRowIndex === undefined){
				// It's a detailSet, abort
				return;
			}
			var cssClass = nodes.attr(CONST.CUSTOM_ATTRIBUTES.SELECT);
			// Clean-up
			nodes.removeClass(cssClass);
			// Default to zero
			var index = 0;

			if(options.dataSource.getCurrentRowIndex() === -1){
				// Remove class from everyone
				nodes.removeClass(cssClass);
			}
			else{
				index = options.dataSource.getCurrentRowIndex();
				// Apply CSS
				jQuery(nodes.get(index)).addClass(cssClass);
			}

			// Attach click event
			nodes.each(function(index, item){
				var jItem = jQuery(item);
				jItem.click(function(event){
					event.preventDefault();
					nodes.removeClass(cssClass);
					jItem.addClass(cssClass);
				});
			});
		}
	};

	/**
	 * Handles data-lugaregion-setrowid
	 * @param {luga.data.region.traits.options} options
	 */
	luga.data.region.traits.setRowId = function(options){
		options.node.find(CONST.SELECTORS.SET_ROW_ID).each(function(index, item){
			var jItem = jQuery(item);
			jItem.click(function(event){
				event.preventDefault();
				var rowId = jItem.attr(CONST.CUSTOM_ATTRIBUTES.SET_ROW_ID);
				options.dataSource.setCurrentRowId(rowId);
			});
		});
	};

	/**
	 * Handles data-lugaregion-setrowindex
	 * @param {luga.data.region.traits.options} options
	 */
	luga.data.region.traits.setRowIndex = function(options){
		options.node.find(CONST.SELECTORS.SET_ROW_INDEX).each(function(index, item){
			var jItem = jQuery(item);
			jItem.click(function(event){
				event.preventDefault();
				var rowIndex = parseInt(jItem.attr(CONST.CUSTOM_ATTRIBUTES.SET_ROW_INDEX), 10);
				options.dataSource.setCurrentRowIndex(rowIndex);
			});
		});
	};

	/**
	 * Handles data-lugaregion-sort
	 * @param {luga.data.region.traits.options} options
	 */
	luga.data.region.traits.sort = function(options){
		options.node.find(CONST.SELECTORS.SORT).each(function(index, item){
			var jItem = jQuery(item);
			jItem.click(function(event){
				event.preventDefault();
				var sortCol = jItem.attr(CONST.CUSTOM_ATTRIBUTES.SORT);
				options.dataSource.sort(sortCol);
			});
		});
	};

}());<commit_msg>Data: use .on("click") instead of .click()<commit_after>(function(){
	"use strict";

	luga.namespace("luga.data.region.traits");

	/**
	 * @typedef {Object} luga.data.region.traits.options
	 *
	 * @property {jQuery}                                 node          A jQuery object wrapping the Region's node. Required
	 * @property {luga.data.DataSet|luga.data.DetailSet}  dataSource    DataSource. Required
	 */

	var CONST = {
		CUSTOM_ATTRIBUTES: {
			SELECT: "data-lugaregion-select",
			SET_ROW_ID: "data-lugaregion-setrowid",
			SET_ROW_INDEX: "data-lugaregion-setrowindex",
			SORT: "data-lugaregion-sort"
		},
		SELECTORS: {
			SELECT: "*[data-lugaregion-select]",
			SET_ROW_ID: "*[data-lugaregion-setrowid]",
			SET_ROW_INDEX: "*[data-lugaregion-setrowindex]",
			SORT: "*[data-lugaregion-sort]"
		}
	};

	/**
	 * Handles data-lugaregion-select
	 * @param {luga.data.region.traits.options} options
	 */
	luga.data.region.traits.select = function(options){
		var nodes = options.node.find(CONST.SELECTORS.SELECT);
		if(nodes.length > 0){
			if(options.dataSource.getCurrentRowIndex === undefined){
				// It's a detailSet, abort
				return;
			}
			var cssClass = nodes.attr(CONST.CUSTOM_ATTRIBUTES.SELECT);
			// Clean-up
			nodes.removeClass(cssClass);
			// Default to zero
			var index = 0;

			if(options.dataSource.getCurrentRowIndex() === -1){
				// Remove class from everyone
				nodes.removeClass(cssClass);
			}
			else{
				index = options.dataSource.getCurrentRowIndex();
				// Apply CSS
				jQuery(nodes.get(index)).addClass(cssClass);
			}

			// Attach click event
			nodes.each(function(index, item){
				var jItem = jQuery(item);
				jItem.on("click", function(event){
					event.preventDefault();
					nodes.removeClass(cssClass);
					jItem.addClass(cssClass);
				});
			});
		}
	};

	/**
	 * Handles data-lugaregion-setrowid
	 * @param {luga.data.region.traits.options} options
	 */
	luga.data.region.traits.setRowId = function(options){
		options.node.find(CONST.SELECTORS.SET_ROW_ID).each(function(index, item){
			var jItem = jQuery(item);
			jItem.on("click", function(event){
				event.preventDefault();
				var rowId = jItem.attr(CONST.CUSTOM_ATTRIBUTES.SET_ROW_ID);
				options.dataSource.setCurrentRowId(rowId);
			});
		});
	};

	/**
	 * Handles data-lugaregion-setrowindex
	 * @param {luga.data.region.traits.options} options
	 */
	luga.data.region.traits.setRowIndex = function(options){
		options.node.find(CONST.SELECTORS.SET_ROW_INDEX).each(function(index, item){
			var jItem = jQuery(item);
			jItem.on("click", function(event){
				event.preventDefault();
				var rowIndex = parseInt(jItem.attr(CONST.CUSTOM_ATTRIBUTES.SET_ROW_INDEX), 10);
				options.dataSource.setCurrentRowIndex(rowIndex);
			});
		});
	};

	/**
	 * Handles data-lugaregion-sort
	 * @param {luga.data.region.traits.options} options
	 */
	luga.data.region.traits.sort = function(options){
		options.node.find(CONST.SELECTORS.SORT).each(function(index, item){
			var jItem = jQuery(item);
			jItem.on("click", function(event){
				event.preventDefault();
				var sortCol = jItem.attr(CONST.CUSTOM_ATTRIBUTES.SORT);
				options.dataSource.sort(sortCol);
			});
		});
	};

}());<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import elementType from 'react-prop-types/lib/elementType';

import GridTable from './GridTable'
import GridBody from './GridBody'
import GridRow from './GridRow'
import GridCell from './GridCell'
import GridColumnHeader from './GridColumnHeader'
import GridHeader from './GridHeader'
import GridHeaderCell from './GridHeaderCell'
import BaseCellRenderer from './BaseCellRenderer'
import ObjectCellRenderer from './ObjectCellRenderer'
import ArrayCellRenderer from './ArrayCellRenderer'
import Filter from './Filter'
import ToolbarDefault from './ToolbarDefault'
import Pager from './Pager'

import { filterConfigFromProp, sortConfigFromProp } from './utils'
import { ASCENDING, DESCENDING } from './constants'

const Ardagryd = (props)=>{
        //Merge custom and default config
        const config = Object.assign({},defaultConfig,props.config);

        //Get components from config
        const Grid = config.grid;
        const GridHeader = config.header;

        const Toolbar = config.toolbar;
        const GridBody = config.body;
        const ColumnHeader = config.columnHeader;

        //Get custom column-configuration
        const columnConfig = props.columns;

        config.eventHandler = props.dispatch;

        //Columns to show
        var columnKeys = [];

        const idColumn = getOrCreateIdColumn(props.objects,columnConfig);

        //Filter objects based on supplied filter strings
        let filters = {};
        let filterConfig = filterConfigFromProp(props.filter);
        let columnNamesWithFilter = filterConfig.map((filterObject) => {
            filters[filterObject.columnName] = filterObject.expression;
            return filterObject.columnName;
         });

        let objects = props.objects.filter((currentObjectToBeFiltered) => {
            for (let i in columnNamesWithFilter){

                if (!currentObjectToBeFiltered[columnNamesWithFilter[i]]){
                    return false;
                } else if(!(JSON.stringify(currentObjectToBeFiltered[columnNamesWithFilter[i]]).toLowerCase().indexOf(filters[columnNamesWithFilter[i]].toLowerCase()) > -1)){
                    return false;
                }
            }
            return true;
        });

        //Generate array with selected column-names in configured order
        let availableColumnKeys;
        if (props.objects.length > 0){
            availableColumnKeys = Object.keys(props.objects[0]);
            Object.keys(columnConfig).forEach((columnName)=>{
              if (availableColumnKeys.indexOf(columnName) === -1){
                availableColumnKeys.push(columnName);
              }
            });

            columnKeys = availableColumnKeys.filter((currentColumnKey) => {
                const configForColumn = columnConfig[currentColumnKey];
                if(config.showColumnsWithoutConfig){
                    return configForColumn === undefined || configForColumn.show !== false;
                } else {
                    //Show only configured columns
                    return configForColumn !== undefined && configForColumn.show !== false
                }
            }).sort((a,b) => {
                const configForA = columnConfig[a];
                const configForB = columnConfig[b];
                const valueForA = configForA && configForA.order != null ? configForA.order : 1000;
                const valueForB = configForB && configForB.order != null ? configForB.order : 1000;
                return valueForA-valueForB;
            });
        }

        //Sort

    //default order

    var { columnName, order } = sortConfigFromProp(props.sort);

    //check for sort configuration

    if (columnName){
            // TODO allow to pass in a custom sort and/or sortValueGetter function

            // temporary array holds objects with position and sort-value
            let mapped = objects.map(function(el, i) {
              let value = el[columnName];

              if (typeof value == "string"){
                  value = value.toLowerCase();
              }
              else {
                  value = JSON.stringify(value).toLowerCase();
              }
              return { index: i, value: value };
            });

            // sorting the mapped array containing the reduced values
            mapped.sort((a, b) => {
                return +(a.value > b.value) || +(a.value === b.value) - 1;
            });

            // container for the resulting order
            const list = mapped.map(function(el){
              return objects[el.index];
            });

            objects = list;
        }

        //reverse order on "descending" sort option
        if (order === DESCENDING){
            objects.reverse();
        }

        let tools;
        if(config.showToolbar){
            tools = (<Toolbar config={config} columnKeys={columnKeys} columns={columnConfig} filter={filterConfig}/>)
        }

        let pagedObjects;
        const paging = config.paging;
        if (paging){
            pagedObjects = objects.slice(props.skip, props.skip+paging);
        } else {
            pagedObjects = props.objects;
        }
        const pager = () => {
            if(config.paging){
                return(
                    <Pager length={objects.length} updatePagination={config.eventHandler} skip={props.skip} paging={config.paging} />
                )
            }
        };
        return(

                {pager()}
                <Grid>
                    <GridHeader>
                        <ColumnHeader columns={columnConfig} config={config} columnKeys={columnKeys} sort={props.sort} />
                        {tools}
                    </GridHeader>
                    <GridBody idColumn={idColumn} objects={pagedObjects} columns={columnConfig} config={config} columnKeys={columnKeys}/>
                </Grid>

        );

}

const defaultConfig = {
    grid: GridTable,
    body: GridBody,
    row: GridRow,
    cell: GridCell,
    columnHeader: GridColumnHeader,
    header: GridHeader,
    columnHeaderCell: GridHeaderCell,
    cellRendererBase: BaseCellRenderer,
    cellRendererObject: ObjectCellRenderer,
    cellRendererArray: ArrayCellRenderer,
    filter: Filter,
    toolbar: ToolbarDefault,
    showToolbar: true,
    showColumnsWithoutConfig: true, //show all columns which are not explicitly hidden
    paging: 10,
    displayValueGetter: ({value}) => value
};

Ardagryd.defaultProps = {
    config: {},
    columns: {},
    dispatch: () => {}
};

const filterConfig = React.PropTypes.shape({
    columnName: React.PropTypes.string.isRequired,
    expression: React.PropTypes.string.isRequired
});

const sortConfig = React.PropTypes.shape({
    columnName: React.PropTypes.string.isRequired,
    order: React.PropTypes.oneOf([ASCENDING, DESCENDING])
});

Ardagryd.propTypes = {
    objects: PropTypes.arrayOf(PropTypes.object),
    config: PropTypes.object.isRequired,
    columns: PropTypes.objectOf(PropTypes.shape({
      displayValueGetter: PropTypes.func,
      id: PropTypes.bool,
      label: PropTypes.string,
      order: PropTypes.number,
      hideTools: PropTypes.bool,
      sortable: PropTypes.bool,
      cellRenderer: elementType,
      filter: PropTypes.string
    })).isRequired,
    dispatch: PropTypes.func.isRequired,
    sort: PropTypes.oneOfType([
        PropTypes.string,
        sortConfig,
        PropTypes.arrayOf(sortConfig)
    ]),
    filter: PropTypes.oneOfType([
        filterConfig,
        PropTypes.arrayOf(filterConfig)])
};

//Find id-column, or enhance objects with ids
function getOrCreateIdColumn(objects, columns){
    //check if explicit id-column is set in column-config
    const idColumn = Object.keys(columns).find((key)=>{
        if(columns[key].id){
            return true;
        }
    });

    if (idColumn){
        return idColumn;
    } else if (objects.length > 0 && objects[0].id !== undefined) {
        //check if objects have a id-property
        return "id"
    } else {
        //TODO: use some type of hashing
        //generate id-property
        let index = 0;
        objects.map((object) => {
            object.id = index++;
        });
        return "id"
    }
}

export default Ardagryd;
<commit_msg>show warnings if the old sort/filter props are set for a column<commit_after>import React, { Component, PropTypes } from 'react'
import elementType from 'react-prop-types/lib/elementType';

import GridTable from './GridTable'
import GridBody from './GridBody'
import GridRow from './GridRow'
import GridCell from './GridCell'
import GridColumnHeader from './GridColumnHeader'
import GridHeader from './GridHeader'
import GridHeaderCell from './GridHeaderCell'
import BaseCellRenderer from './BaseCellRenderer'
import ObjectCellRenderer from './ObjectCellRenderer'
import ArrayCellRenderer from './ArrayCellRenderer'
import Filter from './Filter'
import ToolbarDefault from './ToolbarDefault'
import Pager from './Pager'

import { filterConfigFromProp, sortConfigFromProp } from './utils'
import { ASCENDING, DESCENDING } from './constants'

const Ardagryd = (props)=>{
        //Merge custom and default config
        const config = Object.assign({},defaultConfig,props.config);

        //Get components from config
        const Grid = config.grid;
        const GridHeader = config.header;

        const Toolbar = config.toolbar;
        const GridBody = config.body;
        const ColumnHeader = config.columnHeader;

        //Get custom column-configuration
        const columnConfig = props.columns;

        config.eventHandler = props.dispatch;

        //Columns to show
        var columnKeys = [];

        const idColumn = getOrCreateIdColumn(props.objects,columnConfig);

        //Filter objects based on supplied filter strings
        let filters = {};
        let filterConfig = filterConfigFromProp(props.filter);
        let columnNamesWithFilter = filterConfig.map((filterObject) => {
            filters[filterObject.columnName] = filterObject.expression;
            return filterObject.columnName;
         });

        let objects = props.objects.filter((currentObjectToBeFiltered) => {
            for (let i in columnNamesWithFilter){

                if (!currentObjectToBeFiltered[columnNamesWithFilter[i]]){
                    return false;
                } else if(!(JSON.stringify(currentObjectToBeFiltered[columnNamesWithFilter[i]]).toLowerCase().indexOf(filters[columnNamesWithFilter[i]].toLowerCase()) > -1)){
                    return false;
                }
            }
            return true;
        });

        //Generate array with selected column-names in configured order
        let availableColumnKeys;
        if (props.objects.length > 0){
            availableColumnKeys = Object.keys(props.objects[0]);
            Object.keys(columnConfig).forEach((columnName)=>{
              if (availableColumnKeys.indexOf(columnName) === -1){
                availableColumnKeys.push(columnName);
              }
            });

            columnKeys = availableColumnKeys.filter((currentColumnKey) => {
                const configForColumn = columnConfig[currentColumnKey];
                if(config.showColumnsWithoutConfig){
                    return configForColumn === undefined || configForColumn.show !== false;
                } else {
                    //Show only configured columns
                    return configForColumn !== undefined && configForColumn.show !== false
                }
            }).sort((a,b) => {
                const configForA = columnConfig[a];
                const configForB = columnConfig[b];
                const valueForA = configForA && configForA.order != null ? configForA.order : 1000;
                const valueForB = configForB && configForB.order != null ? configForB.order : 1000;
                return valueForA-valueForB;
            });
        }

        //Sort

    //default order

    var { columnName, order } = sortConfigFromProp(props.sort);

    //check for sort configuration

    if (columnName){
            // TODO allow to pass in a custom sort and/or sortValueGetter function

            // temporary array holds objects with position and sort-value
            let mapped = objects.map(function(el, i) {
              let value = el[columnName];

              if (typeof value == "string"){
                  value = value.toLowerCase();
              }
              else {
                  value = JSON.stringify(value).toLowerCase();
              }
              return { index: i, value: value };
            });

            // sorting the mapped array containing the reduced values
            mapped.sort((a, b) => {
                return +(a.value > b.value) || +(a.value === b.value) - 1;
            });

            // container for the resulting order
            const list = mapped.map(function(el){
              return objects[el.index];
            });

            objects = list;
        }

        //reverse order on "descending" sort option
        if (order === DESCENDING){
            objects.reverse();
        }

        let tools;
        if(config.showToolbar){
            tools = (<Toolbar config={config} columnKeys={columnKeys} columns={columnConfig} filter={filterConfig}/>)
        }

        let pagedObjects;
        const paging = config.paging;
        if (paging){
            pagedObjects = objects.slice(props.skip, props.skip+paging);
        } else {
            pagedObjects = props.objects;
        }
        const pager = () => {
            if(config.paging){
                return(
                    <Pager length={objects.length} updatePagination={config.eventHandler} skip={props.skip} paging={config.paging} />
                )
            }
        };
        return(

                {pager()}
                <Grid>
                    <GridHeader>
                        <ColumnHeader columns={columnConfig} config={config} columnKeys={columnKeys} sort={props.sort} />
                        {tools}
                    </GridHeader>
                    <GridBody idColumn={idColumn} objects={pagedObjects} columns={columnConfig} config={config} columnKeys={columnKeys}/>
                </Grid>

        );

}

const defaultConfig = {
    grid: GridTable,
    body: GridBody,
    row: GridRow,
    cell: GridCell,
    columnHeader: GridColumnHeader,
    header: GridHeader,
    columnHeaderCell: GridHeaderCell,
    cellRendererBase: BaseCellRenderer,
    cellRendererObject: ObjectCellRenderer,
    cellRendererArray: ArrayCellRenderer,
    filter: Filter,
    toolbar: ToolbarDefault,
    showToolbar: true,
    showColumnsWithoutConfig: true, //show all columns which are not explicitly hidden
    paging: 10,
    displayValueGetter: ({value}) => value
};

Ardagryd.defaultProps = {
    config: {},
    columns: {},
    dispatch: () => {}
};

const filterConfig = React.PropTypes.shape({
    columnName: React.PropTypes.string.isRequired,
    expression: React.PropTypes.string.isRequired
});

const sortConfig = React.PropTypes.shape({
    columnName: React.PropTypes.string.isRequired,
    order: React.PropTypes.oneOf([ASCENDING, DESCENDING])
});

Ardagryd.propTypes = {
    objects: PropTypes.arrayOf(PropTypes.object),
    config: PropTypes.object.isRequired,
    columns: PropTypes.objectOf(PropTypes.shape({
      displayValueGetter: PropTypes.func,
      id: PropTypes.bool,
      label: PropTypes.string,
      order: PropTypes.number,
      hideTools: PropTypes.bool,
      sortable: PropTypes.bool,
      cellRenderer: elementType,
      filter: (props, propName, componentName, location, propFullName)=>{
          if (props[propName]){
              return new Error(
                  'Invalid prop `' + propFullName + '` supplied to' +
                  ' `' + componentName + '`. Please use the `filter` prop on the root component.'
              );
          }
      },
      sort: (props, propName, componentName, location, propFullName)=>{
          if (props[propName]){
              return new Error(
                  'Invalid prop `' + propFullName + '` supplied to' +
                  ' `' + componentName + '`. Please use the `sort` prop on the root component.'
              );
          }
      }
    })).isRequired,
    dispatch: PropTypes.func.isRequired,
    sort: PropTypes.oneOfType([
        PropTypes.string,
        sortConfig,
        PropTypes.arrayOf(sortConfig)
    ]),
    filter: PropTypes.oneOfType([
        filterConfig,
        PropTypes.arrayOf(filterConfig)])
};

//Find id-column, or enhance objects with ids
function getOrCreateIdColumn(objects, columns){
    //check if explicit id-column is set in column-config
    const idColumn = Object.keys(columns).find((key)=>{
        if(columns[key].id){
            return true;
        }
    });

    if (idColumn){
        return idColumn;
    } else if (objects.length > 0 && objects[0].id !== undefined) {
        //check if objects have a id-property
        return "id"
    } else {
        //TODO: use some type of hashing
        //generate id-property
        let index = 0;
        objects.map((object) => {
            object.id = index++;
        });
        return "id"
    }
}

export default Ardagryd;
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
var clone = require("clone");
var Tapable = require("tapable");

var Compilation = require("./Compilation");
var Parser = require("./Parser");
var Resolver = require("enhanced-resolve/lib/Resolver");

var NormalModuleFactory = require("./NormalModuleFactory");
var ContextModuleFactory = require("./ContextModuleFactory");
var NullFactory = require("./NullFactory");

var ModuleDependency = require("./dependencies/ModuleDependency");
var SingleEntryDependency = require("./dependencies/SingleEntryDependency");
var CommonJsRequireDependency = require("./dependencies/CommonJsRequireDependency");
var RequireResolveDependency = require("./dependencies/RequireResolveDependency");
var RequireResolveHeaderDependency = require("./dependencies/RequireResolveHeaderDependency");
var RequireEnsureDependency = require("./dependencies/RequireEnsureDependency");
var RequireEnsureItemDependency = require("./dependencies/RequireEnsureItemDependency");

function Watching(compiler, handler, watchDelay) {
	this.startTime = null;
	this.invalid = false;
	this.error = null;
	this.stats = null;
	this.handler = handler;
	this.watchDelay = watchDelay;
	this.compiler = compiler;
	this.running = true;
	this.compiler.readRecords(function(err) {
		if(err) return this._done(err);

		this._go();
	}.bind(this));
}

Watching.prototype._go = function() {
	this.startTime = new Date().getTime();
	this.running = true;
	this.invalid = false;
	this.compiler.applyPluginsAsync("watch-run", this, function(err) {
		this.compiler.compile(function(err, compilation) {
			if(err) return this._done(err);
			if(this.invalid) return this._done();

			if(this.compiler.applyPluginsBailResult("should-emit", compilation) === false) {
				return this._done(null, compilation);
			}

			this.compiler.emitAssets(compilation, function(err) {
				if(err) return this._done(err);
				if(this.invalid) return this._done();

				this.compiler.emitRecords(function(err) {
					if(err) return this._done(err);

					return this._done(null, compilation);
				}.bind(this));
			}.bind(this));
		}.bind(this));
	}.bind(this));
};

Watching.prototype._done = function(err, compilation) {
	this.running = false;
	if(this.invalid) return this._go();
	this.error = err || null;
	this.stats = compilation ? compilation.getStats() : null;
	if(this.stats) {
		this.stats.startTime = this.startTime;
		this.stats.endTime = new Date().getTime();
	}
	if(this.stats)
		this.compiler.applyPlugins("done", this.stats);
	else
		this.compiler.applyPlugins("failed", this.error);
	this.handler(this.error, this.stats);
	if(!this.error)
		this.watch(compilation.fileDependencies, compilation.contextDependencies);
};

Watching.prototype.watch = function(files, dirs) {
	this.watcher = this.compiler.watchFileSystem.watch(files, dirs, this.startTime, this.watchDelay, function(err, filesModified, contextModified, fileTimestamps, contextTimestamps) {
		this.watcher = null;
		if(err) return this.handler(err);

		this.compiler.fileTimestamps = fileTimestamps;
		this.compiler.contextTimestamps = contextTimestamps;
		this.invalidate();
	}.bind(this), function() {
		this.compiler.applyPlugins("invalid");
	}.bind(this));
};

Watching.prototype.invalidate = function() {
	if(this.watcher) {
		this.watcher.close();
		this.watcher = null;
	}
	if(this.running) {
		this.invalid = true;
		return false;
	} else {
		this._go();
	}
};

Watching.prototype.close = function(callback) {
	if(this.watcher) {
		this.watcher.close();
		this.watcher = null;
	}
	if(this.running) {
		this.invalid = true;
		this._done = function() {
			callback();
		};
	} else {
		callback();
	}
};

function Compiler() {
	Tapable.call(this);

	this.outputPath = "";
	this.outputFileSystem = null;
	this.inputFileSystem = null;

	this.recordsInputPath = null;
	this.recordsOutputPath = null;
	this.records = {};

	this.fileTimestamps = {};
	this.contextTimestamps = {};

	this.resolvers = {
		normal: new Resolver(null),
		loader: new Resolver(null),
		context: new Resolver(null)
	};
	this.parser = new Parser();

	this.options = {};
}
module.exports = Compiler;

Compiler.prototype = Object.create(Tapable.prototype);

Compiler.Watching = Watching;
Compiler.prototype.watch = function(watchDelay, handler) {
	this.fileTimestamps = {};
	this.contextTimestamps = {};
	var watching = new Watching(this, handler, watchDelay || 0);
	return watching;
};

Compiler.prototype.run = function(callback) {
	var startTime = new Date().getTime();
	this.applyPluginsAsync("run", this, function(err) {
		if(err) return callback(err);

		this.readRecords(function(err) {
			if(err) return callback(err);

			this.compile(function(err, compilation) {
				if(err) return callback(err);

				if(this.applyPluginsBailResult("should-emit", compilation) === false) {
					callbackStats.call(this);
					return;
				}

				this.emitAssets(compilation, function(err) {
					if(err) return callback(err);

					this.emitRecords(function(err) {
						if(err) return callback(err);

						var stats = compilation.getStats();
						stats.startTime = startTime;
						stats.endTime = new Date().getTime();
						this.applyPlugins("done", stats);
						return callback(null, stats);
					}.bind(this));
				}.bind(this));
			}.bind(this));
		}.bind(this));
	}.bind(this));
};

Compiler.prototype.runAsChild = function(callback) {
	this.compile(function(err, compilation) {
		if(err) return callback(err);

		this.parentCompilation.children.push(compilation);
		Object.keys(compilation.assets).forEach(function(name) {
			this.parentCompilation.assets[name] = compilation.assets[name];
		}.bind(this));

		var entries = compilation.chunks.filter(function(chunk) {
			return chunk.entry;
		});
		return callback(null, entries, compilation);
	}.bind(this));
};

Compiler.prototype.purgeInputFileSystem = function() {
	if(this.inputFileSystem && this.inputFileSystem.purge)
		this.inputFileSystem.purge();
};

Compiler.prototype.emitAssets = function(compilation, callback) {
	var outputPath;

	this.applyPluginsAsync("emit", compilation, function(err) {
		if(err) return callback(err);
		outputPath = compilation.getPath(this.outputPath);
		this.outputFileSystem.mkdirp(outputPath, emitFiles.bind(this));
	}.bind(this));

	function emitFiles(err) {
		if(err) return callback(err);

		require("async").forEach(Object.keys(compilation.assets), function(file, callback) {

			var targetFile = file;
			var queryStringIdx = targetFile.indexOf("?")
			if(queryStringIdx >= 0) {
				targetFile = targetFile.substr(0, queryStringIdx);
			}
			if(targetFile.indexOf("/") >= 0) {
				var idx = targetFile.lastIndexOf("/");
				var dir = targetFile.substr(0, idx);
				this.outputFileSystem.mkdirp(this.outputFileSystem.join(outputPath, dir), writeOut.bind(this));
			} else writeOut.call(this);
			function writeOut(err) {
				if(err) return callback(err);
				var targetPath = this.outputFileSystem.join(outputPath, targetFile);
				var source = compilation.assets[file];
				if(source.existsAt === targetPath) {
					source.emitted = false;
					return callback();
				}
				var content = source.source();
				if(!Buffer.isBuffer(content))
					content = new Buffer(content, "utf-8");
				source.existsAt = targetPath;
				source.emitted = true;
				this.outputFileSystem.writeFile(targetPath, content, callback);
			};

		}.bind(this), function(err) {
			if(err) return callback(err);

			afterEmit.call(this);
		}.bind(this));
	}

	function afterEmit() {
		this.applyPluginsAsync("after-emit", compilation, function(err) {
			if(err) return callback(err);

			return callback();
		});
	}

};

Compiler.prototype.emitRecords = function emitRecords(callback) {
	if(!this.recordsOutputPath) return callback();
	var idx1 = this.recordsOutputPath.lastIndexOf("/");
	var idx2 = this.recordsOutputPath.lastIndexOf("\\");
	var recordsOutputPathDirectory = null;
	if(idx1 > idx2) recordsOutputPathDirectory = this.recordsOutputPath.substr(0, idx1);
	if(idx1 < idx2) recordsOutputPathDirectory = this.recordsOutputPath.substr(0, idx2);
	if(!recordsOutputPathDirectory) return writeFile.call(this);
	this.outputFileSystem.mkdirp(recordsOutputPathDirectory, function(err) {
		if(err) return callback(err);
		writeFile.call(this)
	}.bind(this));

	function writeFile() {
		this.outputFileSystem.writeFile(this.recordsOutputPath, JSON.stringify(this.records, undefined, 2), callback);
	}
};

Compiler.prototype.readRecords = function readRecords(callback) {
	if(!this.recordsInputPath) {
		this.records = {};
		return callback();
	}
	this.inputFileSystem.stat(this.recordsInputPath, function(err) {
		// It doesn't exist
		// We can ignore this.
		if(err) return callback();

		this.inputFileSystem.readFile(this.recordsInputPath, function(err, content) {
			if(err) return callback(err);

			try {
				this.records = JSON.parse(content);
			} catch(e) {
				e.message = "Cannot parse records: " + e.message;
				return callback(e);
			}

			return callback();
		}.bind(this));
	}.bind(this));
};

Compiler.prototype.createChildCompiler = function(compilation, compilerName, outputOptions) {
	var childCompiler = new Compiler();
	for(var name in this._plugins) {
		if(["make", "compile", "emit", "after-emit", "invalid", "done", "this-compilation"].indexOf(name) < 0)
			childCompiler._plugins[name] = this._plugins[name].slice();
	}
	childCompiler.name = compilerName;
	childCompiler.outputPath = this.outputPath;
	childCompiler.inputFileSystem = this.inputFileSystem;
	childCompiler.outputFileSystem = null;
	childCompiler.resolvers = this.resolvers;
	childCompiler.parser = this.parser;
	childCompiler.fileTimestamps = this.fileTimestamps;
	childCompiler.contextTimestamps = this.contextTimestamps;
	if(!this.records[compilerName]) this.records[compilerName] = [];
	this.records[compilerName].push(childCompiler.records = {});
	childCompiler.options = clone(this.options);
	for(var name in outputOptions) {
		childCompiler.options.output[name] = outputOptions[name];
	}
	childCompiler.parentCompilation = compilation;
	return childCompiler;
};

Compiler.prototype.isChild = function() {
	return !!this.parentCompilation;
};

Compiler.prototype.createCompilation = function() {
	return new Compilation(this);
};

Compiler.prototype.newCompilation = function(params) {
	var compilation = this.createCompilation();
	compilation.fileTimestamps = this.fileTimestamps;
	compilation.contextTimestamps = this.contextTimestamps;
	compilation.name = this.name;
	compilation.records = this.records;
	this.applyPlugins("this-compilation", compilation, params);
	this.applyPlugins("compilation", compilation, params);
	return compilation;
};

Compiler.prototype.createNormalModuleFactory = function() {
	var normalModuleFactory = new NormalModuleFactory(this.options.context, this.resolvers, this.parser, this.options.module || {});
	this.applyPlugins("normal-module-factory", normalModuleFactory);
	return normalModuleFactory;
};

Compiler.prototype.createContextModuleFactory = function() {
	var contextModuleFactory = new ContextModuleFactory(this.resolvers, this.inputFileSystem);
	this.applyPlugins("context-module-factory", contextModuleFactory);
	return contextModuleFactory;
};

Compiler.prototype.newCompilationParams = function() {
	var params = {
		normalModuleFactory: this.createNormalModuleFactory(),
		contextModuleFactory: this.createContextModuleFactory()
	};
	return params;
};

Compiler.prototype.compile = function(callback) {
	var params = this.newCompilationParams();
	this.applyPlugins("compile", params);

	var compilation = this.newCompilation(params);

	this.applyPluginsParallel("make", compilation, function(err) {
		if(err) return callback(err);

		compilation.seal(function(err) {
			if(err) return callback(err);

			this.applyPluginsAsync("after-compile", compilation, function(err) {
				if(err) return callback(err);

				return callback(null, compilation);
			});
		}.bind(this));
	}.bind(this));
};
<commit_msg>fixed incorrect commit<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
var clone = require("clone");
var Tapable = require("tapable");

var Compilation = require("./Compilation");
var Parser = require("./Parser");
var Resolver = require("enhanced-resolve/lib/Resolver");

var NormalModuleFactory = require("./NormalModuleFactory");
var ContextModuleFactory = require("./ContextModuleFactory");
var NullFactory = require("./NullFactory");

var ModuleDependency = require("./dependencies/ModuleDependency");
var SingleEntryDependency = require("./dependencies/SingleEntryDependency");
var CommonJsRequireDependency = require("./dependencies/CommonJsRequireDependency");
var RequireResolveDependency = require("./dependencies/RequireResolveDependency");
var RequireResolveHeaderDependency = require("./dependencies/RequireResolveHeaderDependency");
var RequireEnsureDependency = require("./dependencies/RequireEnsureDependency");
var RequireEnsureItemDependency = require("./dependencies/RequireEnsureItemDependency");

function Watching(compiler, handler, watchDelay) {
	this.startTime = null;
	this.invalid = false;
	this.error = null;
	this.stats = null;
	this.handler = handler;
	this.watchDelay = watchDelay;
	this.compiler = compiler;
	this.running = true;
	this.compiler.readRecords(function(err) {
		if(err) return this._done(err);

		this._go();
	}.bind(this));
}

Watching.prototype._go = function() {
	this.startTime = new Date().getTime();
	this.running = true;
	this.invalid = false;
	this.compiler.applyPluginsAsync("watch-run", this, function(err) {
		this.compiler.compile(function(err, compilation) {
			if(err) return this._done(err);
			if(this.invalid) return this._done();

			if(this.compiler.applyPluginsBailResult("should-emit", compilation) === false) {
				return this._done(null, compilation);
			}

			this.compiler.emitAssets(compilation, function(err) {
				if(err) return this._done(err);
				if(this.invalid) return this._done();

				this.compiler.emitRecords(function(err) {
					if(err) return this._done(err);

					return this._done(null, compilation);
				}.bind(this));
			}.bind(this));
		}.bind(this));
	}.bind(this));
};

Watching.prototype._done = function(err, compilation) {
	this.running = false;
	if(this.invalid) return this._go();
	this.error = err || null;
	this.stats = compilation ? compilation.getStats() : null;
	if(this.stats) {
		this.stats.startTime = this.startTime;
		this.stats.endTime = new Date().getTime();
	}
	if(this.stats)
		this.compiler.applyPlugins("done", this.stats);
	else
		this.compiler.applyPlugins("failed", this.error);
	this.handler(this.error, this.stats);
	if(!this.error)
		this.watch(compilation.fileDependencies, compilation.contextDependencies);
};

Watching.prototype.watch = function(files, dirs) {
	this.watcher = this.compiler.watchFileSystem.watch(files, dirs, this.startTime, this.watchDelay, function(err, filesModified, contextModified, fileTimestamps, contextTimestamps) {
		this.watcher = null;
		if(err) return this.handler(err);

		this.compiler.fileTimestamps = fileTimestamps;
		this.compiler.contextTimestamps = contextTimestamps;
		this.invalidate();
	}.bind(this), function() {
		this.compiler.applyPlugins("invalid");
	}.bind(this));
};

Watching.prototype.invalidate = function() {
	if(this.watcher) {
		this.watcher.close();
		this.watcher = null;
	}
	if(this.running) {
		this.invalid = true;
		return false;
	} else {
		this._go();
	}
};

Watching.prototype.close = function(callback) {
	if(this.watcher) {
		this.watcher.close();
		this.watcher = null;
	}
	if(this.running) {
		this.invalid = true;
		this._done = function() {
			callback();
		};
	} else {
		callback();
	}
};

function Compiler() {
	Tapable.call(this);

	this.outputPath = "";
	this.outputFileSystem = null;
	this.inputFileSystem = null;

	this.recordsInputPath = null;
	this.recordsOutputPath = null;
	this.records = {};

	this.fileTimestamps = {};
	this.contextTimestamps = {};

	this.resolvers = {
		normal: new Resolver(null),
		loader: new Resolver(null),
		context: new Resolver(null)
	};
	this.parser = new Parser();

	this.options = {};
}
module.exports = Compiler;

Compiler.prototype = Object.create(Tapable.prototype);

Compiler.Watching = Watching;
Compiler.prototype.watch = function(watchDelay, handler) {
	this.fileTimestamps = {};
	this.contextTimestamps = {};
	var watching = new Watching(this, handler, watchDelay || 0);
	return watching;
};

Compiler.prototype.run = function(callback) {
	var startTime = new Date().getTime();
	this.applyPluginsAsync("run", this, function(err) {
		if(err) return callback(err);

		this.readRecords(function(err) {
			if(err) return callback(err);

			this.compile(function(err, compilation) {
				if(err) return callback(err);

				if(this.applyPluginsBailResult("should-emit", compilation) === false) {
					var stats = compilation.getStats();
					stats.startTime = startTime;
					stats.endTime = new Date().getTime();
					this.applyPlugins("done", stats);
					return callback(null, stats);
				}

				this.emitAssets(compilation, function(err) {
					if(err) return callback(err);

					this.emitRecords(function(err) {
						if(err) return callback(err);

						var stats = compilation.getStats();
						stats.startTime = startTime;
						stats.endTime = new Date().getTime();
						this.applyPlugins("done", stats);
						return callback(null, stats);
					}.bind(this));
				}.bind(this));
			}.bind(this));
		}.bind(this));
	}.bind(this));
};

Compiler.prototype.runAsChild = function(callback) {
	this.compile(function(err, compilation) {
		if(err) return callback(err);

		this.parentCompilation.children.push(compilation);
		Object.keys(compilation.assets).forEach(function(name) {
			this.parentCompilation.assets[name] = compilation.assets[name];
		}.bind(this));

		var entries = compilation.chunks.filter(function(chunk) {
			return chunk.entry;
		});
		return callback(null, entries, compilation);
	}.bind(this));
};

Compiler.prototype.purgeInputFileSystem = function() {
	if(this.inputFileSystem && this.inputFileSystem.purge)
		this.inputFileSystem.purge();
};

Compiler.prototype.emitAssets = function(compilation, callback) {
	var outputPath;

	this.applyPluginsAsync("emit", compilation, function(err) {
		if(err) return callback(err);
		outputPath = compilation.getPath(this.outputPath);
		this.outputFileSystem.mkdirp(outputPath, emitFiles.bind(this));
	}.bind(this));

	function emitFiles(err) {
		if(err) return callback(err);

		require("async").forEach(Object.keys(compilation.assets), function(file, callback) {

			var targetFile = file;
			var queryStringIdx = targetFile.indexOf("?")
			if(queryStringIdx >= 0) {
				targetFile = targetFile.substr(0, queryStringIdx);
			}
			if(targetFile.indexOf("/") >= 0) {
				var idx = targetFile.lastIndexOf("/");
				var dir = targetFile.substr(0, idx);
				this.outputFileSystem.mkdirp(this.outputFileSystem.join(outputPath, dir), writeOut.bind(this));
			} else writeOut.call(this);
			function writeOut(err) {
				if(err) return callback(err);
				var targetPath = this.outputFileSystem.join(outputPath, targetFile);
				var source = compilation.assets[file];
				if(source.existsAt === targetPath) {
					source.emitted = false;
					return callback();
				}
				var content = source.source();
				if(!Buffer.isBuffer(content))
					content = new Buffer(content, "utf-8");
				source.existsAt = targetPath;
				source.emitted = true;
				this.outputFileSystem.writeFile(targetPath, content, callback);
			};

		}.bind(this), function(err) {
			if(err) return callback(err);

			afterEmit.call(this);
		}.bind(this));
	}

	function afterEmit() {
		this.applyPluginsAsync("after-emit", compilation, function(err) {
			if(err) return callback(err);

			return callback();
		});
	}

};

Compiler.prototype.emitRecords = function emitRecords(callback) {
	if(!this.recordsOutputPath) return callback();
	var idx1 = this.recordsOutputPath.lastIndexOf("/");
	var idx2 = this.recordsOutputPath.lastIndexOf("\\");
	var recordsOutputPathDirectory = null;
	if(idx1 > idx2) recordsOutputPathDirectory = this.recordsOutputPath.substr(0, idx1);
	if(idx1 < idx2) recordsOutputPathDirectory = this.recordsOutputPath.substr(0, idx2);
	if(!recordsOutputPathDirectory) return writeFile.call(this);
	this.outputFileSystem.mkdirp(recordsOutputPathDirectory, function(err) {
		if(err) return callback(err);
		writeFile.call(this)
	}.bind(this));

	function writeFile() {
		this.outputFileSystem.writeFile(this.recordsOutputPath, JSON.stringify(this.records, undefined, 2), callback);
	}
};

Compiler.prototype.readRecords = function readRecords(callback) {
	if(!this.recordsInputPath) {
		this.records = {};
		return callback();
	}
	this.inputFileSystem.stat(this.recordsInputPath, function(err) {
		// It doesn't exist
		// We can ignore this.
		if(err) return callback();

		this.inputFileSystem.readFile(this.recordsInputPath, function(err, content) {
			if(err) return callback(err);

			try {
				this.records = JSON.parse(content);
			} catch(e) {
				e.message = "Cannot parse records: " + e.message;
				return callback(e);
			}

			return callback();
		}.bind(this));
	}.bind(this));
};

Compiler.prototype.createChildCompiler = function(compilation, compilerName, outputOptions) {
	var childCompiler = new Compiler();
	for(var name in this._plugins) {
		if(["make", "compile", "emit", "after-emit", "invalid", "done", "this-compilation"].indexOf(name) < 0)
			childCompiler._plugins[name] = this._plugins[name].slice();
	}
	childCompiler.name = compilerName;
	childCompiler.outputPath = this.outputPath;
	childCompiler.inputFileSystem = this.inputFileSystem;
	childCompiler.outputFileSystem = null;
	childCompiler.resolvers = this.resolvers;
	childCompiler.parser = this.parser;
	childCompiler.fileTimestamps = this.fileTimestamps;
	childCompiler.contextTimestamps = this.contextTimestamps;
	if(!this.records[compilerName]) this.records[compilerName] = [];
	this.records[compilerName].push(childCompiler.records = {});
	childCompiler.options = clone(this.options);
	for(var name in outputOptions) {
		childCompiler.options.output[name] = outputOptions[name];
	}
	childCompiler.parentCompilation = compilation;
	return childCompiler;
};

Compiler.prototype.isChild = function() {
	return !!this.parentCompilation;
};

Compiler.prototype.createCompilation = function() {
	return new Compilation(this);
};

Compiler.prototype.newCompilation = function(params) {
	var compilation = this.createCompilation();
	compilation.fileTimestamps = this.fileTimestamps;
	compilation.contextTimestamps = this.contextTimestamps;
	compilation.name = this.name;
	compilation.records = this.records;
	this.applyPlugins("this-compilation", compilation, params);
	this.applyPlugins("compilation", compilation, params);
	return compilation;
};

Compiler.prototype.createNormalModuleFactory = function() {
	var normalModuleFactory = new NormalModuleFactory(this.options.context, this.resolvers, this.parser, this.options.module || {});
	this.applyPlugins("normal-module-factory", normalModuleFactory);
	return normalModuleFactory;
};

Compiler.prototype.createContextModuleFactory = function() {
	var contextModuleFactory = new ContextModuleFactory(this.resolvers, this.inputFileSystem);
	this.applyPlugins("context-module-factory", contextModuleFactory);
	return contextModuleFactory;
};

Compiler.prototype.newCompilationParams = function() {
	var params = {
		normalModuleFactory: this.createNormalModuleFactory(),
		contextModuleFactory: this.createContextModuleFactory()
	};
	return params;
};

Compiler.prototype.compile = function(callback) {
	var params = this.newCompilationParams();
	this.applyPlugins("compile", params);

	var compilation = this.newCompilation(params);

	this.applyPluginsParallel("make", compilation, function(err) {
		if(err) return callback(err);

		compilation.seal(function(err) {
			if(err) return callback(err);

			this.applyPluginsAsync("after-compile", compilation, function(err) {
				if(err) return callback(err);

				return callback(null, compilation);
			});
		}.bind(this));
	}.bind(this));
};
<|endoftext|>