<commit_before>/*
================
 CQ5 properties
================
 .data["0"].Title (String)
 .data["0"].Description (String)
 .data["0"].Tags (Array)
 .data["0"].Creator (String)
 .data["0"].Contributor (String)
 .data["0"].Language (String)
 .data["0"]["Rights-x-repair"] (String)
 .data["0"].Owner (String)
 .data["0"].ExpirationDate (2017:05:19 03:15-05:00) => (5/19/17 3:15 AM)
================
*/

// variables
const isDevelopment = process.env.NODE_ENV && process.env.NODE_ENV.trim() !== 'production';
const vueModule = isDevelopment ? '../node_modules/vue/dist/vue.js' : '../node_modules/vue/dist/vue.min.js'

const { ipcRenderer } = require('electron');
const csv = require('csvtojson');
const Vue = require(vueModule);

let $vm;

// options for vue instance
function getInitialData(){
  return {
    revertFile: [],
    conversionStarted: false,
    csvCache: null,
    csvDir: null,
    csvFileCount: 0,
    csvFilesProcessed: 0,
    csvFilesSeen: 0,
    csvMaxSizeMB: 30,
    csvName: null,
    csvPath: null,
    csvSize: null,
    data: [],
    errorLog: [],
    filesSkipped: 0,
    hovering: false,
    importCount: 0,
    message: '',
    page: 1,
    title: '...',
    validFileCount: 0
  };
}

// set up drag and drop functionality
window.addEventListener('dragover', e =>  $vm.dragcheck(e) );
window.addEventListener('dragenter', e =>  $vm.dragcheck(e) );
window.addEventListener('dragleave', e =>  $vm.dragcheck(e) );
window.addEventListener('dragend', e =>  $vm.dragcheck(e) );

window.addEventListener('drop', (e) => {
  $vm.dragcheck(e);
  $vm.hovering = false;
});

// vue instance
$vm = new Vue({
  el: '.container',
  data: getInitialData(),
  methods: {
    // saves a copy of the data from the selected csv to state
    cacheFile(data){
      // remove items with no Path property
      data = data.filter((val, indx) => {
        return val.hasOwnProperty('Path');
      });

      this.csvCache = data;
      this.csvFileCount = data.length;
    },
    // creates a backup of current files metadata before editing them
    createBackup(){
      this.conversionStarted = true;
      ipcRenderer.send('save-backup', this.revertFile, this.csvDir);
    },
    // drag and drop visual feedback
    dragcheck(e){
      if(e.target.type === 'file'){
        this.hovering = true;
      }else{
        e.preventDefault();
        this.hovering = false;
      }
    },
    // gets the extension of files
    getExtension(fileName){
      if(!fileName){
        return null;
      }
      return fileName.split('.').pop().substr(0, 3).toLowerCase();
    },
    // generates html for file icon
    getIcon(extension){
      let fileType;

      switch(extension){
        case 'pdf':
          fileType = 'file-pdf-o';
          break;
        case null:
          fileType = 'chain-broken';
          break;
        default:
          fileType = 'file-o';
      }

      return `
      `;
    },
    // validates the csv and then caches it
    getInputFile(e){
      let { name, path, size } = e.target.files[0];
      let message;
      let pathLength;
      let dir;

      if(!name.endsWith('.csv')){
        message = 'file must be a csv';
      }else if(size >= this.csvMaxSizeMB * 1000000){
        message = `file must be less than ${this.csvMaxSizeMB}MB`;
      }

      if(message){
        this.message = message;
        document.querySelector('.drop-box').reset();
        this.csvName = null;
        this.csvPath = null;
        this.csvDir = null;
        this.csvSize = null;
        return false;
      }

      pathLength = path.length - name.length;
      dir = path.slice(0, pathLength);

      this.csvName = name;
      this.csvPath = path;
      this.csvDir = dir;
      this.csvSize = size;
      this.message = `"${name}" has been selected!`;
      this.page = 2;
    },
    // process cached csv data and then read in the files
    importFile(){
      this.page = 4;

      this.processCache()
        .then((data) => this.data = data)
        .then(() => this.readImportedFiles());
    },
    // reveals the file in the explorer
    openWithShell(filePath){
      ipcRenderer.send('show-file', filePath);
    },
    // validate csv and then cache it
    preview(){
      this.page++;
      let errorMessage = '';

      if(!this.csvPath){
        errorMessage = "File required: You must select a file";
      }else if(!this.csvName.endsWith('.csv')){
        errorMessage = `Invalid extension: File must end with ".csv"`;
      }else if(this.csvSize > this.csvMaxSizeMB * 1000000){
        errorMessage = `File too big: Please limit files to ${this.csvMaxSizeMB}MB`;
      }

      if(!errorMessage){
        this.readCsvData(csv, this.csvPath)
          .then((data) => this.cacheFile(data));
      }else{
        this.showErrorMessage('Preview Error', errorMessage);
      }
    },
    // mutates and returns a copy of the selected csv's data for use in processing
    processCache(){
      return new Promise((resolve, reject) => {
        let result;

        try{
          result = this.csvCache.map((doc, indx) => {
            let name;
            let path;
            let extension;
            let val;

            name = doc['Path'];
            delete doc['Path'];
            path = this.csvDir;
            extension = this.getExtension(name);

            doc['zzz_id'] = indx;
            doc['zzz_path'] = name;
            doc['zzz_fullPath'] = `${path}${name}`;
            doc['zzz_extension'] = extension;
            doc['zzz_icon'] = this.getIcon(extension);
            doc['zzz_processedStatus'] = extension === 'pdf' ? null : false;
            doc['zzz_original'] = null;
            doc['zzz_showDetails'] = false;

            /*
            * if the key contains a : then its an array prop:
            * Tags::1: "ONE", Tags::2: "TWO", Tags::3: "THREE" => Tags["ONE", "TWO", "THREE"]
            * Tags:ROBOTS: "FOLLOW" => Tags['ROBOTS:FOLLOW']
            */
            Object.keys(doc).map((key, n) => {
              val = '';

              if(key.includes(':')){
                let baseKey = key.substr(0, key.indexOf(':'));
                let propName = key.substr(key.indexOf(':') + 1);

                if(!doc[baseKey]){
                  doc[baseKey] = [];
                }

                if(propName && propName.startsWith(':')){
                  propName = '';
                }

                if(doc[key] !== 'DELETE'){
                  val = `${propName ? propName + ':' : ''}${doc[key]}`;
                }

                if(val){
                  doc[baseKey].push(val);
                }
                delete doc[key];
              }
            });

            return doc;
          });

          resolve(result);
        }catch(e){
          reject(e);
        }

      });
    },
    // reads the csv file in as a json object
    readCsvData(npmModule, csvFilePath){
      return new Promise((resolve, reject) => {
        let result = [];

        npmModule()
          .fromFile(csvFilePath)
          .on('json', (jsonObj) => {
            result.push(jsonObj);
          })
          .on('done', (error) => {
            if(error){
              reject(error);
            }
            resolve(result);
          });
      });
    },
    // reads meta from the files listed in the csv
    readImportedFiles(){
      return new Promise((resolve, reject) => {
        let data = this.data;
        let filePath;

        try{
          for(let i = 0, len = data.length; i < len; i++){
            filePath = data[i]['zzz_fullPath'];
            this.readMetaAsync('exiftool-read', filePath, i);
          };
          resolve(true);
        }catch(e){
          reject(e);
        }
      });
    },
    // make an asynchronus request
    readMetaAsync(channel, filePath, indx){
      ipcRenderer.send(channel, filePath, indx);
    },
    // reset app to default state
    reset(){
      Object.assign(this.$data, getInitialData());
    },
    // toggles display of file details
    toggleDetails(e, indx){
      let obj = Object.assign({}, this.data);
      let bool = !obj[indx]['zzz_showDetails'];

      if (e.ctrlKey){;
        for(let i = 0, len = Object.keys(obj).length; i < len; i++){
          obj[i]['zzz_showDetails'] = bool;
        }
      }else{
        obj[indx]['zzz_showDetails'] = bool;
      }

      this.data = obj;
    },
    // show and error message
    showErrorMessage(title, err){
      ipcRenderer.send('show-error', title, err);
    },
    // modifies the metadata of all files in the list
    updateFiles(){
      let data;
      let filePath;
      let status;
      let len = Object.keys(this.data).length;
      let arr = Object.assign({}, this.data);
      let val;

      for(let i = 0; i < len; i++){
        data = {};
        filePath = arr[i]['zzz_fullPath'];
        processedStatus = arr[i]['zzz_processedStatus'];

        if(!filePath || processedStatus === false){
          this.filesSkipped++;
          this.csvFilesSeen++;
          continue;
        }

        Object.keys(arr[i]).forEach((key) => {
          val = arr[i][key];
          if((typeof val == 'string' || val instanceof String) && val === 'DELETE'){
            data[key] = ' ';
          }else if(val && !key.toLowerCase().startsWith('zzz_')){
            data[key] = val;
          }
        });

        this.updateMeta(filePath, data, i);
      }
    },
    // update the status flag of items that were reviewed and updated or skipped
    updateListItemStatus(indx, updateStatus){
      let itemToUpdate = Object.assign({}, this.data[indx]);
      itemToUpdate.zzz_processedStatus = updateStatus;
      $vm.$set(this.data, indx, itemToUpdate);
      if(updateStatus){
        this.csvFilesProcessed++;
      }else{
        this.filesSkipped++;
      }

      this.csvFilesSeen++;
    },
    // write to file with exiftool
    updateMeta(filePath, data, indx){
      ipcRenderer.send('exiftool-write', filePath, data, indx);
    }
  },
  computed: {
    info: function(){
      return {
        sourceFile: {
          name: 'Source',
          value: this.csvName || 'No file selected'
        },
        fileCount: {
          name: 'File Count',
          value: this.csvFileCount || 0
        },
        filesProcessed: {
          name: 'Files Updated',
          value: this.csvFilesProcessed || 0
        },
        filesSkipped: {
          name: 'Files Skipped',
          value: this.filesSkipped
        }
      };
    },
    isDone: function(){
      let isDone = this.conversionStarted && this.csvFilesSeen && this.csvFilesSeen === this.csvFileCount;

      if(isDone && this.errorLog.length){
        ipcRenderer.send('log-errors', this.errorLog);
      }

      return isDone;
    }
  }
});

