<commit_before>import BIDS from './obj'
import utils from '../../utils'
const Issue = utils.issues.Issue
import tsv from '../tsv'
import json from '../json'
import NIFTI from '../nifti'
import bval from '../bval'
import bvec from '../bvec'
import Events from '../events'
import { session } from '../session'
import checkAnyDataPresent from '../checkAnyDataPresent'
import headerFields from '../headerFields'
import subSesMismatchTest from './subSesMismatchTest'
import groupFileTypes from './groupFileTypes'
import subjects from './subjects'
import checkDatasetDescription from './checkDatasetDescription'
import checkReadme from './checkReadme'
import validateMisc from '../../utils/files/validateMisc'
import collectSubjectMetadata from '../../utils/summary/collectSubjectMetadata'
import collectPetFields from '../../utils/summary/collectPetFields'

/**
 * Full Test
 *
 * Takes on an array of files, callback, and boolean inidicating if git-annex is used.
 * Starts the validation process for a BIDS package.
 */
const fullTest = (fileList, options, annexed, dir, callback) => {
  const self = BIDS
  self.options = options

  const jsonContentsDict = {}
  const bContentsDict = {}
  const events = []
  const stimuli = {
    events: [],
    directory: [],
  }
  const jsonFiles = []
  const headers = []
  const participants = null
  const phenotypeParticipants = []

  const tsvs = []

  const summary = utils.collectSummary(fileList, self.options)

  // remove size redundancies
  for (const key in fileList) {
    if (fileList.hasOwnProperty(key)) {
      const file = fileList[key]
      if (typeof file.stats === 'object' && file.stats.hasOwnProperty('size'))
        delete file.size
    }
  }

  // remove ignored files from list:
  Object.keys(fileList).forEach(function(key) {
    if (fileList[key].ignore) {
      delete fileList[key]
    }
  })

  self.issues = self.issues.concat(subSesMismatchTest(fileList))

  // check for illegal character in task name and acq name
  self.issues = self.issues.concat(utils.files.illegalCharacterTest(fileList))

  const files = groupFileTypes(fileList, self.options)

  // generate issues for all files that do not comply with
  // bids spec
  files.invalid.map(function(file) {
    self.issues.push(
      new Issue({
        file: file,
        evidence: file.name,
        code: 1,
      }),
    )
  })

  // check if dataset contains T1w
  if (summary.dataTypes.includes('T1w')) {
    self.issues.push(
      new Issue({
        code: 53,
      }),
    )
  }

  validateMisc(files.misc)
    .then(miscIssues => {
      self.issues = self.issues.concat(miscIssues)

      // TSV validation
      return tsv.validate(
        files.tsv,
        fileList,
        tsvs,
        events,
        participants,
        phenotypeParticipants,
        stimuli,
      )
    })
    .then(({ tsvIssues, participantsTsvContent }) => {
      self.issues = self.issues.concat(tsvIssues)

      // extract metadata on participants to metadata.age and
      // return metadata on each subject from participants.tsv
      summary.subjectMetadata = collectSubjectMetadata(participantsTsvContent)
      // Bvec validation
      return bvec.validate(files.bvec, bContentsDict)
    })
    .then(bvecIssues => {
      self.issues = self.issues.concat(bvecIssues)

      // Bval validation
      return bval.validate(files.bval, bContentsDict)
    })
    .then(bvalIssues => {
      self.issues = self.issues.concat(bvalIssues)

      // Load json files and construct a contents object with field, value pairs
      return json.load(files.json, jsonFiles, jsonContentsDict)
    })
    .then(jsonLoadIssues => {
      self.issues = self.issues.concat(jsonLoadIssues)

      // Check for at least one subject
      const noSubjectIssues = subjects.atLeastOneSubject(fileList)
      self.issues = self.issues.concat(noSubjectIssues)

      // Check for datasetDescription file in the proper place
      const datasetDescriptionIssues = checkDatasetDescription(jsonContentsDict)
      self.issues = self.issues.concat(datasetDescriptionIssues)

      // Check for README file in the proper place
      const readmeIssues = checkReadme(fileList)
      self.issues = self.issues.concat(readmeIssues)

      // Validate json files and contents
      return json.validate(jsonFiles, fileList, jsonContentsDict, summary)
    })
    .then(jsonIssues => {
      self.issues = self.issues.concat(jsonIssues)
      // Nifti validation
      return NIFTI.validate(
        files.nifti,
        fileList,
        self.options,
        jsonContentsDict,
        bContentsDict,
        events,
        headers,
        annexed,
        dir,
      )
    })
    .then(niftiIssues => {
      self.issues = self.issues.concat(niftiIssues)

      // Issues related to participants not listed in the subjects list
      const participantsInSubjectsIssues = subjects.participantsInSubjects(
        participants,
        summary.subjects,
      )
      self.issues = self.issues.concat(participantsInSubjectsIssues)

      // Check for equal number of participants from ./phenotype/*.tsv and participants in dataset
      const phenotypeIssues = tsv.checkPhenotype(phenotypeParticipants, summary)
      self.issues = self.issues.concat(phenotypeIssues)

      // Validate nii header fields
      self.issues = self.issues.concat(headerFields(headers))

      // Events validation
      stimuli.directory = files.stimuli
      return Events.validateEvents(
        events,
        stimuli,
        headers,
        jsonContentsDict,
        jsonFiles,
        dir,
      )
    })
    .then(eventsIssues => {
      self.issues = self.issues.concat(eventsIssues)

      // Validate custom fields in all TSVs and add any issues to the list
      self.issues = self.issues.concat(
        tsv.validateTsvColumns(tsvs, jsonContentsDict, headers),
      )
      // Validate continous recording files
      self.issues = self.issues.concat(
        tsv.validateContRec(files.contRecord, jsonContentsDict),
      )

      if (!options.ignoreSubjectConsistency) {
        // Validate session files
        self.issues = self.issues.concat(session(fileList))
      }

      // Determine if each subject has data present
      self.issues = self.issues.concat(
        checkAnyDataPresent(fileList, summary.subjects),
      )

      // Group summary modalities
      summary.modalities = utils.modalities.group(summary.modalities)

      // collect PET specific fields
      if (summary.modalities.includes('PET'))
        summary.pet = collectPetFields(jsonContentsDict)

      // Format issues
      const issues = utils.issues.format(self.issues, summary, self.options)
      callback(issues, summary)
    })
    .catch(err => {
      // take internal exceptions and push to issues
      // note: exceptions caught here may have skipped subsequent validations
      const issues = utils.issues.exceptionHandler(
        err,
        self.issues,
        summary,
        self.options,
      )
      callback(issues, summary)
    })
}

export default fullTest
<commit_msg>Update bids-validator/validators/bids/fullTest.js<commit_after>import BIDS from './obj'
import utils from '../../utils'
const Issue = utils.issues.Issue
import tsv from '../tsv'
import json from '../json'
import NIFTI from '../nifti'
import bval from '../bval'
import bvec from '../bvec'
import Events from '../events'
import { session } from '../session'
import checkAnyDataPresent from '../checkAnyDataPresent'
import headerFields from '../headerFields'
import subSesMismatchTest from './subSesMismatchTest'
import groupFileTypes from './groupFileTypes'
import subjects from './subjects'
import checkDatasetDescription from './checkDatasetDescription'
import checkReadme from './checkReadme'
import validateMisc from '../../utils/files/validateMisc'
import collectSubjectMetadata from '../../utils/summary/collectSubjectMetadata'
import collectPetFields from '../../utils/summary/collectPetFields'

/**
 * Full Test
 *
 * Takes on an array of files, callback, and boolean inidicating if git-annex is used.
 * Starts the validation process for a BIDS package.
 */