// electron process event listeners
// once file is read, check it and update the array
ipcRenderer.on('exiftool-read-reply', (event, res, indx) => {
  let result;
  let keys;
  let key;
  let backup = {};
  let oldData = {};

  if(res.error && indx !== -1 || indx >= 0){
    // get the data for all files
    result = Object.assign({}, $vm.data);

    if(res.error){
      // update problem file with error info
      result[indx]['zzz_path'] = res.error;
      result[indx]['zzz_icon'] = $vm.getIcon(null);
      $vm.errorLog.push(res.error);
    }else{
      // if pdf, mark as valid
      if(result[indx]['zzz_extension'] === 'pdf'){
        $vm.validFileCount++;
      }

      // get keys and remove zzz keys
      keys = Object.keys(result[indx]);
      keys = keys.filter((key) => {
        return !key.startsWith('zzz_');
      });

      // for all keys in stored data...
      // (if the value is not present it should be set to DELETE in the backup file)
      for(let i = 0, len = keys.length; i < len; i++){

        key = keys[i];

        // if the file to be edited has the key...
        if(res.data[0].hasOwnProperty(key)){

          // ... and the value is an array...
          if(Array.isArray(res.data[0][key])){
            let arr = res.data[0][key];
            let num = 1;

            // loop through each array item and parse out the values for the backup file
            for(let i = 0, len = arr.length; i < len; i++){

              // Case 1 = Tags::1: "ROBOTS:FOLLOW", Tags::2: "FOO:BAR" => Tags['ROBOTS:FOLLOW', 'FOO:BAR']
              // Case 2 = Tags:ROBOTS: "FOLLOW", Tags:FOO: "BAR" => Tags['ROBOTS:FOLLOW', 'FOO:BAR']
              let k = arr[i].substr(0, arr[i].indexOf(':'));
              let v = arr[i].substr(arr[i].indexOf(':') + 1);

              if(v && v.startsWith(':')){
                // case 1
                backup[`${key}::${num}`] = k;
                num++;
              }else{
                // case 2
                backup[`${key}:${k}`] = v ? v : 'DELETE';
              }
            }

            // save the file to be edited array data
            oldData[key] = res.data[0][key];
          }else{
            // save the file to be edited non-array data and back it up
            oldData[key] = res.data[0][key];
            backup[key] = (res.data[0][key]).trim() ? res.data[0][key] : 'DELETE';
          }
        }else{
          backup[key] = 'DELETE';
        }
      }
    }

    // add the file to be edited data to our file data
    result[indx]['zzz_original'] = oldData;
    $vm.data = result;

    // push the file to be edited data to our file data's revertFile property array
    $vm.revertFile.push( Object.assign({}, backup, { Path: result[indx]['zzz_path'] }) );
    $vm.importCount++;
  }else if(res.error){
    // there was an error, show it
    $vm.showErrorMessage('Read Reply Error', res.error);
  }else{
    // this was a test, log it
    console.log(res.data[0]);
  }
});

// once a file has been written update the status regarding wether the operation was successful
ipcRenderer.on('exiftool-write-reply', (event, res, indx) => {
  if(res.error && res.error !== '1 image files updated'){
    //$vm.showErrorMessage('Write Reply Error', res.error);
    $vm.errorLog.push(res.error);
    $vm.updateListItemStatus(indx, false);
  }else{
    $vm.updateListItemStatus(indx, true);
  }
});

// test read file
ipcRenderer.on('test-read-file', (event) => {
  console.log('Test file being read...');
  $vm.readMetaAsync('exiftool-read', './test/test.pdf', -1);
});

// if the backup was successful then update the files
ipcRenderer.on('save-backup-reply', (event, res) => {
  if(!res.error && res){
    $vm.updateFiles();
  }
});
<commit_msg>ignore array key if value is empty<commit_after>/*
================
 CQ5 properties
================
 .data["0"].Title (String)
 .data["0"].Description (String)
 .data["0"].Tags (Array)
 .data["0"].Creator (String)
 .data["0"].Contributor (String)
 .data["0"].Language (String)
 .data["0"]["Rights-x-repair"] (String)
 .data["0"].Owner (String)
 .data["0"].ExpirationDate (2017:05:19 03:15-05:00) => (5/19/17 3:15 AM)
================
*/

// variables
const isDevelopment = process.env.NODE_ENV && `${process.env.NODE_ENV}`.trim() !== 'production';
const vueModule = isDevelopment ? '../node_modules/vue/dist/vue.js' : '../node_modules/vue/dist/vue.min.js'

const { ipcRenderer } = require('electron');
const csv = require('csvtojson');
const Vue = require(vueModule);

let $vm;

// options for vue instance
function getInitialData(){
  return {
    revertFile: [],
    conversionStarted: false,
    csvCache: null,
    csvDir: null,
    csvFileCount: 0,
    csvFilesProcessed: 0,
    csvFilesSeen: 0,
    csvMaxSizeMB: 30,
    csvName: null,
    csvPath: null,
    csvSize: null,
    data: [],
    errorLog: [],
    filesSkipped: 0,
    hovering: false,
    importCount: 0,
    message: '',
    page: 1,
    title: '...',
    validFileCount: 0
  };
}

// set up drag and drop functionality
window.addEventListener('dragover', e =>  $vm.dragcheck(e) );
window.addEventListener('dragenter', e =>  $vm.dragcheck(e) );
window.addEventListener('dragleave', e =>  $vm.dragcheck(e) );
window.addEventListener('dragend', e =>  $vm.dragcheck(e) );

window.addEventListener('drop', (e) => {
  $vm.dragcheck(e);
  $vm.hovering = false;
});

// vue instance
$vm = new Vue({
  el: '.container',
  data: getInitialData(),
  methods: {
    // saves a copy of the data from the selected csv to state
    cacheFile(data){
      // remove items with no Path property
      data = data.filter((val, indx) => {
        return val.hasOwnProperty('Path');
      });

      this.csvCache = data;
      this.csvFileCount = data.length;
    },
    // creates a backup of current files metadata before editing them
    createBackup(){
      this.conversionStarted = true;
      ipcRenderer.send('save-backup', this.revertFile, this.csvDir);
    },
    // drag and drop visual feedback
    dragcheck(e){
      if(e.target.type === 'file'){
        this.hovering = true;
      }else{
        e.preventDefault();
        this.hovering = false;
      }
    },
    // gets the extension of files
    getExtension(fileName){
      if(!fileName){
        return null;
      }
      return fileName.split('.').pop().substr(0, 3).toLowerCase();
    },
    // generates html for file icon
    getIcon(extension){
      let fileType;

      switch(extension){
        case 'pdf':
          fileType = 'file-pdf-o';
          break;
        case null:
          fileType = 'chain-broken';
          break;
        default:
          fileType = 'file-o';
      }

      return `
      `;
    },
    // validates the csv and then caches it
    getInputFile(e){
      let { name, path, size } = e.target.files[0];
      let message;
      let pathLength;
      let dir;

      if(!name.endsWith('.csv')){
        message = 'file must be a csv';
      }else if(size >= this.csvMaxSizeMB * 1000000){
        message = `file must be less than ${this.csvMaxSizeMB}MB`;
      }

      if(message){
        this.message = message;
        document.querySelector('.drop-box').reset();
        this.csvName = null;
        this.csvPath = null;
        this.csvDir = null;
        this.csvSize = null;
        return false;
      }

      pathLength = path.length - name.length;
      dir = path.slice(0, pathLength);

      this.csvName = name;
      this.csvPath = path;
      this.csvDir = dir;
      this.csvSize = size;
      this.message = `"${name}" has been selected!`;
      this.page = 2;
    },
    // process cached csv data and then read in the files
    importFile(){
      this.page = 4;

      this.processCache()
        .then((data) => this.data = data)
        .then(() => this.readImportedFiles());
    },
    // reveals the file in the explorer
    openWithShell(filePath){
      ipcRenderer.send('show-file', filePath);
    },
    // validate csv and then cache it
    preview(){
      this.page++;
      let errorMessage = '';

      if(!this.csvPath){
        errorMessage = "File required: You must select a file";
      }else if(!this.csvName.endsWith('.csv')){
        errorMessage = `Invalid extension: File must end with ".csv"`;
      }else if(this.csvSize > this.csvMaxSizeMB * 1000000){
        errorMessage = `File too big: Please limit files to ${this.csvMaxSizeMB}MB`;
      }

      if(!errorMessage){
        this.readCsvData(csv, this.csvPath)
          .then((data) => this.cacheFile(data));
      }else{
        this.showErrorMessage('Preview Error', errorMessage);
      }
    },
    // mutates and returns a copy of the selected csv's data for use in processing
    processCache(){
      return new Promise((resolve, reject) => {
        let result;

        try{
          result = this.csvCache.map((doc, indx) => {
            let name;
            let path;
            let extension;
            let val;

            name = doc['Path'];
            delete doc['Path'];
            path = this.csvDir;
            extension = this.getExtension(name);

            doc['zzz_id'] = indx;
            doc['zzz_path'] = name;
            doc['zzz_fullPath'] = `${path}${name}`;
            doc['zzz_extension'] = extension;
            doc['zzz_icon'] = this.getIcon(extension);
            doc['zzz_processedStatus'] = extension === 'pdf' ? null : false;
            doc['zzz_original'] = null;
            doc['zzz_showDetails'] = false;

            /*
            * if the key contains a : then its an array prop:
            * Tags::1: "ONE", Tags::2: "TWO", Tags::3: "THREE" => Tags["ONE", "TWO", "THREE"]
            * Tags:ROBOTS: "FOLLOW" => Tags['ROBOTS:FOLLOW']
            */
            Object.keys(doc).map((key, n) => {
              val = '';

              if(key.includes(':')){
                let baseKey = key.substr(0, key.indexOf(':'));
                let propName = key.substr(key.indexOf(':') + 1);

                if(!doc[baseKey]){
                  doc[baseKey] = [];
                }

                if(propName && propName.startsWith(':')){
                  propName = '';
                }

                if(doc[key] !== 'DELETE' && `${doc[key]}`.trim()){
                  val = `${propName ? propName + ':' : ''}${doc[key]}`;
                }

                if(val){
                  doc[baseKey].push(val);
                }
                delete doc[key];
              }
            });

            return doc;
          });

          resolve(result);
        }catch(e){
          reject(e);
        }

      });
    },
    // reads the csv file in as a json object
    readCsvData(npmModule, csvFilePath){
      return new Promise((resolve, reject) => {
        let result = [];

        npmModule()
          .fromFile(csvFilePath)
          .on('json', (jsonObj) => {
            result.push(jsonObj);
          })
          .on('done', (error) => {
            if(error){
              reject(error);
            }
            resolve(result);
          });
      });
    },
    // reads meta from the files listed in the csv
    readImportedFiles(){
      return new Promise((resolve, reject) => {
        let data = this.data;
        let filePath;

        try{
          for(let i = 0, len = data.length; i < len; i++){
            filePath = data[i]['zzz_fullPath'];
            this.readMetaAsync('exiftool-read', filePath, i);
          };
          resolve(true);
        }catch(e){
          reject(e);
        }
      });
    },
    // make an asynchronus request
    readMetaAsync(channel, filePath, indx){
      ipcRenderer.send(channel, filePath, indx);
    },
    // reset app to default state
    reset(){
      Object.assign(this.$data, getInitialData());
    },
    // toggles display of file details
    toggleDetails(e, indx){
      let obj = Object.assign({}, this.data);
      let bool = !obj[indx]['zzz_showDetails'];

      if (e.ctrlKey){;
        for(let i = 0, len = Object.keys(obj).length; i < len; i++){
          obj[i]['zzz_showDetails'] = bool;
        }
      }else{
        obj[indx]['zzz_showDetails'] = bool;
      }

      this.data = obj;
    },
    // show and error message
    showErrorMessage(title, err){
      ipcRenderer.send('show-error', title, err);
    },
    // modifies the metadata of all files in the list
    updateFiles(){
      let data;
      let filePath;
      let status;
      let len = Object.keys(this.data).length;
      let arr = Object.assign({}, this.data);
      let val;

      for(let i = 0; i < len; i++){
        data = {};
        filePath = arr[i]['zzz_fullPath'];
        processedStatus = arr[i]['zzz_processedStatus'];

        if(!filePath || processedStatus === false){
          this.filesSkipped++;
          this.csvFilesSeen++;
          continue;
        }

        Object.keys(arr[i]).forEach((key) => {
          val = arr[i][key];
          if((typeof val == 'string' || val instanceof String) && val === 'DELETE'){
            data[key] = ' ';
          }else if(val && !key.toLowerCase().startsWith('zzz_')){
            data[key] = val;
          }
        });

        this.updateMeta(filePath, data, i);
      }
    },
    // update the status flag of items that were reviewed and updated or skipped
    updateListItemStatus(indx, updateStatus){
      let itemToUpdate = Object.assign({}, this.data[indx]);
      itemToUpdate.zzz_processedStatus = updateStatus;
      $vm.$set(this.data, indx, itemToUpdate);
      if(updateStatus){
        this.csvFilesProcessed++;
      }else{
        this.filesSkipped++;
      }

      this.csvFilesSeen++;
    },
    // write to file with exiftool
    updateMeta(filePath, data, indx){
      ipcRenderer.send('exiftool-write', filePath, data, indx);
    }
  },
  computed: {
    info: function(){
      return {
        sourceFile: {
          name: 'Source',
          value: this.csvName || 'No file selected'
        },
        fileCount: {
          name: 'File Count',
          value: this.csvFileCount || 0
        },
        filesProcessed: {
          name: 'Files Updated',
          value: this.csvFilesProcessed || 0
        },
        filesSkipped: {
          name: 'Files Skipped',
          value: this.filesSkipped
        }
      };
    },
    isDone: function(){
      let isDone = this.conversionStarted && this.csvFilesSeen && this.csvFilesSeen === this.csvFileCount;

      if(isDone && this.errorLog.length){
        ipcRenderer.send('log-errors', this.errorLog);
      }

      return isDone;
    }
  }
});