const fullTest = (fileList, options, annexed, dir, callback) => {
  const self = BIDS
  self.options = options

  const jsonContentsDict = {}
  const bContentsDict = {}
  const events = []
  const stimuli = {
    events: [],
    directory: [],
  }
  const jsonFiles = []
  const headers = []
  const participants = null
  const phenotypeParticipants = []

  const tsvs = []

  const summary = utils.collectSummary(fileList, self.options)

  // remove size redundancies
  for (const key in fileList) {
    if (fileList.hasOwnProperty(key)) {
      const file = fileList[key]
      if (typeof file.stats === 'object' && file.stats.hasOwnProperty('size'))
        delete file.size
    }
  }

  // remove ignored files from list:
  Object.keys(fileList).forEach(function(key) {
    if (fileList[key].ignore) {
      delete fileList[key]
    }
  })

  self.issues = self.issues.concat(subSesMismatchTest(fileList))

  // check for illegal character in task name and acq name
  self.issues = self.issues.concat(utils.files.illegalCharacterTest(fileList))

  const files = groupFileTypes(fileList, self.options)

  // generate issues for all files that do not comply with
  // bids spec
  files.invalid.map(function(file) {
    self.issues.push(
      new Issue({
        file: file,
        evidence: file.name,
        code: 1,
      }),
    )
  })

  // check if dataset contains T1w
  if (!summary.dataTypes.includes('T1w')) {
    self.issues.push(
      new Issue({
        code: 53,
      }),
    )
  }

  validateMisc(files.misc)
    .then(miscIssues => {
      self.issues = self.issues.concat(miscIssues)

      // TSV validation
      return tsv.validate(
        files.tsv,
        fileList,
        tsvs,
        events,
        participants,
        phenotypeParticipants,
        stimuli,
      )
    })
    .then(({ tsvIssues, participantsTsvContent }) => {
      self.issues = self.issues.concat(tsvIssues)

      // extract metadata on participants to metadata.age and
      // return metadata on each subject from participants.tsv
      summary.subjectMetadata = collectSubjectMetadata(participantsTsvContent)
      // Bvec validation
      return bvec.validate(files.bvec, bContentsDict)
    })
    .then(bvecIssues => {
      self.issues = self.issues.concat(bvecIssues)

      // Bval validation
      return bval.validate(files.bval, bContentsDict)
    })
    .then(bvalIssues => {
      self.issues = self.issues.concat(bvalIssues)

      // Load json files and construct a contents object with field, value pairs
      return json.load(files.json, jsonFiles, jsonContentsDict)
    })
    .then(jsonLoadIssues => {
      self.issues = self.issues.concat(jsonLoadIssues)

      // Check for at least one subject
      const noSubjectIssues = subjects.atLeastOneSubject(fileList)
      self.issues = self.issues.concat(noSubjectIssues)

      // Check for datasetDescription file in the proper place
      const datasetDescriptionIssues = checkDatasetDescription(jsonContentsDict)
      self.issues = self.issues.concat(datasetDescriptionIssues)

      // Check for README file in the proper place
      const readmeIssues = checkReadme(fileList)
      self.issues = self.issues.concat(readmeIssues)

      // Validate json files and contents
      return json.validate(jsonFiles, fileList, jsonContentsDict, summary)
    })
    .then(jsonIssues => {
      self.issues = self.issues.concat(jsonIssues)
      // Nifti validation
      return NIFTI.validate(
        files.nifti,
        fileList,
        self.options,
        jsonContentsDict,
        bContentsDict,
        events,
        headers,
        annexed,
        dir,
      )
    })
    .then(niftiIssues => {
      self.issues = self.issues.concat(niftiIssues)

      // Issues related to participants not listed in the subjects list
      const participantsInSubjectsIssues = subjects.participantsInSubjects(
        participants,
        summary.subjects,
      )
      self.issues = self.issues.concat(participantsInSubjectsIssues)

      // Check for equal number of participants from ./phenotype/*.tsv and participants in dataset
      const phenotypeIssues = tsv.checkPhenotype(phenotypeParticipants, summary)
      self.issues = self.issues.concat(phenotypeIssues)

      // Validate nii header fields
      self.issues = self.issues.concat(headerFields(headers))

      // Events validation
      stimuli.directory = files.stimuli
      return Events.validateEvents(
        events,
        stimuli,
        headers,
        jsonContentsDict,
        jsonFiles,
        dir,
      )
    })
    .then(eventsIssues => {
      self.issues = self.issues.concat(eventsIssues)

      // Validate custom fields in all TSVs and add any issues to the list
      self.issues = self.issues.concat(
        tsv.validateTsvColumns(tsvs, jsonContentsDict, headers),
      )
      // Validate continous recording files
      self.issues = self.issues.concat(
        tsv.validateContRec(files.contRecord, jsonContentsDict),
      )

      if (!options.ignoreSubjectConsistency) {
        // Validate session files
        self.issues = self.issues.concat(session(fileList))
      }

      // Determine if each subject has data present
      self.issues = self.issues.concat(
        checkAnyDataPresent(fileList, summary.subjects),
      )

      // Group summary modalities
      summary.modalities = utils.modalities.group(summary.modalities)

      // collect PET specific fields
      if (summary.modalities.includes('PET'))
        summary.pet = collectPetFields(jsonContentsDict)

      // Format issues
      const issues = utils.issues.format(self.issues, summary, self.options)
      callback(issues, summary)
    })
    .catch(err => {
      // take internal exceptions and push to issues
      // note: exceptions caught here may have skipped subsequent validations
      const issues = utils.issues.exceptionHandler(
        err,
        self.issues,
        summary,
        self.options,
      )
      callback(issues, summary)
    })
}

export default fullTest
<|endoftext|>


<commit_before>import {dragElement} from '../../util.js';
import {getCatalogEntries} from '../../services.js';
import {CatalogItem} from './catalogItem.js';

/** A Component that builds the CatalogMenu. Can be added to html using <catalog-menu></catalog-menu>
 *
 * Includes three different columns for data related to fires, fuel moisture, and satellite data.
 * Can be moved around by clicking the title bar, can be closed by clicking x in top right corner, and
 * supports searching columns for data that matches a description. */
export class CatalogMenu extends HTMLElement {
    constructor() {
        super();
        // Arrays of catalog entries based on their descriptions.
        this.firesList = [];
        this.fuelMoistureList = [];
        this.satelliteList = [];
        this.addOrder = [];
        this.innerHTML = `

Select Simulation...

                        x

                        order/search by
                        <select id="sort-by" class="selector">
                            <option value="original-order">original order</option>
                            <option value="description">description</option>
                            <option value="start-date">start date</option>
                            <option value="end-date">end date</option>
                        </select>

                        Reverse Order
                        <input type="checkbox" id="reverse-order" style="display:inline-block"></input>

                        <input id="search-for" class="menu-search" type="text"></input>

                    <select id="mobile-selector">
                        <option value="Fires">Fires</option>
                        <option value="Fuel Moisture">Fuel Moisture</option>
                        <option value="Satellite Data">Satellite Data</option>
                    </select>

Fires

Fuel moisture

Satellite Data

        `;
    }

    /** This function is called once the HTML Element has been added to the DOM. Add all the UI events and change some
     * presentation details based on whether on mobile. */
    connectedCallback() {
        const catalogMenu = this.querySelector('.catalog-menu');
        const clientWidth = document.body.clientWidth;
        const sortBy = this.querySelector('#sort-by');
        const reverseOrder = this.querySelector('#reverse-order');
        const reverseLabel = this.querySelector('#reverse-label');
        const menuSearch = this.querySelector('#search-for');
        const menuSelect = this.querySelector('#mobile-selector');
        // change labels, sizes and positions based on screen size
        reverseLabel.innerText = (clientWidth < 769) ? "Reverse" : "Reverse Order";
        catalogMenu.style.right = ((clientWidth - catalogMenu.clientWidth)/ 2) + "px";
        var searchDescription = (clientWidth < 769) ? "Search..." : "Search for Simulation...";
        // Makes sure that map events like zooming and panning are disabled from within menu div
        L.DomEvent.disableClickPropagation(catalogMenu);
        // Closes the menu when the x is clicked
        this.querySelector('#menu-close').onclick = () => {
            catalogMenu.style.display = 'none';
        }
        // Implements repositioning menu
        dragElement(catalogMenu, "menu-title");
        menuSearch.placeholder = searchDescription;
        menuSearch.onpointerdown = (e) => {
            e.stopPropagation();
        }
        // Sets up search functionality
        menuSearch.oninput = () => {
            this.searchCatalog(menuSearch.value.toLowerCase(), sortBy.value);
        }
        sortBy.onchange = () => {
            this.sortBy(sortBy.value, reverseOrder.checked);
        }
        reverseOrder.onclick = () => {
            this.sortBy(sortBy.value, reverseOrder.checked);
        }
        menuSelect.onchange = () => {
            this.selectCategory(menuSelect.value);
        }
        this.buildMenu();
    }