// electron process event listeners
// once file is read, check it and update the array
ipcRenderer.on('exiftool-read-reply', (event, res, indx) => {
  let result;
  let keys;
  let key;
  let backup = {};
  let oldData = {};

  if(res.error && indx !== -1 || indx >= 0){
    // get the data for all files
    result = Object.assign({}, $vm.data);

    if(res.error){
      // update problem file with error info
      result[indx]['zzz_path'] = res.error;
      result[indx]['zzz_icon'] = $vm.getIcon(null);
      $vm.errorLog.push(res.error);
    }else{
      // if pdf, mark as valid
      if(result[indx]['zzz_extension'] === 'pdf'){
        $vm.validFileCount++;
      }

      // get keys and remove zzz keys
      keys = Object.keys(result[indx]);
      keys = keys.filter((key) => {
        return !key.startsWith('zzz_');
      });

      // for all keys in stored data...
      // (if the value is not present it should be set to DELETE in the backup file)
      for(let i = 0, len = keys.length; i < len; i++){

        key = keys[i];

        // if the file to be edited has the key...
        if(res.data[0].hasOwnProperty(key)){

          // ... and the value is an array...
          if(Array.isArray(res.data[0][key])){
            let arr = res.data[0][key];
            let num = 1;

            // loop through each array item and parse out the values for the backup file
            for(let i = 0, len = arr.length; i < len; i++){

              // Case 1 = Tags::1: "ROBOTS:FOLLOW", Tags::2: "FOO:BAR" => Tags['ROBOTS:FOLLOW', 'FOO:BAR']
              // Case 2 = Tags:ROBOTS: "FOLLOW", Tags:FOO: "BAR" => Tags['ROBOTS:FOLLOW', 'FOO:BAR']
              let k = arr[i].substr(0, arr[i].indexOf(':'));
              let v = arr[i].substr(arr[i].indexOf(':') + 1);

              if(v && v.startsWith(':')){
                // case 1
                backup[`${key}::${num}`] = k;
                num++;
              }else{
                // case 2
                backup[`${key}:${k}`] = v ? v : 'DELETE';
              }
            }

            // save the file to be edited array data
            oldData[key] = res.data[0][key];
          }else{
            // save the file to be edited non-array data and back it up
            oldData[key] = res.data[0][key];
            backup[key] = `${res.data[0][key]}`.trim() ? res.data[0][key] : 'DELETE';
          }
        }else{
          backup[key] = 'DELETE';
        }
      }
    }

    // add the file to be edited data to our file data
    result[indx]['zzz_original'] = oldData;
    $vm.data = result;

    // push the file to be edited data to our file data's revertFile property array
    $vm.revertFile.push( Object.assign({}, backup, { Path: result[indx]['zzz_path'] }) );
    $vm.importCount++;
  }else if(res.error){
    // there was an error, show it
    $vm.showErrorMessage('Read Reply Error', res.error);
  }else{
    // this was a test, log it
    console.log(res.data[0]);
  }
});

// once a file has been written update the status regarding wether the operation was successful
ipcRenderer.on('exiftool-write-reply', (event, res, indx) => {
  if(res.error && res.error !== '1 image files updated'){
    //$vm.showErrorMessage('Write Reply Error', res.error);
    $vm.errorLog.push(res.error);
    $vm.updateListItemStatus(indx, false);
  }else{
    $vm.updateListItemStatus(indx, true);
  }
});

// test read file
ipcRenderer.on('test-read-file', (event) => {
  console.log('Test file being read...');
  $vm.readMetaAsync('exiftool-read', './test/test.pdf', -1);
});

// if the backup was successful then update the files
ipcRenderer.on('save-backup-reply', (event, res) => {
  if(!res.error && res){
    $vm.updateFiles();
  }
});
<|endoftext|>


<commit_before>var TWEEN;

require("raf.js");

// Just here to simplify the initialization logic.  If
// window doesn't exist, this module is useless anyway
if(typeof window === 'undefined') { window = {}; }

// The build will declare TWEEN as external. However, if it isn't provided by
// browserify, we really want to check to see if it was included directly via
// script tag first.  Only if it isn't will we try a require.  This *should*
// make it easier to bundle/or not and to use with requirejs...
TWEEN = window.TWEEN || require("tween.js");

function Scroll(viewOrSelector, listener) {
    var view,
        min, max, offset, reference, pressed,
        velocity, frame, timestamp, ticker,
        amplitude, target, timeConstant, innerHeight;

    var p0 = { y: 0 };
    var t0 = false;

    function ypos(e) {
        // touch event
        if (e.targetTouches && (e.targetTouches.length >= 1)) {
            return e.targetTouches[0].clientY;
        }

        // mouse event
        return e.clientY;
    }

    function scroll(y) {
        offset = (y > max) ? max : (y < min) ? min : y;

        view.scrollTop = offset;
        listener.call(view);
    }

    function track() {
        var now, elapsed, delta, v;

        now = Date.now();
        elapsed = now - timestamp;
        timestamp = now;
        delta = offset - frame;
        frame = offset;

        v = 1000 * delta / (1 + elapsed);
        velocity = 0.8 * v + 0.2 * velocity;
    }

    function tick() {
        TWEEN.update();
    }

    function tap(e) {
        pressed = true;
        reference = ypos(e);

        velocity = amplitude = 0;
        frame = offset;
        timestamp = Date.now();
        clearInterval(ticker);
        ticker = setInterval(track, 100);

        if(t0) {
            t0.stop();
            t0 = false;
        }
    }

    function drag(e) {
        var y, delta;
        if (pressed) {
            y = ypos(e);
            delta = reference - y;
            if (delta > 2 || delta < -2) {
                reference = y;
                scroll(offset + delta);
            }
        }
        e.preventDefault();
    }

    function release(e) {
        pressed = false;

        clearInterval(ticker);

        // If no velocity yet, track once make sure
        if(velocity === 0) { track(); }

        if (velocity > 10 || velocity < -10) {
            amplitude = 0.8 * velocity;
            target = Math.round(offset + amplitude);
            timestamp = Date.now();

            p0.y = view.scrollTop;
            t0 = new TWEEN.Tween(p0)
                .to({y: target}, timeConstant)
                .easing(TWEEN.Easing.Quintic.Out)
                .onUpdate(function() {
                    scroll(p0.y);
                    window.requestAnimationFrame(tick);
                })
                .onComplete(function() {
                    scroll(p0.y);
                    t0.stop();
                    t0 = false;
                });

            t0.start();
            window.requestAnimationFrame(tick);
        }
    }

    view = typeof viewOrSelector === 'string' ? document.querySelector(viewOrSelector) : viewOrSelector;
    if (typeof window.ontouchstart !== 'undefined') {
        view.addEventListener('touchstart', tap);
        view.addEventListener('touchmove', drag);
        view.addEventListener('touchend', release);
    }

    this.unbind = function detach() {
        if (typeof window.ontouchstart !== 'undefined') {
            view.removeEventListener('touchstart', tap);
            view.removeEventListener('touchmove', drag);
            view.removeEventListener('touchend', release);
        }
    };

    max = parseInt(window.getComputedStyle(view).height, 10) - innerHeight;
    offset = min = 0;
    pressed = false;
    timeConstant = 2000; // ms
}

module.exports = Scroll;
<commit_msg>Clean up jshint error that was causing travis failure<commit_after>var TWEEN;

require("raf.js");

// Just here to simplify the initialization logic.  If
// window doesn't exist, this module is useless anyway
if(typeof window === 'undefined') { window = {}; }

// The build will declare TWEEN as external. However, if it isn't provided by
// browserify, we really want to check to see if it was included directly via
// script tag first.  Only if it isn't will we try a require.  This *should*
// make it easier to bundle/or not and to use with requirejs...
TWEEN = window.TWEEN || require("tween.js");

function Scroll(viewOrSelector, listener) {
    var view,
        min, max, offset, reference, pressed,
        velocity, frame, timestamp, ticker,
        amplitude, target, timeConstant, innerHeight;

    var p0 = { y: 0 };
    var t0 = false;

    function ypos(e) {
        // touch event
        if (e.targetTouches && (e.targetTouches.length >= 1)) {
            return e.targetTouches[0].clientY;
        }

        // mouse event
        return e.clientY;
    }

    function scroll(y) {
        offset = (y > max) ? max : (y < min) ? min : y;

        view.scrollTop = offset;
        listener.call(view);
    }

    function track() {
        var now, elapsed, delta, v;

        now = Date.now();
        elapsed = now - timestamp;
        timestamp = now;
        delta = offset - frame;
        frame = offset;

        v = 1000 * delta / (1 + elapsed);
        velocity = 0.8 * v + 0.2 * velocity;
    }

    function tick() {
        TWEEN.update();
    }

    function tap(e) {
        pressed = true;
        reference = ypos(e);

        velocity = amplitude = 0;
        frame = offset;
        timestamp = Date.now();
        clearInterval(ticker);
        ticker = setInterval(track, 100);

        if(t0) {
            t0.stop();
            t0 = false;
        }
    }

    function drag(e) {
        var y, delta;
        if (pressed) {
            y = ypos(e);
            delta = reference - y;
            if (delta > 2 || delta < -2) {
                reference = y;
                scroll(offset + delta);
            }
        }
        e.preventDefault();
    }

    function release(/*e*/) {
        pressed = false;

        clearInterval(ticker);

        // If no velocity yet, track once make sure
        if(velocity === 0) { track(); }

        if (velocity > 10 || velocity < -10) {
            amplitude = 0.8 * velocity;
            target = Math.round(offset + amplitude);
            timestamp = Date.now();

            p0.y = view.scrollTop;
            t0 = new TWEEN.Tween(p0)
                .to({y: target}, timeConstant)
                .easing(TWEEN.Easing.Quintic.Out)
                .onUpdate(function() {
                    scroll(p0.y);
                    window.requestAnimationFrame(tick);
                })
                .onComplete(function() {
                    scroll(p0.y);
                    t0.stop();
                    t0 = false;
                });

            t0.start();
            window.requestAnimationFrame(tick);
        }
    }

    view = typeof viewOrSelector === 'string' ? document.querySelector(viewOrSelector) : viewOrSelector;
    if (typeof window.ontouchstart !== 'undefined') {
        view.addEventListener('touchstart', tap);
        view.addEventListener('touchmove', drag);
        view.addEventListener('touchend', release);
    }

    this.unbind = function detach() {
        if (typeof window.ontouchstart !== 'undefined') {
            view.removeEventListener('touchstart', tap);
            view.removeEventListener('touchmove', drag);
            view.removeEventListener('touchend', release);
        }
    };

    max = parseInt(window.getComputedStyle(view).height, 10) - innerHeight;
    offset = min = 0;
    pressed = false;
    timeConstant = 2000; // ms
}

module.exports = Scroll;
<|endoftext|>


<commit_before>const AWS = require("aws-sdk");
const BbPromise = require("bluebird");
const _ = require("lodash");
const path = require("path");
const fs = require("fs");

// DynamoDB has a 25 item limit in batch requests
// https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html
const MAX_MIGRATION_CHUNK = 25;

// TODO: let this be configurable
const MIGRATION_SEED_CONCURRENCY = 5;

/**
 * Writes a batch chunk of migration seeds to DynamoDB. DynamoDB has a limit on the number of
 * items that may be written in a batch operation.
 * @param {DynamoDocumentClient} dynamodb The DynamoDB Document client
 * @param {string} tableName The table name being written to
 * @param {any[]} seeds The migration seeds being written to the table
 */
function writeSeedBatch(dynamodb, tableName, seeds) {
  const params = {
    RequestItems: {
      [tableName]: seeds.map((seed) => ({
        PutRequest: {
          Item: seed,
        },
      })),
    },
  };
  return new BbPromise((resolve, reject) => {
    // interval lets us know how much time we have burnt so far. This lets us have a backoff mechanism to try
    // again a few times in case the Database resources are in the middle of provisioning.
    let interval = 0;
    function execute(interval) {
      setTimeout(() => dynamodb.batchWrite(params, (err) => {
        if (err) {
          if (err.code === "ResourceNotFoundException" && interval <= 5000) {
            execute(interval + 1000);
          } else {
            reject(err);
          }
        } else {
          resolve();
        }
      }), interval);
    }
    execute(interval);
  });
}

/**
 * Writes a seed corpus to the given database table
 * @param {DocumentClient} dynamodb The DynamoDB document instance
 * @param {string} tableName The table name
 * @param {any[]} seeds The seed values
 */
function writeSeeds(dynamodb, tableName, seeds) {
  if (!dynamodb) {
    throw new Error("dynamodb argument must be provided");
  }
  if (!tableName) {
    throw new Error("table name argument must be provided");
  }
  if (!seeds) {
    throw new Error("seeds argument must be provided");
  }

  if (seeds.length > 0) {
    const seedChunks = _.chunk(seeds, MAX_MIGRATION_CHUNK);
    return BbPromise.map(
      seedChunks,
      (chunk) => writeSeedBatch(dynamodb, tableName, chunk),
      { concurrency: MIGRATION_SEED_CONCURRENCY }
    )
      .then(() => console.log("Seed running complete for table: " + tableName));
  }
}

/**
 * A promise-based function that determines if a file exists
 * @param {string} fileName The path to the file
 */
function fileExists(fileName) {
  return new BbPromise((resolve) => {
    fs.exists(fileName, (exists) => resolve(exists));
  });
}

/**
 * Scrapes seed files out of a given location. This file may contain
 * either a simple json object, or an array of simple json objects. An array
 * of json objects is returned.
 *
 * @param {any} location the filename to read seeds from.
 */
function getSeedsAtLocation(location) {
  // load the file as JSON
  const result = require(location);

  // Ensure the output is an array
  if (Array.isArray(result)) {
    return result;
  } else {
    return [ result ];
  }
}

/**
 * Locates seeds given a set of files to scrape
 * @param {string[]} sources The filenames to scrape for seeds
 */
function locateSeeds(sources = [], cwd = process.cwd()) {
  const locations = sources.map(source => path.join(cwd, source));
  return BbPromise.map(locations, (location) => {
    return fileExists(location).then((exists) => {
      if(!exists) {
        throw new Error("source file " + location + " does not exist");
      }
      return getSeedsAtLocation(location);
    });
  // Smash the arrays together
  }).then((seedArrays) => [].concat.apply([], seedArrays));
}

module.exports = { writeSeeds, locateSeeds };
<commit_msg>Correcting minor stylistic issue that codacy reported<commit_after>const AWS = require("aws-sdk");
const BbPromise = require("bluebird");
const _ = require("lodash");
const path = require("path");
const fs = require("fs");

// DynamoDB has a 25 item limit in batch requests
// https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html
const MAX_MIGRATION_CHUNK = 25;

// TODO: let this be configurable
const MIGRATION_SEED_CONCURRENCY = 5;

/**
 * Writes a batch chunk of migration seeds to DynamoDB. DynamoDB has a limit on the number of
 * items that may be written in a batch operation.
 * @param {DynamoDocumentClient} dynamodb The DynamoDB Document client
 * @param {string} tableName The table name being written to
 * @param {any[]} seeds The migration seeds being written to the table
 */
function writeSeedBatch(dynamodb, tableName, seeds) {
  const params = {
    RequestItems: {
      [tableName]: seeds.map((seed) => ({
        PutRequest: {
          Item: seed,
        },
      })),
    },
  };
  return new BbPromise((resolve, reject) => {
    // interval lets us know how much time we have burnt so far. This lets us have a backoff mechanism to try
    // again a few times in case the Database resources are in the middle of provisioning.
    let interval = 0;
    function execute(interval) {
      setTimeout(() => dynamodb.batchWrite(params, (err) => {
        if (err) {
          if (err.code === "ResourceNotFoundException" && interval <= 5000) {
            execute(interval + 1000);
          } else {
            reject(err);
          }
        } else {
          resolve();
        }
      }), interval);
    }
    execute(interval);
  });
}

/**
 * Writes a seed corpus to the given database table
 * @param {DocumentClient} dynamodb The DynamoDB document instance
 * @param {string} tableName The table name
 * @param {any[]} seeds The seed values
 */
function writeSeeds(dynamodb, tableName, seeds) {
  if (!dynamodb) {
    throw new Error("dynamodb argument must be provided");
  }
  if (!tableName) {
    throw new Error("table name argument must be provided");
  }
  if (!seeds) {
    throw new Error("seeds argument must be provided");
  }

  if (seeds.length > 0) {
    const seedChunks = _.chunk(seeds, MAX_MIGRATION_CHUNK);
    return BbPromise.map(
      seedChunks,
      (chunk) => writeSeedBatch(dynamodb, tableName, chunk),
      { concurrency: MIGRATION_SEED_CONCURRENCY }
    )
      .then(() => console.log("Seed running complete for table: " + tableName));
  }
}

/**
 * A promise-based function that determines if a file exists
 * @param {string} fileName The path to the file
 */
function fileExists(fileName) {
  return new BbPromise((resolve) => {
    fs.exists(fileName, (exists) => resolve(exists));
  });
}

/**
 * Scrapes seed files out of a given location. This file may contain
 * either a simple json object, or an array of simple json objects. An array
 * of json objects is returned.
 *
 * @param {any} location the filename to read seeds from.
 */
function getSeedsAtLocation(location) {
  // load the file as JSON
  const result = require(location);

  // Ensure the output is an array
  if (Array.isArray(result)) {
    return result;
  } else {
    return [ result ];
  }
}

/**
 * Locates seeds given a set of files to scrape
 * @param {string[]} sources The filenames to scrape for seeds
 */
function locateSeeds(sources = [], cwd = process.cwd()) {
  const locations = sources.map((source) => path.join(cwd, source));
  return BbPromise.map(locations, (location) => {
    return fileExists(location).then((exists) => {
      if(!exists) {
        throw new Error("source file " + location + " does not exist");
      }
      return getSeedsAtLocation(location);
    });
  // Smash the arrays together
  }).then((seedArrays) => [].concat.apply([], seedArrays));
}

module.exports = { writeSeeds, locateSeeds };
<|endoftext|>


<commit_before>Ext.namespace("cron");