    /** Function that retrieves catalog Entries from services.js and builds a CatalogItem for each and adds it to the
     * appropriate category in the menu. */
    async buildMenu() {
        const urlParams = new URLSearchParams(window.location.search);
        const navJobId = urlParams.get('job_id');
        const firesListDOM = this.querySelector('#catalog-fires');
        const fuelMoistureListDOM = this.querySelector('#catalog-fuel-moisture');
        const satelliteListDOM = this.querySelector('#catalog-satellite-data');
        // build html for list item for each catalog entry and add it to the proper list depending on its description
        const catalogEntries = await getCatalogEntries();
        for (const [catName, catEntry] of Object.entries(catalogEntries)) {
            this.addOrder.push(catEntry.job_id);
            let desc = catEntry.description;
            var newLI = new CatalogItem(catEntry, navJobId);
            if(desc.indexOf('GACC') >= 0) {
                this.fuelMoistureList.push(catEntry);
                fuelMoistureListDOM.appendChild(newLI);
            } else if(desc.indexOf('SAT') >= 0) {
                this.satelliteList.push(catEntry);
                satelliteListDOM.appendChild(newLI);
            } else {
                this.firesList.push(catEntry);
                firesListDOM.appendChild(newLI);
            }
        }
    }

    /** Called each time a character is entered into the search input. filters the stored array of catalog entries by search text */
    searchCatalog(searchText, sortBy) {
        const filterFunction = (catalogEntry) => {
            if (sortBy == 'original-order' || sortBy == 'description') {
                return catalogEntry.description.toLowerCase().includes(searchText);
            }
            if (sortBy.includes('start-date')) {
                return catalogEntry.from_utc.toLowerCase().includes(searchText);
            }
            if (sortBy.includes('end-date')) {
                return catalogEntry.to_utc.toLowerCase().includes(searchText);
            }
        }
        const createList = (list) => list.filter(filterFunction);
        this.filterColumns(createList);
    }

    /** Function that sorts the lists in the menu. Takes @sortBy the selected category to sortBy and @reverseOrder indicating if
     * the order should be reversed. */
    sortBy(sortBy, reverseOrder) {
        const catalogSearch = this.querySelector('#search-for');
        catalogSearch.value = "";
        const sortingFunction = (listElem1, listElem2) => {
            let result = false;
            if (sortBy == "original-order") result = this.addOrder.indexOf(listElem1.job_id) > this.addOrder.indexOf(listElem2.job_id);
            if (sortBy == "description") result = listElem1.description > listElem2.description;
            if (sortBy == "start-date") result = listElem1.from_utc > listElem2.from_utc;
            if (sortBy == "end-date") result = listElem1.to_utc > listElem2.to_utc;
            if (reverseOrder) result = !result;
            return result ? 1 : -1;
        }
        const createList = (list) => list.sort(sortingFunction);
        this.filterColumns(createList);
    }

    /** Clears each catalog column on the DOM, filters the stored array of catalog entries by
     * provided function. Builds
 html for filtered catalog entries and adds them to the columns */
    filterColumns(createList) {
        const firesListDOM = this.querySelector('#catalog-fires');
        const fuelMoistureListDOM = this.querySelector('#catalog-fuel-moisture');
        const satelliteListDOM = this.querySelector('#catalog-satellite-data');
        let catalogColumns = [[firesListDOM, this.firesList], [fuelMoistureListDOM, this.fuelMoistureList], [satelliteListDOM, this.satelliteList]];
        for (const [listDOM, list] of catalogColumns) {
            listDOM.innerHTML = '';
            let newList = createList(list);
            for (var catalogEntry of newList) {
                let newLI = new CatalogItem(catalogEntry, null);
                listDOM.append(newLI);
            }
        }
    }

    /** Function used only in mobile versions. Mobile shows only one column at a time and this function is called when a user switches between columns.
     * Hides all columns and then shows the selected column. */
    selectCategory(selection) {
        const firesListDOM = this.querySelector('#fires-column');
        firesListDOM.style.display = "none";
        const fuelMoistureListDOM = this.querySelector('#fuel-moisture-column');
        fuelMoistureListDOM.style.display = "none";
        const satelliteListDOM = this.querySelector('#satellite-column');
        satelliteListDOM.style.display = "none";
        if (selection == 'Fires') {
            firesListDOM.style.display = 'block';
        } else if (selection == 'Fuel Moisture') {
            fuelMoistureListDOM.style.display = 'block';
        } else {
            satelliteListDOM.style.display = 'block';
        }
    }
}

window.customElements.define('catalog-menu', CatalogMenu);<commit_msg>Refactors catalogMenu<commit_after>import {dragElement} from '../../util.js';
import {getCatalogEntries} from '../../services.js';
import {CatalogItem} from './catalogItem.js';

/** A Component that builds the CatalogMenu. Can be added to html using <catalog-menu></catalog-menu>
 *
 * Includes three different columns for data related to fires, fuel moisture, and satellite data.
 * Can be moved around by clicking the title bar, can be closed by clicking x in top right corner, and
 * supports searching columns for data that matches a description. */
export class CatalogMenu extends HTMLElement {
    constructor() {
        super();
        // Arrays of catalog entries based on their descriptions.
        this.firesList = [];
        this.fuelMoistureList = [];
        this.satelliteList = [];
        this.addOrder = [];
        this.innerHTML = `

Select Simulation...

                        x

                        order/search by
                        <select id="sort-by" class="selector">
                            <option value="original-order">original order</option>
                            <option value="description">description</option>
                            <option value="start-date">start date</option>
                            <option value="end-date">end date</option>
                        </select>

                        Reverse Order
                        <input type="checkbox" id="reverse-order" style="display:inline-block"></input>

                        <input id="search-for" class="menu-search" type="text"></input>

                    <select id="mobile-selector">
                        <option value="Fires">Fires</option>
                        <option value="Fuel Moisture">Fuel Moisture</option>
                        <option value="Satellite Data">Satellite Data</option>
                    </select>

Fires

Fuel moisture

Satellite Data

        `;
    }

    /** This function is called once the HTML Element has been added to the DOM. Add all the UI events and change some
     * presentation details based on whether on mobile. */
    connectedCallback() {
        const catalogMenu = this.querySelector('.catalog-menu');
        const clientWidth = document.body.clientWidth;
        const sortBy = this.querySelector('#sort-by');
        const reverseOrder = this.querySelector('#reverse-order');
        const reverseLabel = this.querySelector('#reverse-label');
        const menuSearch = this.querySelector('#search-for');
        const menuSelect = this.querySelector('#mobile-selector');
        // change labels, sizes and positions based on screen size
        reverseLabel.innerText = (clientWidth < 769) ? "Reverse" : "Reverse Order";
        catalogMenu.style.right = ((clientWidth - catalogMenu.clientWidth)/ 2) + "px";
        var searchDescription = (clientWidth < 769) ? "Search..." : "Search for Simulation...";
        // Makes sure that map events like zooming and panning are disabled from within menu div
        L.DomEvent.disableClickPropagation(catalogMenu);
        // Closes the menu when the x is clicked
        this.querySelector('#menu-close').onclick = () => {
            catalogMenu.style.display = 'none';
        }
        // Implements repositioning menu
        dragElement(catalogMenu, "menu-title");
        menuSearch.placeholder = searchDescription;
        menuSearch.onpointerdown = (e) => {
            e.stopPropagation();
        }
        // Sets up search functionality
        menuSearch.oninput = () => {
            this.searchCatalog(menuSearch.value.toLowerCase(), sortBy.value);
        }
        sortBy.onchange = () => {
            this.sortBy(sortBy.value, reverseOrder.checked);
        }
        reverseOrder.onclick = () => {
            this.sortBy(sortBy.value, reverseOrder.checked);
        }
        menuSelect.onchange = () => {
            this.selectCategory(menuSelect.value);
        }
        this.buildMenu();
    }

    /** Function that retrieves catalog Entries from services.js and builds a CatalogItem for each and adds it to the
     * appropriate category in the menu. */
    async buildMenu() {
        const urlParams = new URLSearchParams(window.location.search);
        const navJobId = urlParams.get('job_id');
        const firesListDOM = this.querySelector('#catalog-fires');
        const fuelMoistureListDOM = this.querySelector('#catalog-fuel-moisture');
        const satelliteListDOM = this.querySelector('#catalog-satellite-data');
        // build html for list item for each catalog entry and add it to the proper list depending on its description
        const catalogEntries = await getCatalogEntries();
        for (const [catName, catEntry] of Object.entries(catalogEntries)) {
            this.addOrder.push(catEntry.job_id);
            let desc = catEntry.description;
            var newLI = new CatalogItem(catEntry, navJobId);
            if(desc.indexOf('GACC') >= 0) {
                this.fuelMoistureList.push(catEntry);
                fuelMoistureListDOM.appendChild(newLI);
            } else if(desc.indexOf('SAT') >= 0) {
                this.satelliteList.push(catEntry);
                satelliteListDOM.appendChild(newLI);
            } else {
                this.firesList.push(catEntry);
                firesListDOM.appendChild(newLI);
            }
        }
    }

    /** Called each time a character is entered into the search input. filters the stored array of catalog entries by search text */
    searchCatalog(searchText, sortBy) {
        function filterFunction(catalogEntry) {
            if (sortBy == 'original-order' || sortBy == 'description') {
                return catalogEntry.description.toLowerCase().includes(searchText);
            }
            if (sortBy.includes('start-date')) {
                return catalogEntry.from_utc.toLowerCase().includes(searchText);
            }
            if (sortBy.includes('end-date')) {
                return catalogEntry.to_utc.toLowerCase().includes(searchText);
            }
        }
        function createList(list) {
            return list.filter(filterFunction);
        }
        this.filterColumns(createList);
    }

    /** Function that sorts the lists in the menu. Takes @sortBy the selected category to sortBy and @reverseOrder indicating if
     * the order should be reversed. */
    sortBy(sortBy, reverseOrder) {
        const catalogSearch = this.querySelector('#search-for');
        catalogSearch.value = "";
        const sortingFunction = (listElem1, listElem2) => {
            let result = false;
            if (sortBy == "original-order") {
                result = this.addOrder.indexOf(listElem1.job_id) > this.addOrder.indexOf(listElem2.job_id);
            }
            if (sortBy == "description") {
                result = listElem1.description > listElem2.description;
            }
            if (sortBy == "start-date") {
                result = listElem1.from_utc > listElem2.from_utc;
            }
            if (sortBy == "end-date") {
                result = listElem1.to_utc > listElem2.to_utc;
            }
            if (reverseOrder) {
                result = !result;
            }
            return result ? 1 : -1;
        }
        function createList(list) {
            return list.sort(sortingFunction);
        }
        this.filterColumns(createList);
    }

    /** Clears each catalog column on the DOM, filters the stored array of catalog entries by
     * provided function. Builds
 html for filtered catalog entries and adds them to the columns */
    filterColumns(listCreator) {
        const firesListDOM = this.querySelector('#catalog-fires');
        const fuelMoistureListDOM = this.querySelector('#catalog-fuel-moisture');
        const satelliteListDOM = this.querySelector('#catalog-satellite-data');
        let catalogColumns = [[firesListDOM, this.firesList], [fuelMoistureListDOM, this.fuelMoistureList], [satelliteListDOM, this.satelliteList]];
        for (var [listDOM, list] of catalogColumns) {
            listDOM.innerHTML = '';
            var newList = listCreator(list);
            for (var catalogEntry of newList) {
                var newLI = new CatalogItem(catalogEntry, null);
                listDOM.append(newLI);
            }
        }
    }

    /** Function used only in mobile versions. Mobile shows only one column at a time and this function is called when a user switches between columns.
     * Hides all columns and then shows the selected column. */
    selectCategory(selection) {
        const firesListDOM = this.querySelector('#fires-column');
        firesListDOM.style.display = "none";
        const fuelMoistureListDOM = this.querySelector('#fuel-moisture-column');
        fuelMoistureListDOM.style.display = "none";
        const satelliteListDOM = this.querySelector('#satellite-column');
        satelliteListDOM.style.display = "none";
        if (selection == 'Fires') {
            firesListDOM.style.display = 'block';
        } else if (selection == 'Fuel Moisture') {
            fuelMoistureListDOM.style.display = 'block';
        } else {
            satelliteListDOM.style.display = 'block';
        }
    }
}

window.customElements.define('catalog-menu', CatalogMenu);<|endoftext|>


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

  angular
    .module('studyBoxFe')
    .config(routerConfig);

  /** @ngInject */
  function routerConfig($stateProvider, $urlRouterProvider) {
    $stateProvider
      .state('home', {
        url: '/',
        templateUrl: 'app/login/landing.html',
        controller: 'LandingController',
        controllerAs: 'landing'
      })
      .state('addCard', {
        url: '/addCard',
        controller: 'AddCardController',
        controllerAs: 'AddCardController',
        templateUrl: 'app/addCard/addCard.html'
      })
      .state('addCard', {
        url: '/addCard',
        controller: 'AddCardController',
        controllerAs: 'AddCardController',
        templateUrl: 'app/addCard/addCard.html'
      })
      .state('decks', {
        url: '/decks',
        templateUrl: 'app/decks/decks.html',
        controller: 'DecksController',
        controllerAs: 'decks'
      })
      .state('deck', {
        url: '/deck/:id',
        templateUrl: 'app/decks/deck.html',
        controller: 'DeckController',
        controllerAs: 'deck'
      })
      .state('registration', {
        url: '/registration',
        templateUrl: 'app/registration/registration.html',
        controller: 'RegistrationController',
        controllerAs: 'registration'
      })
      .state('login', {
        url: '/login',
        templateUrl: 'app/login/login.html',
        controller: 'LoginController',
        controllerAs: 'login'
      });

    $urlRouterProvider.otherwise('/');
  }

})();
<commit_msg>Usunięto duplikujące się routy<commit_after>(function() {
  'use strict';

  angular
    .module('studyBoxFe')
    .config(routerConfig);

  /** @ngInject */
  function routerConfig($stateProvider, $urlRouterProvider) {
    $stateProvider
      .state('home', {
        url: '/',
        templateUrl: 'app/login/landing.html',
        controller: 'LandingController',
        controllerAs: 'landing'
      })
      .state('addCard', {
        url: '/addCard',
        controller: 'AddCardController',
        controllerAs: 'AddCardController',
        templateUrl: 'app/addCard/addCard.html'
      })
      .state('decks', {
        url: '/decks',
        templateUrl: 'app/decks/decks.html',
        controller: 'DecksController',
        controllerAs: 'decks'
      })
      .state('deck', {
        url: '/deck/:id',
        templateUrl: 'app/decks/deck.html',
        controller: 'DeckController',
        controllerAs: 'deck'
      })
      .state('registration', {
        url: '/registration',
        templateUrl: 'app/registration/registration.html',
        controller: 'RegistrationController',
        controllerAs: 'registration'
      })
      .state('login', {
        url: '/login',
        templateUrl: 'app/login/login.html',
        controller: 'LoginController',
        controllerAs: 'login'
      });

    $urlRouterProvider.otherwise('/');
  }

})();
<|endoftext|>


<commit_before>/**
 * Created by daniel.irwin on 6/17/17.
 */
module.exports = (function() {

    let server = null;

    return {
        start : function (port, opts) {
            const http = require('http');

            let dir = opts.dir || process.cwd();

            let routes = require(`${dir}/routes`);

            let lambdas = require('./lambdas')(opts);

            let conductor = require('../lib/routeAnalyzer').conductor(routes);

            let cookieParser = require('../lib/cookieParser');

            server = http.createServer((req, res) => {
                let choosenRoute = conductor(req.url);
                if (!choosenRoute) {
                    res.write('HTTP/1.1 404 Not Found\r\n\r\n');
                    res.end();
                }
                else {
                    lambdas[choosenRoute.rail](Object.assign({
                        cookies: cookieParser(req.headers)
                    }, choosenRoute, {
                        body: req.body,
                        headers: req.headers
                    }), {}, (err, data, ress) => {
                        if (typeof data !== 'string') {
                            data = JSON.stringify(data);
                        }
                        res.end(data);
                    });
                }
            });

            server.on('clientError', (err, socket) => {
                socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
            });

            server.listen(port || 1337);
        }
    ,
        close : (cb) => {
            if(server){
                server.close(() => {
                    server = null;
                    if(typeof cb === 'function'){
                        cb();
                    }
                });
            }
        }
    }
    ;

})();<commit_msg>bind req,res via closure<commit_after>/**
 * Created by daniel.irwin on 6/17/17.
 */