cron.application = {
    init: function ()
    {
        var loadMaskCron = new Ext.LoadMask(Ext.getBody(), {msg: _("ID_LOADING_GRID")});

        cronProcessAjax = function (option)
        {
            var p;

            switch (option) {
                case "EMPTY":
                    p = {
                        "option": option
                    };
                    break;
            }

            Ext.Ajax.request({
                url: "cronAjax",
                method: "POST",
                params: p,

                success: function (response, opts)
                {
                    var dataResponse = eval("(" + response.responseText + ")"); //json

                    switch (option) {
                        case "EMPTY":
                            if (dataResponse.status && dataResponse.status == "OK") {
                                pagingCron.moveFirst();
                            }
                            break;
                    }
                },
                failure: function (response, opts)
                {
                    //
                }
            });
        }

        infoView = function ()
        {
            var strData = "" + _("ID_CRON_INFO") + "
";
            strData = strData + "" + _("ID_STATUS") + ": " + CRON.status + "
";
            strData = strData + "" + _("ID_EVENT_LAST_EXECUTION_DATE") + ": " + CRON.lastExecution + "

";
            strData = strData + "" + _("ID_LOG_INFO") + "
";
            strData = strData + "" + _("ID_FILENAME") + ": " + CRON.fileLogName + "
";
            strData = strData + "" + _("ID_SIZE") + ": " + CRON.fileLogSize + " MB
";
            strData = strData + "" + _("ID_PATH") + ": " + CRON.fileLogPath + "
";

            var formItems = Ext.getCmp("frmInfoView").form.items;
            formItems.items[0].setValue(strData);

            winInfo.show();
        }

        logView = function ()
        {
            var record = grdpnlMain.getSelectionModel().getSelected();

            var strData = "" + _("ID_DATE_LABEL") + "
" + record.get("DATE") + "
";
            strData = strData + "" + _("ID_WORKSPACE") + "
" + record.get("WORKSPACE") + "
";
            strData = strData + "" + _("ID_ACTION") + "
" + record.get("ACTION") + "
";
            strData = strData + "" + _("ID_STATUS") + "
" + record.get("STATUS") + "
";
            strData = strData + "" + _("ID_DESCRIPTION") + "
" + record.get("DESCRIPTION") + "
";

            var formItems = Ext.getCmp("frmLogView").form.items;
            formItems.items[0].setValue(strData);

            winLog.setTitle("Log - " + _("ID_WORKSPACE") + " " + record.get("WORKSPACE"));
            winLog.show();
        }

        //Variables
        var pageSize = parseInt(CONFIG.pageSize);

        var expander = new Ext.ux.grid.RowExpander({
            tpl: new Ext.Template(
                "" + _("ID_DESCRIPTION") + "
{DESCRIPTION}"
            )
        });

        //Stores
        var storeCron = new Ext.data.Store({
            proxy: new Ext.data.HttpProxy({
                url: "cronAjax",
                method: "POST"
            }),

            reader: new Ext.data.JsonReader({
                root: "resultRoot",
                totalProperty: "resultTotal",
                fields: [
                    {name: "DATE"},
                    {name: "WORKSPACE"},
                    {name: "ACTION"},
                    {name: "STATUS"},
                    {name: "DESCRIPTION"}
                ]
            }),

            //autoLoad: true, //First call

            listeners: {
                beforeload: function (store)
                {
                    loadMaskCron.show();

                    this.baseParams = {
                        "option": "LST",
                        "pageSize": pageSize,
                        "workspace": Ext.getCmp("cboWorkspace").getValue(),
                        "status": Ext.getCmp("cboStatus").getValue(),
                        "dateFrom": Ext.getCmp("dateFrom").getValue(),
                        "dateTo": Ext.getCmp("dateTo").getValue()
                    };

                    //btnView.setDisabled(true);
                },
                load: function (store, record, opt)
                {
                    loadMaskCron.hide();
                }
            }
        });

        var storeWorkspace = new Ext.data.ArrayStore({
            idIndex: 0,
            fields: ["id", "value"],
            data: WORKSPACE
        });

        var storeStatus = new Ext.data.ArrayStore({
            idIndex: 0,
            fields: ["id", "value"],
            data: STATUS
        });

        var storePageSize = new Ext.data.SimpleStore({
            fields: ["size"],
            data: [["20"], ["30"], ["40"], ["50"], ["100"]],
            autoLoad: true
        });

        //Components
        var dateFrom = new Ext.form.DateField({
            id: "dateFrom",

            format: "Y-m-d",
            editable: false,
            width: 90,
            value: ""
        });

        var dateTo = new Ext.form.DateField({
            id: "dateTo",

            format: "Y-m-d",
            editable: false,
            width: 90,
            value: ""
        });

        var cboWorkspace = new Ext.form.ComboBox({
            id: "cboWorkspace",

            valueField: "id",
            displayField: "value",
            value: "ALL",
            store: storeWorkspace,

            triggerAction: "all",
            mode: "local",
            editable: false,

            width: 150,

            listeners: {
                select: function (combo, record, index)
                {
                    pagingCron.moveFirst();
                }
            }
        });

        var cboStatus = new Ext.form.ComboBox({
            id: "cboStatus",

            valueField: "id",
            displayField: "value",
            value: "ALL",
            store: storeStatus,

            triggerAction: "all",
            mode: "local",
            editable: false,

            width: 90,

            listeners: {
                select: function (combo, record, index)
                {
                    pagingCron.moveFirst();
                }
            }
        });

        var cboPageSize = new Ext.form.ComboBox({
            id: "cboPageSize",

            mode: "local",
            triggerAction: "all",
            store: storePageSize,
            valueField: "size",
            displayField: "size",
            width: 50,
            editable: false,

            listeners: {
                select: function (combo, record, index)
                {
                    pageSize = parseInt(record.data["size"]);

                    pagingCron.pageSize = pageSize;
                    pagingCron.moveFirst();
                }
            }
        });

        var btnInfoView = new Ext.Action({
            id: "btnInfoView",

            text: _("ID_VIEW_INFO"),
            iconCls: "button_menu_ext ss_sprite ss_zoom",

            handler: function ()
            {
                infoView();
            }
        });

        var btnLogClear = new Ext.Action({
            id: "btnLogClear",

            text: _("ID_CLEAR_LOG"),
            iconCls: "button_menu_ext ss_sprite ss_bin_empty",

            handler: function ()
            {
                Ext.MessageBox.confirm(
                    _("ID_CONFIRM"),
                    _("ID_CRON_LOG_CLEAR"),
                    function (btn, text)
                    {
                        if (btn == "yes") {
                            cronProcessAjax("EMPTY");
                        }
                    }
                );
            }
        });

        /*
        var btnView = new Ext.Action({
            id: "btnView",

            text: _("ID_VIEW"),
            iconCls: "button_menu_ext ss_sprite ss_zoom",
            disabled: true,

            handler: function ()
            {
                logView();
            }
        });
        */

        var pagingCron = new Ext.PagingToolbar({
            id: "pagingCron",

            pageSize: pageSize,
            store: storeCron,
            displayInfo: true,
            displayMsg: _("ID_CRON_GRID_PAGE_DISPLAYING_MESSAGE"),
            emptyMsg: _("ID_NO_RECORDS_FOUND"),
            items: ["-", _("ID_PAGE_SIZE") + " ", cboPageSize]
        });

        var cmodel = new Ext.grid.ColumnModel({
            defaults: {
                width: 50,
                sortable: true
            },
            columns: [
                expander,
                {id: "ID", dataIndex: "DATE", hidden: true, hideable: false},
                {header: _("ID_DATE_LABEL"), dataIndex: "DATE", width: 10, align: "center"},
                {header: _("ID_WORKSPACE"), dataIndex: "WORKSPACE", width: 10},
                {header: _("ID_ACTION"), dataIndex: "ACTION", width: 10},
                {header: _("ID_STATUS"), dataIndex: "STATUS", width: 7, align: "center"},
                {header: _("ID_DESCRIPTION"), dataIndex: "DESCRIPTION"}
            ]
        });

        var smodel = new Ext.grid.RowSelectionModel({
            singleSelect: true,
            listeners: {
                rowselect: function (sm)
                {
                    //btnView.setDisabled(false);
                },
                rowdeselect: function (sm)
                {
                    //btnView.setDisabled(true);
                }
            }
        });

        var winInfo = new Ext.Window({
            layout: "fit",
            width: 500,
            height: 250,
            title: _("ID_CRON_INFO"),
            modal: true,
            closeAction: "hide",

            items: [
                new Ext.FormPanel({
                    id: "frmInfoView",

                    frame: true,
                    labelAlign: "right",
                    labelWidth: 1,
                    autoWidth: true,
                    //height: 395,
                    autoScroll: true,
                    items: [
                        {
                            xtype: "displayfield",
                            fieldLabel: ""
                        }
                    ]
                })
            ]
        });

        var winLog = new Ext.Window({
            layout: "fit",
            width: 500,
            height: 250,
            title: "",
            modal: true,
            closeAction: "hide",

            items: [
                new Ext.FormPanel({
                    id: "frmLogView",

                    frame: true,
                    labelAlign: "right",
                    labelWidth: 1,
                    autoWidth: true,
                    //height: 395,
                    autoScroll: true,
                    items: [
                        {
                            xtype: "displayfield",
                            fieldLabel: ""
                        }
                    ]
                })
            ]
        });

        var grdpnlMain = new Ext.grid.GridPanel({
            id: "grdpnlMain",

            store: storeCron,
            colModel: cmodel,
            selModel: smodel,

            columnLines: true,
            viewConfig: {forceFit: true},
            enableColumnResize: true,
            enableHdMenu: false,
            plugins: expander,

            tbar: [
                {xtype: "tbtext", text: _("ID_CRON_STATUS") + ": "},
                {xtype: "tbtext", html: "" + CRON.status + ""},
                "-",
                btnInfoView,
                btnLogClear,
                //"-",
                //btnView,
                "->",
                {xtype: "tbtext", text: _("ID_WORKSPACE") + " "},
                cboWorkspace,
                "-",
                {xtype: "tbtext", text: _("ID_STATUS") + " "},
                cboStatus,
                "-",
                {xtype: "tbtext", text: _("ID_FROM") + " "},
                dateFrom,
                " ",
                {xtype: "tbtext", text: _("ID_TO") + " "},
                dateTo,
                " ",
                {
                    xtype: "button",
                    text: _("ID_FILTER"),

                    handler: function ()
                    {
                        pagingCron.moveFirst();
                    }
                },
                " ",
                {
                    xtype: "button",
                    text: _("ID_RESET"),

                    handler: function ()
                    {
                        Ext.getCmp("dateFrom").reset(),
                        Ext.getCmp("dateTo").reset()
                    }
                }
            ],
            bbar: pagingCron,

            title: _("ID_CRON_ACTIONS_LOG"),

            listeners: {
                rowdblclick: function ()
                {
                    logView();
                }
            }
        });

        //Initialize events
        storeCron.load();

        cboPageSize.setValue(pageSize);

        //Load all panels
        var viewport = new Ext.Viewport({
            layout: "fit",
            autoScroll: false,
            items: [grdpnlMain]
        });
    }
}