module.exports = (function() {

    let server = null;

    return {
        start : function (port, opts) {
            const http = require('http');

            let dir = opts.dir || process.cwd();

            let routes = require(`${dir}/routes`);

            let lambdas = require('./lambdas')(opts);

            let conductor = require('../lib/routeAnalyzer').conductor(routes);

            let cookieParser = require('../lib/cookieParser');

            server = http.createServer((httpRequest, httpResponse) => {
                 function (req, res) {
                   let choosenRoute = conductor(req.url);
                    if (!choosenRoute) {
                        res.write('HTTP/1.1 404 Not Found\r\n\r\n');
                        res.end();
                    }
                    else {
                        lambdas[choosenRoute.rail](Object.assign({
                            cookies: cookieParser(req.headers)
                        }, choosenRoute, {
                            body: req.body,
                            headers: req.headers
                        }), {}, (err, data, ress) => {
                            if (typeof data !== 'string') {
                                data = JSON.stringify(data);
                            }
                            res.end(data);
                        });
                    }
                }(httpRequest, httpResponse);
            });

            server.on('clientError', (err, socket) => {
                socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
            });

            server.listen(port || 1337);
        }
    ,
        close : (cb) => {
            if(server){
                server.close(() => {
                    server = null;
                    if(typeof cb === 'function'){
                        cb();
                    }
                });
            }
        }
    }
    ;

})();
<|endoftext|>


<commit_before>let expect = require('chai').expect;
let {Dealer, Stack} = require('../app/dealer');

var myDealer = new Dealer();

describe('Dealer', () => {

	describe('Validation', () => {
		it('requires a number of chips', () => {
			let dealer = new Dealer();
			expect(dealer.deal()).to.equal('I require a number of chips before dealing.');
			dealer = new Dealer(0);
			expect(dealer.deal()).to.equal('I require a number of chips before dealing.');
		});
		it('requires an amount of possible denominations of chips', () => {
			let dealer = new Dealer(10,0,0,0);
			expect(dealer.deal()).to.equal('I require a number of possible chip denominations before dealing.');
			dealer = new Dealer(10);
			expect(dealer.deal()).to.equal('I require a number of possible chip denominations before dealing.');
		});
		it('requires an amount of players', () => {
			let dealer = new Dealer(10,10);
			expect(dealer.deal()).to.equal('I require a number of players before dealing.');
			dealer = new Dealer(10,10,0,0);
			expect(dealer.deal()).to.equal('I require a number of players before dealing.');
		});
		it('requires a buy-in', () => {
			let dealer = new Dealer(10,10,10);
			expect(dealer.deal()).to.equal('I require a buy-in (total value) before dealing.');
			dealer = new Dealer(10,10,10,0);
			expect(dealer.deal()).to.equal('I require a buy-in (total value) before dealing.');
		});
	});

	describe('Distribution', () => {
		it('should provide a stack of at least 50 big blinds', () => {
			let stack = new Dealer(chips, 1, players, 10).distribute();
			expect(stack.totalPerPlayer >= (50 * stack.bigBlindDenomination)).to.be.true;
		});
		it('1000 chips, 1 denomination, 10 players, 10 buy-in', () => {
			let chips = 1000;
			let players = 10;
			let stack = new Dealer(chips, 1, players, 10).distribute();
			expect(stack.denominations).to.deep.equal([10]);
			expect(stack.amounts).to.deep.equal([10]);
			expect(stack.totalPerPlayer).to.equal(100);
			expect(stack.totalPerPlayer * players).to.equal(chips);
		});
		// it('1000 chips, 5 denominations, 1 player, 10 buy-in', () => {
		// });
		// it('distribution with 100 chips, 5 values, 9 players, 10 buy-in', () => {
		// });
		// it('distribution with 100 total chips, 5 values, 6 players, 10 buy-in', () => {
		// });
	});

	describe('Stack', () => {
		it('denominations returns all the different denominations', () => {
			let stack = new Stack([1,10],[2,20],[3,45]);
			expect(stack.denominations).to.deep.equal([1,2,3]);
		});
		it('amounts returns all the different amounts', () => {
			let stack = new Stack([1,10],[2,20],[3,45]);
			expect(stack.amounts).to.deep.equal([10,20,45]);
		});
		it('totalPerPlayer returns the sum of value-amount pairs', () => {
			let stack = new Stack([1,10],[2,20],[3,45]);
			expect(stack.totalPerPlayer).to.deep.equal(185);
		});
	});

});
<commit_msg>added my pokersets<commit_after>let expect = require('chai').expect;
let {Dealer, Stack} = require('../app/dealer');

var myDealer = new Dealer();