Ext.onReady(cron.application.init, cron.application);<commit_msg>BUG 9709 "Add a new option in ADMIN -> Logs for the cron" SOLVED<commit_after>Ext.namespace("cron");

cron.application = {
    init: function ()
    {
        var loadMaskCron = new Ext.LoadMask(Ext.getBody(), {msg: _("ID_LOADING_GRID")});

        cronProcessAjax = function (option)
        {
            var p;

            switch (option) {
                case "EMPTY":
                    p = {
                        "option": option
                    };
                    break;
            }

            Ext.Ajax.request({
                url: "cronAjax",
                method: "POST",
                params: p,

                success: function (response, opts)
                {
                    var dataResponse = eval("(" + response.responseText + ")"); //json

                    switch (option) {
                        case "EMPTY":
                            if (dataResponse.status && dataResponse.status == "OK") {
                                pagingCron.moveFirst();
                            }
                            break;
                    }
                },
                failure: function (response, opts)
                {
                    //
                }
            });
        }

        infoView = function ()
        {
            var strData = "" + _("ID_CRON_INFO") + "
";
            strData = strData + "" + _("ID_STATUS") + ": " + CRON.status + "
";
            strData = strData + "" + _("ID_EVENT_LAST_EXECUTION_DATE") + ": " + CRON.lastExecution + "

";
            strData = strData + "" + _("ID_LOG_INFO") + " (" + CRON.fileLogName + ")
";
            strData = strData + "" + _("ID_FILENAME") + ": " + CRON.fileLogName + "
";
            strData = strData + "" + _("ID_SIZE") + ": " + CRON.fileLogSize + " MB
";
            strData = strData + "" + _("ID_PATH") + ": " + CRON.fileLogPath + "
";

            var formItems = Ext.getCmp("frmInfoView").form.items;
            formItems.items[0].setValue(strData);

            winInfo.show();
        }

        logView = function ()
        {
            var record = grdpnlMain.getSelectionModel().getSelected();

            var strData = "" + _("ID_DATE_LABEL") + "
" + record.get("DATE") + "
";
            strData = strData + "" + _("ID_WORKSPACE") + "
" + record.get("WORKSPACE") + "
";
            strData = strData + "" + _("ID_ACTION") + "
" + record.get("ACTION") + "
";
            strData = strData + "" + _("ID_STATUS") + "
" + record.get("STATUS") + "
";
            strData = strData + "" + _("ID_DESCRIPTION") + "
" + record.get("DESCRIPTION") + "
";

            var formItems = Ext.getCmp("frmLogView").form.items;
            formItems.items[0].setValue(strData);

            winLog.setTitle("Log - " + _("ID_WORKSPACE") + " " + record.get("WORKSPACE"));
            winLog.show();
        }

        //Variables
        var pageSize = parseInt(CONFIG.pageSize);

        var expander = new Ext.ux.grid.RowExpander({
            tpl: new Ext.Template(
                "" + _("ID_DESCRIPTION") + "
{DESCRIPTION}"
            )
        });

        //Stores
        var storeCron = new Ext.data.Store({
            proxy: new Ext.data.HttpProxy({
                url: "cronAjax",
                method: "POST"
            }),

            reader: new Ext.data.JsonReader({
                root: "resultRoot",
                totalProperty: "resultTotal",
                fields: [
                    {name: "DATE"},
                    {name: "WORKSPACE"},
                    {name: "ACTION"},
                    {name: "STATUS"},
                    {name: "DESCRIPTION"}
                ]
            }),

            //autoLoad: true, //First call

            listeners: {
                beforeload: function (store)
                {
                    loadMaskCron.show();

                    this.baseParams = {
                        "option": "LST",
                        "pageSize": pageSize,
                        "workspace": Ext.getCmp("cboWorkspace").getValue(),
                        "status": Ext.getCmp("cboStatus").getValue(),
                        "dateFrom": Ext.getCmp("dateFrom").getValue(),
                        "dateTo": Ext.getCmp("dateTo").getValue()
                    };

                    //btnView.setDisabled(true);
                },
                load: function (store, record, opt)
                {
                    loadMaskCron.hide();
                }
            }
        });

        var storeWorkspace = new Ext.data.ArrayStore({
            idIndex: 0,
            fields: ["id", "value"],
            data: WORKSPACE
        });

        var storeStatus = new Ext.data.ArrayStore({
            idIndex: 0,
            fields: ["id", "value"],
            data: STATUS
        });

        var storePageSize = new Ext.data.SimpleStore({
            fields: ["size"],
            data: [["20"], ["30"], ["40"], ["50"], ["100"]],
            autoLoad: true
        });

        //Components
        var dateFrom = new Ext.form.DateField({
            id: "dateFrom",

            format: "Y-m-d",
            editable: false,
            width: 90,
            value: ""
        });

        var dateTo = new Ext.form.DateField({
            id: "dateTo",

            format: "Y-m-d",
            editable: false,
            width: 90,
            value: ""
        });

        var cboWorkspace = new Ext.form.ComboBox({
            id: "cboWorkspace",

            valueField: "id",
            displayField: "value",
            value: "ALL",
            store: storeWorkspace,

            triggerAction: "all",
            mode: "local",
            editable: false,

            width: 150,

            listeners: {
                select: function (combo, record, index)
                {
                    pagingCron.moveFirst();
                }
            }
        });

        var cboStatus = new Ext.form.ComboBox({
            id: "cboStatus",

            valueField: "id",
            displayField: "value",
            value: "ALL",
            store: storeStatus,

            triggerAction: "all",
            mode: "local",
            editable: false,

            width: 90,

            listeners: {
                select: function (combo, record, index)
                {
                    pagingCron.moveFirst();
                }
            }
        });

        var cboPageSize = new Ext.form.ComboBox({
            id: "cboPageSize",

            mode: "local",
            triggerAction: "all",
            store: storePageSize,
            valueField: "size",
            displayField: "size",
            width: 50,
            editable: false,

            listeners: {
                select: function (combo, record, index)
                {
                    pageSize = parseInt(record.data["size"]);

                    pagingCron.pageSize = pageSize;
                    pagingCron.moveFirst();
                }
            }
        });

        var btnInfoView = new Ext.Action({
            id: "btnInfoView",

            text: _("ID_VIEW_INFO"),
            iconCls: "button_menu_ext ss_sprite ss_zoom",

            handler: function ()
            {
                infoView();
            }
        });

        var btnLogClear = new Ext.Action({
            id: "btnLogClear",

            text: _("ID_CLEAR_LOG"),
            iconCls: "button_menu_ext ss_sprite ss_bin_empty",

            handler: function ()
            {
                Ext.MessageBox.confirm(
                    _("ID_CONFIRM"),
                    _("ID_CRON_LOG_CLEAR"),
                    function (btn, text)
                    {
                        if (btn == "yes") {
                            cronProcessAjax("EMPTY");
                        }
                    }
                );
            }
        });

        /*
        var btnView = new Ext.Action({
            id: "btnView",

            text: _("ID_VIEW"),
            iconCls: "button_menu_ext ss_sprite ss_zoom",
            disabled: true,

            handler: function ()
            {
                logView();
            }
        });
        */

        var pagingCron = new Ext.PagingToolbar({
            id: "pagingCron",

            pageSize: pageSize,
            store: storeCron,
            displayInfo: true,
            displayMsg: _("ID_CRON_GRID_PAGE_DISPLAYING_MESSAGE"),
            emptyMsg: _("ID_NO_RECORDS_FOUND"),
            items: ["-", _("ID_PAGE_SIZE") + " ", cboPageSize]
        });

        var cmodel = new Ext.grid.ColumnModel({
            defaults: {
                width: 50,
                sortable: true
            },
            columns: [
                expander,
                {id: "ID", dataIndex: "DATE", hidden: true, hideable: false},
                {header: _("ID_DATE_LABEL"), dataIndex: "DATE", width: 10, align: "center"},
                {header: _("ID_WORKSPACE"), dataIndex: "WORKSPACE", width: 10},
                {header: _("ID_ACTION"), dataIndex: "ACTION", width: 10},
                {header: _("ID_STATUS"), dataIndex: "STATUS", width: 7, align: "center"},
                {header: _("ID_DESCRIPTION"), dataIndex: "DESCRIPTION"}
            ]
        });

        var smodel = new Ext.grid.RowSelectionModel({
            singleSelect: true,
            listeners: {
                rowselect: function (sm)
                {
                    //btnView.setDisabled(false);
                },
                rowdeselect: function (sm)
                {
                    //btnView.setDisabled(true);
                }
            }
        });

        var winInfo = new Ext.Window({
            layout: "fit",
            width: 500,
            height: 250,
            title: _("ID_CRON_INFO"),
            modal: true,
            closeAction: "hide",

            items: [
                new Ext.FormPanel({
                    id: "frmInfoView",

                    frame: true,
                    labelAlign: "right",
                    labelWidth: 1,
                    autoWidth: true,
                    //height: 395,
                    autoScroll: true,
                    items: [
                        {
                            xtype: "displayfield",
                            fieldLabel: ""
                        }
                    ]
                })
            ]
        });

        var winLog = new Ext.Window({
            layout: "fit",
            width: 500,
            height: 250,
            title: "",
            modal: true,
            closeAction: "hide",

            items: [
                new Ext.FormPanel({
                    id: "frmLogView",

                    frame: true,
                    labelAlign: "right",
                    labelWidth: 1,
                    autoWidth: true,
                    //height: 395,
                    autoScroll: true,
                    items: [
                        {
                            xtype: "displayfield",
                            fieldLabel: ""
                        }
                    ]
                })
            ]
        });

        var grdpnlMain = new Ext.grid.GridPanel({
            id: "grdpnlMain",

            store: storeCron,
            colModel: cmodel,
            selModel: smodel,

            columnLines: true,
            viewConfig: {forceFit: true},
            enableColumnResize: true,
            enableHdMenu: false,
            plugins: expander,

            tbar: [
                {xtype: "tbtext", text: _("ID_CRON_STATUS") + ": "},
                {xtype: "tbtext", html: "" + CRON.status + ""},
                "-",
                btnInfoView,
                btnLogClear,
                //"-",
                //btnView,
                "->",
                {xtype: "tbtext", text: _("ID_WORKSPACE") + " "},
                cboWorkspace,
                "-",
                {xtype: "tbtext", text: _("ID_STATUS") + " "},
                cboStatus,
                "-",
                {xtype: "tbtext", text: _("ID_FROM") + " "},
                dateFrom,
                " ",
                {xtype: "tbtext", text: _("ID_TO") + " "},
                dateTo,
                " ",
                {
                    xtype: "button",
                    text: _("ID_FILTER"),

                    handler: function ()
                    {
                        pagingCron.moveFirst();
                    }
                },
                " ",
                {
                    xtype: "button",
                    text: _("ID_RESET_DATES"),

                    handler: function ()
                    {
                        Ext.getCmp("dateFrom").reset(),
                        Ext.getCmp("dateTo").reset()
                    }
                }
            ],
            bbar: pagingCron,

            title: _("ID_CRON_ACTIONS_LOG"),

            listeners: {
                rowdblclick: function ()
                {
                    logView();
                }
            }
        });

        //Initialize events
        storeCron.load();

        cboPageSize.setValue(pageSize);

        //Load all panels
        var viewport = new Ext.Viewport({
            layout: "fit",
            autoScroll: false,
            items: [grdpnlMain]
        });
    }
}

Ext.onReady(cron.application.init, cron.application);<|endoftext|>


<commit_before>'use strict';

var expando = 'sektor-' + Date.now();
var rsiblings = /[+~]/;
var document = global.document;
var del = document.documentElement;
var match = del.matches ||
            del.webkitMatchesSelector ||
            del.mozMatchesSelector ||
            del.oMatchesSelector ||
            del.msMatchesSelector;

function qsa (selector, context) {
  var existed, id, prefix, prefixed, adapter, hack = context !== document;
  if (hack) { // id hack for context-rooted queries
    existed = context.getAttribute('id');
    id = existed || expando;
    prefix = '#' + id + ' ';
    prefixed = prefix + selector.replace(/,/g, ',' + prefix);
    adapter = rsiblings.test(selector) && context.parentNode || context;
    if (!existed) { context.setAttribute('id', id); }
  }
  try {
    return (adapter || context).querySelectorAll(prefixed || selector);
  } catch (e) {
    return [];
  } finally {
    if (existed === null) { context.removeAttribute('id'); }
  }
}

function find (selector, ctx, collection, seed) {
  var element;
  var context = ctx || document;
  var results = collection || [];
  var i = 0;
  if (typeof selector !== 'string') {
    return results;
  }
  if (context.nodeType !== 1 && context.nodeType !== 9) {
    return []; // bail if context is not an element or document
  }
  if (seed) {
    while ((element = seed[i++])) {
      if (matchesSelector(element, selector)) {
        results.push(element);
      }
    }
  } else {
    results.push.apply(results, qsa(selector, context));
  }
  return results;
}

function matches (selector, elements) {
  return find(selector, null, null, elements);
}

function matchesSelector (element, selector) {
  return match.call(element, selector);
}

module.exports = find;

find.matches = matches;
find.matchesSelector = matchesSelector;
<commit_msg>shorten<commit_after>'use strict';

var expando = 'sektor-' + Date.now();
var rsiblings = /[+~]/;
var document = global.document;
var del = document.documentElement;
var match = del.matches ||
            del.webkitMatchesSelector ||
            del.mozMatchesSelector ||
            del.oMatchesSelector ||
            del.msMatchesSelector;

function qsa (selector, context) {
  var existed, id, prefix, prefixed, adapter, hack = context !== document;
  if (hack) { // id hack for context-rooted queries
    existed = context.getAttribute('id');
    id = existed || expando;
    prefix = '#' + id + ' ';
    prefixed = prefix + selector.replace(/,/g, ',' + prefix);
    adapter = rsiblings.test(selector) && context.parentNode;
    if (!existed) { context.setAttribute('id', id); }
  }
  try {
    return (adapter || context).querySelectorAll(prefixed || selector);
  } catch (e) {
    return [];
  } finally {
    if (existed === null) { context.removeAttribute('id'); }
  }
}

function find (selector, ctx, collection, seed) {
  var element;
  var context = ctx || document;
  var results = collection || [];
  var i = 0;
  if (typeof selector !== 'string') {
    return results;
  }
  if (context.nodeType !== 1 && context.nodeType !== 9) {
    return []; // bail if context is not an element or document
  }
  if (seed) {
    while ((element = seed[i++])) {
      if (matchesSelector(element, selector)) {
        results.push(element);
      }
    }
  } else {
    results.push.apply(results, qsa(selector, context));
  }
  return results;
}

function matches (selector, elements) {
  return find(selector, null, null, elements);
}

function matchesSelector (element, selector) {
  return match.call(element, selector);
}

module.exports = find;

find.matches = matches;
find.matchesSelector = matchesSelector;
<|endoftext|>


<commit_before>'use strict';

// MODULES //

var debug = require( 'debug' )( 'browserify:bundle' );
var writeFile = require( 'fs' ).writeFile;
var browserify = require( 'browserify' );
var isStringArray = require( '@stdlib/assert/is-string-array' ).primitives;
var isFunction = require( '@stdlib/assert/is-function' );
var isString = require( '@stdlib/assert/is-string' ).isPrimitive;

// MAIN //

/**
* Bundles files into a single file using `browserify`.
*
* @param {StringArray} files - files to bundle
* @param {string} [dest] - output file path
* @param {Callback} clbk - callback to invoke after creating a bundle
* @throws {TypeError} first argument must be an array of string primitives
* @throws {TypeError} `dest` argument must be a string primitive
* @throws {TypeError} last argument must be a function
*
* @example
* var files = [ '/foo/bar.js', '/beep/boop.js' ];
*
* bundle( files, clbk );
*
* function clbk( error, bundle ) {
*     if ( error ) {
*         throw error;
*     }
*     console.log( bundle.toString() );
* }
*/
function bundle( files, dest, clbk ) {
	var opts;
	var out;
	var cb;
	var b;

	if ( !isStringArray( files ) ) {
		throw new TypeError( 'invalid input argument. First argument must be an array of string primitives. Value: `'+files+'`.' );
	}
	if ( arguments.length < 3 ) {
		cb = dest;
	} else {
		if ( !isString( dest ) ) {
			throw new TypeError( 'invalid input argument. Second argument must be a string primitive. Value: `'+dest+'`.' );
		}
		out = dest;
		cb = clbk;
	}
	if ( !isFunction( cb ) ) {
		throw new TypeError( 'invalid input argument. Last argument must be a function. Value: `'+cb+'`.' );
	}
	opts = {
		'transform': [ 'envify' ],
		'plugin': [ 'proxyquire-universal' ]
	};
	debug( 'Browserify options: %s', JSON.stringify( opts ) );

	b = browserify( files, opts );

	debug( 'Creating a bundle...' );
	b.bundle( onBundle );

	/**
	* Callback invoked upon creating a bundle.
	*
	* @private
	* @param {(Error|null)} error - error object
	* @param {Buffer} bundle - bundle
	* @returns {void}
	*/
	function onBundle( error, bundle ) {
		var opts;
		if ( error ) {
			debug( 'Encountered an error when creating a bundle: %s', error.message );
			return cb( error );
		}
		debug( 'Successfully created a bundle.' );
		if ( out === void 0 ) {
			return cb( null, bundle );
		}
		debug( 'Writing bundle to file...' );
		opts = {
			'encoding': 'utf8'
		};
		writeFile( out, bundle, opts, onWrite );
	} // end FUNCTION onBundle()

	/**
	* Callback invoked upon writing a file.
	*
	* @private
	* @param {(Error|null)} error - error object
	* @returns {void}
	*/
	function onWrite( error ) {
		if ( error ) {
			debug( 'Encountered an error when writing bundle to file: %s', error.message );
			return cb( error );
		}
		debug( 'Successfully wrote bundle to file.' );
		cb();
	} // end FUNCTION onWrite()
} // end FUNCTION bundle()

// EXPORTS //

module.exports = bundle;
<commit_msg>Update throws annotation<commit_after>'use strict';

// MODULES //

var debug = require( 'debug' )( 'browserify:bundle' );
var writeFile = require( 'fs' ).writeFile;
var browserify = require( 'browserify' );
var isStringArray = require( '@stdlib/assert/is-string-array' ).primitives;
var isFunction = require( '@stdlib/assert/is-function' );
var isString = require( '@stdlib/assert/is-string' ).isPrimitive;

// MAIN //

/**
* Bundles files into a single file using `browserify`.
*
* @param {StringArray} files - files to bundle
* @param {string} [dest] - output file path
* @param {Callback} clbk - callback to invoke after creating a bundle
* @throws {TypeError} first argument must be an array of string primitives
* @throws {TypeError} the output file path must be a string primitive
* @throws {TypeError} last argument must be a function
*
* @example
* var files = [ '/foo/bar.js', '/beep/boop.js' ];
*
* bundle( files, clbk );
*
* function clbk( error, bundle ) {
*     if ( error ) {
*         throw error;
*     }
*     console.log( bundle.toString() );
* }
*/
function bundle( files, dest, clbk ) {
	var opts;
	var out;
	var cb;
	var b;

	if ( !isStringArray( files ) ) {
		throw new TypeError( 'invalid input argument. First argument must be an array of string primitives. Value: `'+files+'`.' );
	}
	if ( arguments.length < 3 ) {
		cb = dest;
	} else {
		if ( !isString( dest ) ) {
			throw new TypeError( 'invalid input argument. Second argument must be a string primitive. Value: `'+dest+'`.' );
		}
		out = dest;
		cb = clbk;
	}
	if ( !isFunction( cb ) ) {
		throw new TypeError( 'invalid input argument. Last argument must be a function. Value: `'+cb+'`.' );
	}
	opts = {
		'transform': [ 'envify' ],
		'plugin': [ 'proxyquire-universal' ]
	};
	debug( 'Browserify options: %s', JSON.stringify( opts ) );

	b = browserify( files, opts );

	debug( 'Creating a bundle...' );
	b.bundle( onBundle );

	/**
	* Callback invoked upon creating a bundle.
	*
	* @private
	* @param {(Error|null)} error - error object
	* @param {Buffer} bundle - bundle
	* @returns {void}
	*/
	function onBundle( error, bundle ) {
		var opts;
		if ( error ) {
			debug( 'Encountered an error when creating a bundle: %s', error.message );
			return cb( error );
		}
		debug( 'Successfully created a bundle.' );
		if ( out === void 0 ) {
			return cb( null, bundle );
		}
		debug( 'Writing bundle to file...' );
		opts = {
			'encoding': 'utf8'
		};
		writeFile( out, bundle, opts, onWrite );
	} // end FUNCTION onBundle()

	/**
	* Callback invoked upon writing a file.
	*
	* @private
	* @param {(Error|null)} error - error object
	* @returns {void}
	*/
	function onWrite( error ) {
		if ( error ) {
			debug( 'Encountered an error when writing bundle to file: %s', error.message );
			return cb( error );
		}
		debug( 'Successfully wrote bundle to file.' );
		cb();
	} // end FUNCTION onWrite()
} // end FUNCTION bundle()