describe('Dealer', () => {

	describe('Validation', () => {
		it('requires a number of chips', () => {
			let dealer = new Dealer();
			expect(dealer.deal()).to.equal('I require a number of chips before dealing.');
			dealer = new Dealer(0);
			expect(dealer.deal()).to.equal('I require a number of chips before dealing.');
		});
		it('requires an amount of possible denominations of chips', () => {
			let dealer = new Dealer(10,0,0,0);
			expect(dealer.deal()).to.equal('I require a number of possible chip denominations before dealing.');
			dealer = new Dealer(10);
			expect(dealer.deal()).to.equal('I require a number of possible chip denominations before dealing.');
		});
		it('requires an amount of players', () => {
			let dealer = new Dealer(10,10);
			expect(dealer.deal()).to.equal('I require a number of players before dealing.');
			dealer = new Dealer(10,10,0,0);
			expect(dealer.deal()).to.equal('I require a number of players before dealing.');
		});
		it('requires a buy-in', () => {
			let dealer = new Dealer(10,10,10);
			expect(dealer.deal()).to.equal('I require a buy-in (total value) before dealing.');
			dealer = new Dealer(10,10,10,0);
			expect(dealer.deal()).to.equal('I require a buy-in (total value) before dealing.');
		});
	});

	describe('Distribution', () => {
		it('should provide a stack of at least 50 big blinds', () => {
			let chips = 1000;
			let players = 10;
			let stack = new Dealer(chips, 1, players, 10).distribute();
			expect(stack.totalPerPlayer >= (50 * stack.bigBlindDenomination)).to.be.true;
		});
		it('1000 chips, 1 denomination, 10 players, 10 buy-in', () => {
			let chips = 1000;
			let players = 10;
			let stack = new Dealer(chips, 1, players, 10).distribute();
			expect(stack.denominations).to.deep.equal([10]);
			expect(stack.amounts).to.deep.equal([10]);
			expect(stack.totalPerPlayer).to.equal(100);
			expect(stack.totalPerPlayer * players).to.equal(chips);
		});
		// Full poker set big (8 different denoms); 500 chips
		// {
		// 	.25 :  75,
		// 	.50 :  75,
		// 	1   : 100,
		// 	5   : 100,
		// 	10  :  50,
		// 	25  :  50,
		// 	100 :  25,
		// 	500 :  25
		// }
		it('1000 chips, 8 denomination, 1 players, 10 buy-in', () => {
			let chips = 1000;
			let players = 10;
			let stack = new Dealer(chips, 1, players, 10).distribute();
			expect(stack.denominations).to.deep.equal([10]);
			expect(stack.amounts).to.deep.equal([10]);
			expect(stack.totalPerPlayer).to.equal(100);
			expect(stack.totalPerPlayer * players).to.equal(chips);
		}
		// Full poker set small (4 different denoms); 200 chips
		// {
		// 	.25 : 100,
		// 	.50 : 100,
		// 	1   :  50,
		// 	5   :  50
		// }
		it('1000 chips, 1 denomination, 10 players, 10 buy-in', () => {
			let chips = 1000;
			let players = 10;
			let stack = new Dealer(chips, 1, players, 10).distribute();
			expect(stack.denominations).to.deep.equal([10]);
			expect(stack.amounts).to.deep.equal([10]);
			expect(stack.totalPerPlayer).to.equal(100);
			expect(stack.totalPerPlayer * players).to.equal(chips);
		});
		// it('1000 chips, 5 denominations, 1 player, 10 buy-in', () => {
		// });
		// it('distribution with 100 chips, 5 values, 9 players, 10 buy-in', () => {
		// });
		// it('distribution with 100 total chips, 5 values, 6 players, 10 buy-in', () => {
		// });
	});

	describe('Stack', () => {
		it('denominations returns all the different denominations', () => {
			let stack = new Stack([1,10],[2,20],[3,45]);
			expect(stack.denominations).to.deep.equal([1,2,3]);
		});
		it('amounts returns all the different amounts', () => {
			let stack = new Stack([1,10],[2,20],[3,45]);
			expect(stack.amounts).to.deep.equal([10,20,45]);
		});
		it('totalPerPlayer returns the sum of value-amount pairs', () => {
			let stack = new Stack([1,10],[2,20],[3,45]);
			expect(stack.totalPerPlayer).to.deep.equal(185);
		});
	});

});
<|endoftext|>


<commit_before>import { applyMiddleware as reduxApplyMiddleware, combine } from 'redux'
import thunk from 'redux-thunk'

export default function applyMiddleware (...additionalMiddlewares) {
  return createStore => (reducer, initial, enhancer) => {
    const middleware = reduxApplyMiddleware(thunk)
    const enhancerFunc = enhancer ? combine(enhancer, middleware) : middleware
    return createStore(reducer, initial, enhancerFunc)
  }
}
<commit_msg>Fix combine order<commit_after>import { applyMiddleware as reduxApplyMiddleware, combine } from 'redux'
import thunk from 'redux-thunk'

export default function applyMiddleware (...additionalMiddlewares) {
  return createStore => (reducer, initial, enhancer) => {
    const middleware = reduxApplyMiddleware(thunk)
    const enhancerFunc = enhancer ? combine(middleware, enhancer) : middleware
    return createStore(reducer, initial, enhancerFunc)
  }
}
<|endoftext|>


<commit_before>// #DROPZONE#
// This script implements the dropzone settings
'use strict';

/* global Dropzone */
(function ($) {
    $(function () {
        var dropzoneTemplateSelector = '.js-filer-dropzone-template';
        var previewImageSelector = '.js-img-preview';
        var dropzoneSelector = '.js-filer-dropzone';
        var messageSelector = '.js-filer-dropzone-message';
        var lookupButtonSelector = '.js-related-lookup';
        var progressSelector = '.js-filer-dropzone-progress';
        var previewImageWrapperSelector = '.js-img-wrapper';
        var filerClearerSelector = '.filerClearer';
        var fileChooseSelector = '.js-file-selector';
        var dropzones = $(dropzoneSelector);
        var fileIdInputSelector = '.vForeignKeyRawIdAdminField';
        var hiddenClass = 'hidden';
        var mobileClass = 'filer-dropzone-mobile';
        var objectAttachedClass = 'js-object-attached';
        var minWidth = 500;
        var checkMinWidth = function (element) {
            element.toggleClass(mobileClass, element.width() < minWidth);
        };
        var showError = function (message) {
            try {
                window.parent.CMS.API.Messages.open({
                    message: message
                });
            } catch (errorText) {
                console.log(errorText);
            }
        };

        if (dropzones.length && Dropzone && !window.filerDropzoneInitialized) {
            window.filerDropzoneInitialized = true;
            Dropzone.autoDiscover = false;
            dropzones.each(function () {
                var dropzone = $(this);
                var dropzoneUrl = dropzone.data('url');
                var inputId = dropzone.find(fileIdInputSelector);
                var isImage = inputId.is('[name="image"]');
                var lookupButton = dropzone.find(lookupButtonSelector);
                var message = dropzone.find(messageSelector);
                var clearButton = dropzone.find(filerClearerSelector);
                var fileChoose = dropzone.find(fileChooseSelector);

                $(window).on('resize', function () {
                    checkMinWidth(dropzone);
                });

                new Dropzone(this, {
                    url: dropzoneUrl,
                    paramName: 'file',
                    maxFiles: 1,
                    previewTemplate: $(dropzoneTemplateSelector).html(),
                    clickable: false,
                    addRemoveLinks: false,
                    init: function () {
                        checkMinWidth(dropzone);
                        this.on('removedfile', function () {
                            fileChoose.show();
                            dropzone.removeClass(objectAttachedClass);
                            this.removeAllFiles(true);
                            clearButton.trigger('click');
                        });
                        $('img', this.element).on('dragstart', function (event) {
                            event.preventDefault();
                        });
                        clearButton.on('click', function () {
                            dropzone.removeClass(objectAttachedClass);
                        });
                    },
                    maxfilesexceeded: function () {
                        this.removeAllFiles(true);
                    },
                    drop: function () {
                        this.removeAllFiles(true);
                        fileChoose.hide();
                        lookupButton.addClass(hiddenClass);
                        message.addClass(hiddenClass);
                        dropzone.removeClass('dz-drag-hover');
                        dropzone.addClass(objectAttachedClass);
                    },
                    success: function (file, response) {
                        dropzone.find(progressSelector).addClass(hiddenClass);

                        if (file && file.status === 'success' && response) {
                            if (response.file_id) {
                                inputId.val(response.file_id);
                            }
                            if (response.thumbnail_180) {
                                if (isImage) {
                                    $(previewImageSelector).css({
                                        'background-image': 'url(' + response.thumbnail_180 + ')'
                                    });
                                    $(previewImageWrapperSelector).removeClass(hiddenClass);
                                }
                            }
                        } else {
                            if (response && response.error) {
                                window.showError(file.name + ': ' + response.error);
                            }
                            this.removeAllFiles(true);
                        }

                        $('img', this.element).on('dragstart', function (event) {
                            event.preventDefault();
                        });
                    },
                    error: function (file, errorMessage) {
                        showError(file.name + ': ' + errorMessage);
                        this.removeAllFiles(true);
                    },
                    reset: function () {
                        if (isImage) {
                            $(previewImageWrapperSelector).addClass(hiddenClass);
                            $(previewImageSelector).css({'background-image': 'none'});
                        }
                        dropzone.removeClass(objectAttachedClass);
                        inputId.val('');
                        lookupButton.removeClass(hiddenClass);
                        message.removeClass(hiddenClass);
                    }
                });
            });
        }
    });
})(jQuery);
<commit_msg>Fix 5xx ajax upload response handling in JS<commit_after>// #DROPZONE#
// This script implements the dropzone settings
'use strict';

/* global Dropzone */
(function ($) {
    $(function () {
        var dropzoneTemplateSelector = '.js-filer-dropzone-template';
        var previewImageSelector = '.js-img-preview';
        var dropzoneSelector = '.js-filer-dropzone';
        var messageSelector = '.js-filer-dropzone-message';
        var lookupButtonSelector = '.js-related-lookup';
        var progressSelector = '.js-filer-dropzone-progress';
        var previewImageWrapperSelector = '.js-img-wrapper';
        var filerClearerSelector = '.filerClearer';
        var fileChooseSelector = '.js-file-selector';
        var dropzones = $(dropzoneSelector);
        var fileIdInputSelector = '.vForeignKeyRawIdAdminField';
        var hiddenClass = 'hidden';
        var mobileClass = 'filer-dropzone-mobile';
        var objectAttachedClass = 'js-object-attached';
        var minWidth = 500;
        var checkMinWidth = function (element) {
            element.toggleClass(mobileClass, element.width() < minWidth);
        };
        var showError = function (message) {
            try {
                window.parent.CMS.API.Messages.open({
                    message: message
                });
            } catch (errorText) {
                console.log(errorText);
            }
        };

        if (dropzones.length && Dropzone && !window.filerDropzoneInitialized) {
            window.filerDropzoneInitialized = true;
            Dropzone.autoDiscover = false;
            dropzones.each(function () {
                var dropzone = $(this);
                var dropzoneUrl = dropzone.data('url');
                var inputId = dropzone.find(fileIdInputSelector);
                var isImage = inputId.is('[name="image"]');
                var lookupButton = dropzone.find(lookupButtonSelector);
                var message = dropzone.find(messageSelector);
                var clearButton = dropzone.find(filerClearerSelector);
                var fileChoose = dropzone.find(fileChooseSelector);

                $(window).on('resize', function () {
                    checkMinWidth(dropzone);
                });

                new Dropzone(this, {
                    url: dropzoneUrl,
                    paramName: 'file',
                    maxFiles: 1,
                    previewTemplate: $(dropzoneTemplateSelector).html(),
                    clickable: false,
                    addRemoveLinks: false,
                    init: function () {
                        checkMinWidth(dropzone);
                        this.on('removedfile', function () {
                            fileChoose.show();
                            dropzone.removeClass(objectAttachedClass);
                            this.removeAllFiles(true);
                            clearButton.trigger('click');
                        });
                        $('img', this.element).on('dragstart', function (event) {
                            event.preventDefault();
                        });
                        clearButton.on('click', function () {
                            dropzone.removeClass(objectAttachedClass);
                        });
                    },
                    maxfilesexceeded: function () {
                        this.removeAllFiles(true);
                    },
                    drop: function () {
                        this.removeAllFiles(true);
                        fileChoose.hide();
                        lookupButton.addClass(hiddenClass);
                        message.addClass(hiddenClass);
                        dropzone.removeClass('dz-drag-hover');
                        dropzone.addClass(objectAttachedClass);
                    },
                    success: function (file, response) {
                        dropzone.find(progressSelector).addClass(hiddenClass);

                        if (file && file.status === 'success' && response) {
                            if (response.file_id) {
                                inputId.val(response.file_id);
                            }
                            if (response.thumbnail_180) {
                                if (isImage) {
                                    $(previewImageSelector).css({
                                        'background-image': 'url(' + response.thumbnail_180 + ')'
                                    });
                                    $(previewImageWrapperSelector).removeClass(hiddenClass);
                                }
                            }
                        } else {
                            if (response && response.error) {
                                window.showError(file.name + ': ' + response.error);
                            }
                            this.removeAllFiles(true);
                        }

                        $('img', this.element).on('dragstart', function (event) {
                            event.preventDefault();
                        });
                    },
                    error: function (file, response) {
                        showError(file.name + ': ' + response.error);
                        this.removeAllFiles(true);
                    },
                    reset: function () {
                        if (isImage) {
                            $(previewImageWrapperSelector).addClass(hiddenClass);
                            $(previewImageSelector).css({'background-image': 'none'});
                        }
                        dropzone.removeClass(objectAttachedClass);
                        inputId.val('');
                        lookupButton.removeClass(hiddenClass);
                        message.removeClass(hiddenClass);
                    }
                });
            });
        }
    });
})(jQuery);
<|endoftext|>


<commit_before>import Q from 'q';
const _ = require('lodash');
import moment from 'moment';
import naturalSort from 'javascript-natural-sort';
const fs = require('fs');

export default class GrabberLogic {
	static doBackup(lib, argv) {
		return Q()
			.then(() => {
				return lib.getAllTasks();
			})
			.then((tasks) => {
				if (argv.output) {
					//noinspection JSUnresolvedFunction
					return Q.fcall(fs.writeFile, argv.output, JSON.stringify(tasks, null, 4));
				}
				console.log(tasks);
			});
	}

	static getStats(lib, argv) {
		return Q()
			.then(() => lib.getAllTasks())
			.then((tasks) => GrabberLogic.beautifyTasks(lib, tasks))
			.tap((tasks) => {
				const crunchTasks = _.filter(tasks, t => t.project === 'Crunch');
				return console.log(`Will omit ${crunchTasks.length} Crunch tasks`);
			})
			.then((tasks) => _.reject(tasks, t => t.project === 'Crunch'))
			.tap(() => console.log('------DONE BY PROJECT---------'))
			.tap((tasks) => GrabberLogic.printCompletedByProjectStats(lib, argv, tasks))
			.tap(() => console.log('-----------DONE---------------'))
			.tap((tasks) => GrabberLogic.printCompletedStats(argv, tasks))
			.tap((tasks) => GrabberLogic.printIncompleteStats(lib, argv, tasks))

	}

	//noinspection JSUnusedLocalSymbols
	static printIncompleteStats(lib, argv, tasks) {
		return Q()
			.then(() => _.reject(tasks, 'completed')) // there's a bug here. It pass completed which were completed after week end
			.then((tasks) => _.filter(tasks, t => t.myType === 'planned'))
			.tap(incompletePlanned => console.log(incompletePlanned.length));
	}

	static printCompletedByProjectStats(lib, argv, tasks) {
		let lastWeekStart = GrabberLogic.getLastWeekStart(argv);
		let lastWeekEnd = GrabberLogic.getLastWeekEnd(argv);

		console.log(`Searching completed by project between ${lastWeekStart.format()} and ${lastWeekEnd.format()}`);

		return Q()
			.then(() => _.filter(tasks, 'completed'))
			.then((tasks) => _.filter(tasks, t => t.completedMoment.clone().isBetween(lastWeekStart, lastWeekEnd)))
			.then((tasks) => _.groupBy(tasks, 'project'))
			.then((groupedTasks) => {
				return Q()
					.then(() => lib.getProjects())
					.then((projects) => {
						const projectNames = _.values(projects);
						_.each(projectNames, project => {
							if(!groupedTasks[project])
								groupedTasks[project] = [];
						});
						return groupedTasks;
					});
			})
			.tap((groupedTasks) => {
				if (groupedTasks[undefined]) {
					console.warn('there are tasks with undefined project, assign them first!');
					console.log(_.map(groupedTasks[undefined], t => t.title));
				}
				else {
					let sortedProjects = _(groupedTasks).keys().sort(naturalSort).value();
					_.each(sortedProjects, projectName => {
						console.log(`${projectName ? projectName : 'Unknown'}: ${groupedTasks[projectName].length}`);
					})
				}
			});
	}

	static getLastWeekStart(argv) {
		if (argv.start)
			return moment.utc(argv.start);
		const proposedStart = moment.utc().startOf('week').subtract(1, 'day').utcOffset(5);
		if (proposedStart.diff(moment.utc(), 'days') <= 3) // most likely late statistics gathering
			proposedStart.subtract(1, 'week');
		return proposedStart;
	}

	static getLastWeekEnd(argv) {
		if (argv.end)
			return moment.utc(argv.end);
		return GrabberLogic.getLastWeekStart(argv).clone().add(1, 'week').utcOffset(5);
	}

	static printCompletedStats(argv, tasks) {
		let lastWeekStart = GrabberLogic.getLastWeekStart(argv);
		let lastWeekEnd = GrabberLogic.getLastWeekEnd(argv);

		console.log(`Searching completed between ${lastWeekStart.format()} and ${lastWeekEnd.format()}`);

		function formatTask(t) {
			return {
				title: t.title,
				completedAt: t.completedAt
			}
		}

		let lastWeek = [];
		let scheduledDone = [];
		let plannedDone = [];
		let unplannedDone = [];
		//noinspection JSUnresolvedVariable
		return Q()
			.then(() => _.filter(tasks, 'completed'))
			.then((tasks) => _.filter(tasks, t => t.completedMoment.clone().isAfter(lastWeekStart)))
			.tap((tasks) => lastWeek = _.filter(tasks, t => t.completedMoment.clone().isBetween(lastWeekStart, lastWeekEnd)))
			.tap((tasks) => scheduledDone = _.filter(lastWeek, t => t.myType === 'scheduled'))
			.tap((tasks) => plannedDone = _.filter(lastWeek, t => t.myType === 'planned'))
			.tap((tasks) => unplannedDone = _.filter(lastWeek, t => t.myType === 'unplanned'))
			// for debug only
			.then((tasks) => _.map(tasks, formatTask))
			.tap(argv.debugPrint ? console.log : () => {
				})
			// then result is ignored, uses side-effected vars above
			.then(() => {
				console.log(`Scheduled done: ${scheduledDone.length}`);
				console.log(`Planned done ${plannedDone.length}`);
				console.log(`Unplanned done ${unplannedDone.length}`);
				console.log('Incomplete - see next');
				console.log('For copying:');
				console.log(scheduledDone.length);
				console.log(plannedDone.length);
				console.log(unplannedDone.length);
			});
	}

	static beautifyTasks(lib, tasks) {
		/*
		 {
		 "repeat_no": "20161015",
		 "attribute": "plan",
		 "all_day": true,
		 "start_at": 1476640800000,
		 "end_at": 0,
		 "project": "6780af3d-ddb2-47f0-b8cc-282da2901cb4",
		 "priority": 2,
		 "sent_at": 0,
		 "now": false,
		 "pos": 47755005,
		 "estimated_time": 0,
		 "spent_time": 0,
		 "uuid": "6d9b8ba2-fd8b-48c2-bf6e-286bff3c9702",
		 "title": "Пропылесосил",
		 "usn": 9251,
		 "created": 1476381741880,
		 "updated": 1476552136356,
		 "deleted": 0,
		 "trashed": 0,
		 "completed": 1476381741880,
		 "archived": 0,
		 "hidden": 0,
		 "id": "57ffcc2ded7850c026028352",
		 "type": "task"
		 }
		 * */
		/// MAPS TO
		/*
		 * {
		 * title: 'Пропылесосил',
		 * project: 'chores'
		 * completed: true,
		 * completedAt: '2016-10-23T16:54:00Z',
		 * priority: 'low',
		 * }
		 *
		 * */

		return Q()
			.then(() => lib.getProjects())
			.then((projects) => {
				return _.map(tasks, task => {

					const isCompleted = task.completed;
					const completedMoment = moment.utc(task.completed);
					return ({
						title: task.title,
						completed: isCompleted,
						completedAt: isCompleted ? completedMoment.format() : null,
						completedMoment: isCompleted ? completedMoment : null,
						priority: ['none', 'low', 'medium', 'high'][task.priority],
						myType: ['unplanned', 'planned', 'scheduled', 'scheduled'][task.priority],
						project: projects[task.project]
					});
				});
			})
	}

	static chooseTaskAndGo(doItLib, argv) {
		const mode = argv.mode;
		switch (mode) {
			case 'stats':
				return GrabberLogic.getStats(doItLib, argv);
			case 'backup': // intentionally pass through
			default:
				return GrabberLogic.doBackup(doItLib, argv);
		}
	}
}
<commit_msg>fix logic, skip project that are completed in getting completed tasks<commit_after>import Q from 'q';
const _ = require('lodash');
import moment from 'moment';
import naturalSort from 'javascript-natural-sort';
const fs = require('fs');

export default class GrabberLogic {
	static doBackup(lib, argv) {
		return Q()
			.then(() => {
				return lib.getAllTasks();
			})
			.then((tasks) => {
				if (argv.output) {
					//noinspection JSUnresolvedFunction
					return Q.fcall(fs.writeFile, argv.output, JSON.stringify(tasks, null, 4));
				}
				console.log(tasks);
			});
	}

	static getStats(lib, argv) {
		return Q()
			.then(() => lib.getAllTasks())
			.then((tasks) => GrabberLogic.beautifyTasks(lib, tasks))
			.tap((tasks) => {
				const crunchTasks = _.filter(tasks, t => t.project === 'Crunch');
				return console.log(`Will omit ${crunchTasks.length} Crunch tasks`);
			})
			.then((tasks) => _.reject(tasks, t => t.project === 'Crunch'))
			.tap(() => console.log('------DONE BY PROJECT---------'))
			.tap((tasks) => GrabberLogic.printCompletedByProjectStats(lib, argv, tasks))
			.tap(() => console.log('-----------DONE---------------'))
			.tap((tasks) => GrabberLogic.printCompletedStats(argv, tasks))
			.tap((tasks) => GrabberLogic.printIncompleteStats(lib, argv, tasks))

	}

	//noinspection JSUnusedLocalSymbols
	static printIncompleteStats(lib, argv, tasks) {
		return Q()
			.then(() => _.reject(tasks, 'completed')) // there's a bug here. It pass completed which were completed after week end
			.then((tasks) => _.filter(tasks, t => t.myType === 'planned'))
			.tap(incompletePlanned => console.log(incompletePlanned.length));
	}

	static printCompletedByProjectStats(lib, argv, tasks) {
		let lastWeekStart = GrabberLogic.getLastWeekStart(argv);
		let lastWeekEnd = GrabberLogic.getLastWeekEnd(argv);

		console.log(`Searching completed by project between ${lastWeekStart.format()} and ${lastWeekEnd.format()}`);

		return Q()
			.then(() => _.filter(tasks, 'completed'))
			.then((tasks) => _.filter(tasks, t => t.completedMoment.clone().isBetween(lastWeekStart, lastWeekEnd)))
			.then((tasks) => _.groupBy(tasks, 'project'))
			.then((groupedTasks) => {
				return Q()
					.then(() => lib.getProjects())
					.then((projects) => {
						const projectNames = _.values(projects);
						_.each(projectNames, project => {
							if(!groupedTasks[project])
								groupedTasks[project] = [];
						});
						return groupedTasks;
					});
			})
			.tap((groupedTasks) => {
				if (groupedTasks[undefined]) {
					console.warn('there are tasks with undefined project, assign them first!');
					console.log(_.map(groupedTasks[undefined], t => t.title));
				}
				else {
					let sortedProjects = _(groupedTasks).keys().sort(naturalSort).value();
					_.each(sortedProjects, projectName => {
						console.log(`${projectName ? projectName : 'Unknown'}: ${groupedTasks[projectName].length}`);
					})
				}
			});
	}

	static getLastWeekStart(argv) {
		if (argv.start)
			return moment.utc(argv.start);
		const proposedStart = moment.utc().startOf('week').subtract(1, 'day').utcOffset(5);
		if (proposedStart.diff(moment.utc(), 'days') <= 3) // most likely late statistics gathering
			proposedStart.subtract(1, 'week');
		return proposedStart;
	}

	static getLastWeekEnd(argv) {
		if (argv.end)
			return moment.utc(argv.end);
		return GrabberLogic.getLastWeekStart(argv).clone().add(1, 'week').utcOffset(5);
	}

	static printCompletedStats(argv, tasks) {
		let lastWeekStart = GrabberLogic.getLastWeekStart(argv);
		let lastWeekEnd = GrabberLogic.getLastWeekEnd(argv);

		console.log(`Searching completed between ${lastWeekStart.format()} and ${lastWeekEnd.format()}`);

		function formatTask(t) {
			return {
				title: t.title,
				completedAt: t.completedAt
			}
		}

		let lastWeek = [];
		let scheduledDone = [];
		let plannedDone = [];
		let unplannedDone = [];
		//noinspection JSUnresolvedVariable
		return Q()
			.then(() => _.filter(tasks, 'completed'))
			.then((tasks) => _.filter(tasks, t => t.completedMoment.clone().isAfter(lastWeekStart)))
			.tap((tasks) => lastWeek = _.filter(tasks, t => t.completedMoment.clone().isBetween(lastWeekStart, lastWeekEnd)))
			.tap((tasks) => scheduledDone = _.filter(lastWeek, t => t.myType === 'scheduled'))
			.tap((tasks) => plannedDone = _.filter(lastWeek, t => t.myType === 'planned'))
			.tap((tasks) => unplannedDone = _.filter(lastWeek, t => t.myType === 'unplanned'))
			// for debug only
			.then((tasks) => _.map(tasks, formatTask))
			.tap(argv.debugPrint ? console.log : () => {
				})
			// then result is ignored, uses side-effected vars above
			.then(() => {
				console.log(`Scheduled done: ${scheduledDone.length}`);
				console.log(`Planned done ${plannedDone.length}`);
				console.log(`Unplanned done ${unplannedDone.length}`);
				console.log('Incomplete - see next');
				console.log('For copying:');
				console.log(scheduledDone.length);
				console.log(plannedDone.length);
				console.log(unplannedDone.length);
			});
	}

	static beautifyTasks(lib, tasks) {
		/*
		 {
		 "repeat_no": "20161015",
		 "attribute": "plan",
		 "all_day": true,
		 "start_at": 1476640800000,
		 "end_at": 0,
		 "project": "6780af3d-ddb2-47f0-b8cc-282da2901cb4",
		 "priority": 2,
		 "sent_at": 0,
		 "now": false,
		 "pos": 47755005,
		 "estimated_time": 0,
		 "spent_time": 0,
		 "uuid": "6d9b8ba2-fd8b-48c2-bf6e-286bff3c9702",
		 "title": "Пропылесосил",
		 "usn": 9251,
		 "created": 1476381741880,
		 "updated": 1476552136356,
		 "deleted": 0,
		 "trashed": 0,
		 "completed": 1476381741880,
		 "archived": 0,
		 "hidden": 0,
		 "id": "57ffcc2ded7850c026028352",
		 "type": "task"
		 }
		 * */
		/// MAPS TO
		/*
		 * {
		 * title: 'Пропылесосил',
		 * project: 'chores'
		 * completed: true,
		 * completedAt: '2016-10-23T16:54:00Z',
		 * priority: 'low',
		 * }
		 *
		 * */

		return Q()
			.then(() => lib.getProjects())
			.then((projects) => {
				const tasksWithUndefineds = _.map(tasks, task => {
					const isCompleted = task.completed;
					const completedMoment = moment.utc(task.completed);
					if(task.type === 'project')
						return undefined;
					return ({
						title: task.title,
						completed: isCompleted,
						completedAt: isCompleted ? completedMoment.format() : null,
						completedMoment: isCompleted ? completedMoment : null,
						priority: ['none', 'low', 'medium', 'high'][task.priority],
						myType: ['unplanned', 'planned', 'scheduled', 'scheduled'][task.priority],
						project: projects[task.project]
					});
				});
				return _.filter(tasksWithUndefineds)
			})
	}

	static chooseTaskAndGo(doItLib, argv) {
		const mode = argv.mode;
		switch (mode) {
			case 'stats':
				return GrabberLogic.getStats(doItLib, argv);
			case 'backup': // intentionally pass through
			default:
				return GrabberLogic.doBackup(doItLib, argv);
		}
	}
}
<|endoftext|>