// EXPORTS //

module.exports = bundle;
<|endoftext|>


<commit_before>var teardown, clearCache, registerDependant, unregisterDependant, notifyDependants, resolveRef;

teardown = function ( thing ) {
	if ( !thing.keypath ) {
		// this was on the 'unresolved' list, we need to remove it
		var index = thing.root._pendingResolution.indexOf( thing );

		if ( index !== -1 ) {
			thing.root._pendingResolution.splice( index, 1 );
		}

	} else {
		// this was registered as a dependency
		unregisterDependant( thing.root, thing.keypath, thing, thing.priority || 0 );
	}

	if ( thing.evaluator ) {
		thing.evaluator.teardown();
	}
};

clearCache = function ( root, keypath ) {
	var value, dependants = root._depsMap[ keypath ], i;

	// is this a modified array, which shouldn't fire set events on this keypath anymore?
	if ( root.modifyArrays ) {
		if ( keypath.charAt( 0 ) !== '(' ) { // expressions don't get wrapped
			value = root._cache[ keypath ];
			if ( isArray( value ) && !value._ractive.setting ) {
				unregisterKeypathFromArray( value, keypath, root );
			}
		}
	}

	delete root._cache[ keypath ];

	if ( !dependants ) {
		return;
	}

	i = dependants.length;
	while ( i-- ) {
		clearCache( root, dependants[i] );
	}
};

registerDependant = function ( root, keypath, dependant, priority ) {
	var deps;

	if ( !root._deps[ keypath ] ) {
		root._deps[ keypath ] = [];
	}

	deps = root._deps[ keypath ];

	if ( !deps[ priority ] ) {
		deps[ priority ] = [ dependant ];
		return;
	}

	deps = deps[ priority ];

	if ( deps.indexOf( dependant ) === -1 ) {
		deps[ deps.length ] = dependant;
	}
};

unregisterDependant = function ( root, keypath, dependant, priority ) {
	var deps, i, keep;

	deps = root._deps[ keypath ][ priority ];
	deps.splice( deps.indexOf( dependant ), 1 );

	if ( !deps.length ) {
		root._deps[ keypath ][ priority ] = null;
	}

	// can we forget this keypath altogether?
	i = root._deps[ keypath ].length;
	while ( i-- ) {
		if ( root._deps[ keypath ][i] ) {
			keep = true;
			break;
		}
	}

	if ( !keep ) {
		// yes, we can forget it
		root._deps[ keypath ] = null;
	}
};

notifyDependants = function ( root, keypath, onlyDirect ) {
	var depsByPriority, deps, i, j, len, childDeps;

	depsByPriority = root._deps[ keypath ];

	if ( depsByPriority ) {
		len = depsByPriority.length;
		for ( i=0; i<len; i+=1 ) {
			deps = depsByPriority[i];

			if ( deps ) {
				j = deps.length;
				while ( j-- ) {
					deps[j].update();
				}
			}
		}
	}

	// If we're only notifying direct dependants, not dependants
	// of downstream keypaths, then YOU SHALL NOT PASS
	if ( onlyDirect ) {
		return;
	}

	// cascade
	childDeps = root._depsMap[ keypath ];

	if ( childDeps ) {
		i = childDeps.length;
		while ( i-- ) {
			notifyDependants( root, childDeps[i] );

			// TODO at some point, no-longer extant dependants need to be removed
			// from the map. However a keypath can have no direct dependants yet
			// still have downstream dependants, so it's not entirely straightforward
		}
	}
};

// Resolve a full keypath from `ref` within the given `contextStack` (e.g.
// `'bar.baz'` within the context stack `['foo']` might resolve to `'foo.bar.baz'`
resolveRef = function ( root, ref, contextStack ) {

	var keys, lastKey, innerMostContext, contextKeys, parentValue, keypath;

	// Implicit iterators - i.e. {{.}} - are a special case
	if ( ref === '.' ) {
		return contextStack[ contextStack.length - 1 ];
	}

	keys = splitKeypath( ref );
	lastKey = keys.pop();

	// Clone the context stack, so we don't mutate the original
	contextStack = contextStack.concat();

	// Take each context from the stack, working backwards from the innermost context
	while ( contextStack.length ) {

		innerMostContext = contextStack.pop();
		contextKeys = splitKeypath( innerMostContext );

		parentValue = root.get( contextKeys.concat( keys ) );

		if ( typeof parentValue === 'object' && parentValue.hasOwnProperty( lastKey ) ) {
			keypath = innerMostContext + '.' + ref;
			break;
		}
	}

	if ( !keypath && root.get( ref ) !== undefined ) {
		keypath = ref;
	}

	return keypath;
};<commit_msg>let evaluators tear themselves down when they no longer have dependants<commit_after>var teardown, clearCache, registerDependant, unregisterDependant, notifyDependants, resolveRef;

teardown = function ( thing ) {
	if ( !thing.keypath ) {
		// this was on the 'unresolved' list, we need to remove it
		var index = thing.root._pendingResolution.indexOf( thing );

		if ( index !== -1 ) {
			thing.root._pendingResolution.splice( index, 1 );
		}

	} else {
		// this was registered as a dependency
		unregisterDependant( thing.root, thing.keypath, thing, thing.priority || 0 );
	}
};

clearCache = function ( root, keypath ) {
	var value, dependants = root._depsMap[ keypath ], i;

	// is this a modified array, which shouldn't fire set events on this keypath anymore?
	if ( root.modifyArrays ) {
		if ( keypath.charAt( 0 ) !== '(' ) { // expressions don't get wrapped
			value = root._cache[ keypath ];
			if ( isArray( value ) && !value._ractive.setting ) {
				unregisterKeypathFromArray( value, keypath, root );
			}
		}
	}

	delete root._cache[ keypath ];

	if ( !dependants ) {
		return;
	}

	i = dependants.length;
	while ( i-- ) {
		clearCache( root, dependants[i] );
	}
};

registerDependant = function ( root, keypath, dependant, priority ) {
	var deps;

	if ( !root._deps[ keypath ] ) {
		root._deps[ keypath ] = [];
	}

	deps = root._deps[ keypath ];

	if ( !deps[ priority ] ) {
		deps[ priority ] = [ dependant ];
		return;
	}

	deps = deps[ priority ];

	if ( deps.indexOf( dependant ) === -1 ) {
		deps[ deps.length ] = dependant;
	}
};

unregisterDependant = function ( root, keypath, dependant, priority ) {
	var deps, i, keep;

	deps = root._deps[ keypath ][ priority ];
	deps.splice( deps.indexOf( dependant ), 1 );

	if ( !deps.length ) {
		root._deps[ keypath ][ priority ] = null;
	}

	// can we forget this keypath altogether?
	i = root._deps[ keypath ].length;
	while ( i-- ) {
		if ( root._deps[ keypath ][i] ) {
			keep = true;
			break;
		}
	}

	if ( !keep ) {
		// yes, we can forget it
		root._deps[ keypath ] = null;
	}
};

notifyDependants = function ( root, keypath, onlyDirect ) {
	var depsByPriority, deps, i, j, len, childDeps;

	depsByPriority = root._deps[ keypath ];

	if ( depsByPriority ) {
		len = depsByPriority.length;
		for ( i=0; i<len; i+=1 ) {
			deps = depsByPriority[i];

			if ( deps ) {
				j = deps.length;
				while ( j-- ) {
					deps[j].update();
				}
			}
		}
	}

	// If we're only notifying direct dependants, not dependants
	// of downstream keypaths, then YOU SHALL NOT PASS
	if ( onlyDirect ) {
		return;
	}

	// cascade
	childDeps = root._depsMap[ keypath ];

	if ( childDeps ) {
		i = childDeps.length;
		while ( i-- ) {
			notifyDependants( root, childDeps[i] );

			// TODO at some point, no-longer extant dependants need to be removed
			// from the map. However a keypath can have no direct dependants yet
			// still have downstream dependants, so it's not entirely straightforward
		}
	}
};

// Resolve a full keypath from `ref` within the given `contextStack` (e.g.
// `'bar.baz'` within the context stack `['foo']` might resolve to `'foo.bar.baz'`
resolveRef = function ( root, ref, contextStack ) {

	var keys, lastKey, innerMostContext, contextKeys, parentValue, keypath;

	// Implicit iterators - i.e. {{.}} - are a special case
	if ( ref === '.' ) {
		return contextStack[ contextStack.length - 1 ];
	}

	keys = splitKeypath( ref );
	lastKey = keys.pop();

	// Clone the context stack, so we don't mutate the original
	contextStack = contextStack.concat();

	// Take each context from the stack, working backwards from the innermost context
	while ( contextStack.length ) {

		innerMostContext = contextStack.pop();
		contextKeys = splitKeypath( innerMostContext );

		parentValue = root.get( contextKeys.concat( keys ) );

		if ( typeof parentValue === 'object' && parentValue.hasOwnProperty( lastKey ) ) {
			keypath = innerMostContext + '.' + ref;
			break;
		}
	}

	if ( !keypath && root.get( ref ) !== undefined ) {
		keypath = ref;
	}

	return keypath;
};<|endoftext|>


<commit_before>/**
 * Server configuration
 */

import 'babel-polyfill';
import path from 'path';
import express from 'express';
import bodyParser from 'body-parser';
import React from 'react';
// import ReactDom from 'react-dom/server';
// import schema from './data/models'

import routes from './routes';
import apiRoutes from './apiRoutes';
import {port} from './config';

const app = express();

//
// Register Node.js middleware
// -----------------------------------------------------------------------------
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

//
// Register routes in express
// -----------------------------------------------------------------------------
app.use('/', routes);
app.use('/api', apiRoutes);

//
// Launch the server
// -----------------------------------------------------------------------------
app.listen(port, () => {
    console.log(`The server is running at http://localhost:${port}/`);
});<commit_msg>Putting /api routes before / so that any get routes inside /api endpoints can be reached<commit_after>/**
 * Server configuration
 */

import 'babel-polyfill';
import path from 'path';
import express from 'express';
import bodyParser from 'body-parser';
import React from 'react';
// import ReactDom from 'react-dom/server';
// import schema from './data/models'

import routes from './routes';
import apiRoutes from './apiRoutes';
import {port} from './config';

const app = express();

//
// Register Node.js middleware
// -----------------------------------------------------------------------------
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

//
// Register routes in express
// -----------------------------------------------------------------------------
app.use('/api', apiRoutes);
app.use('/', routes);

//
// Launch the server
// -----------------------------------------------------------------------------
app.listen(port, () => {
    console.log(`The server is running at http://localhost:${port}/`);
});<|endoftext|>