<commit_before>import _ from 'lodash';

import * as constants from '../constants';
import Settings from '../WDLSettings';
import Port from '../../../model/Port';

import { buildDeclarations } from '../utils/utils';

export default class WorkflowGenerator {
  constructor(wfStep, settings) {
    this.wfBodyElementGenerators = {
      call: this.genCall,
      scatter: this.genScatter,
      whileloop: this.genWhile,
      if: this.genIf,
    };
    this.wfName = wfStep.name;
    this.wfStep = wfStep;

    const action = wfStep.action;
    this.declarations = action.i;
    this.outputMappings = wfStep.o;

    this.children = wfStep.children;

    this.meta = action.data.meta;
    this.parameterMeta = action.data.parameterMeta;

    this.wfBlockString = '';

    this.settings = new Settings(settings);
  }

  renderWorkflow() {
    const EOL = this.settings.getValue('style.eol');

    if (this.wfBlockString === '') {
      this.wfBlockString += `${constants.WORKFLOW} ${this.wfName} ${constants.SCOPE_OPEN}${EOL}`;

      const prosessed = [];
      this.wfBlockString += this.renderStepMainBody(this.wfStep, prosessed);
      this.wfBlockString += this.buildSegment(constants.META, this.meta);
      this.wfBlockString += this.buildSegment(constants.PARAMETER_META, this.parameterMeta);
      this.wfBlockString += this.buildOutputMap(this.outputMappings);

      this.wfBlockString += `${constants.SCOPE_CLOSE}${EOL}${EOL}`;
    }
    return this.wfBlockString;
  }

  renderStepMainBody(step, prosessed) {
    const declarations = step.action.i;
    const children = step.children;

    let res = '';

    if (!step.type) {
      res += buildDeclarations(declarations, this.settings);
    }

    for (let i = 0, size = _.size(children); i < size; ++i) {
      const childName = this.getNextOrderedChild(children, prosessed);
      prosessed.push(childName);

      const child = children[childName];
      res += this.wfBodyElementGenerators[child.type || 'call'].call(this, child, prosessed);
    }

    return res;
  }

  getNextOrderedChild(children, processed) {
    let res = '';
    const isSubset = (source, target) => !_.difference(_.flatten(source), _.flatten(target)).length;

    _.forEach(children, (child) => {
      if (_.indexOf(processed, child.name) < 0) {
        const refs = this.findCallReferences(child);

        if (refs.length === 0 || isSubset(refs, processed)) {
          res = child.name;
          return false;
        }
      }

      return true;
    });

    if (res === '') {
      throw new Error('Cycled links are not allowed');
    }
    return res;
  }

  findCallReferences(child) {
    const res = [];

    _.forEach(child.i, (value) => {
      if (value.inputs && _.size(value.inputs) > 0) {
        if (_.size(value.inputs) > 1) {
          throw new Error('Multiple links into one input are prohibited');
        }

        const connection = value.inputs[0];
        if (connection.from instanceof Port && connection.from.step.name !== this.wfStep.name) {
          res.push(connection.from.step.name);
        }
      }
    });

    _.forEach(child.children, (item) => {
      res.concat(this.findCallReferences(item));
    });

    return res;
  }

  buildPortValue(value) {
    if (value.inputs && _.size(value.inputs) > 0) {
      if (_.size(value.inputs) > 1) {
        throw new Error('Multiple links into one input are prohibited');
      }

      const connection = value.inputs[0];
      if (connection.from instanceof Port) {
        const outStep = connection.from.step;
        const outStepName = outStep.name;
        const outVarName = connection.from.name;

        if (outStepName === this.wfName || outStep.type) {
          return outVarName;
        }
        return `${outStepName}.${outVarName}`;
      }
      return connection.from;
    }
    return '';
  }

  genCall(child) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');
    const DOUBLE_SCOPE_INDENT = `${SCOPE_INDENT}${SCOPE_INDENT}`;
    const TRIPLE_SCOPE_INDENT = `${DOUBLE_SCOPE_INDENT}${SCOPE_INDENT}`;

    let res = '';

    const val = child;
    let callString = `${SCOPE_INDENT}${constants.CALL} `;

    if (val.action.name === val.name) {
      callString += `${val.name}`;
    } else {
      callString += `${val.action.name} ${constants.AS} ${val.name}`;
    }

    res += `${callString}`;

    if (_.size(val.i) > 0) {
      let mappingWasFounded = false;

      _.forEach(val.i, (pVal, pKey) => {
        if (pVal.inputs && _.size(pVal.inputs) > 0) {
          if (!mappingWasFounded) {
            res += ` ${constants.SCOPE_OPEN}${EOL}`;
            res += `${DOUBLE_SCOPE_INDENT}${constants.INPUT}${constants.COLON}${EOL}`;
            mappingWasFounded = true;
          }
          res += `${TRIPLE_SCOPE_INDENT}${pKey} ${constants.EQ} ${this.buildPortValue(pVal)},${EOL}`;
        }
      });
      if (mappingWasFounded) {
        res += `${SCOPE_INDENT}${constants.SCOPE_CLOSE}`;
      }
    }

    return `${res}${EOL}`;
  }

  genScatter(child, prosessed) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');

    let res = '';

    const item = child.action.data.variable;
    const collection = child.action.data.collection;
    res += `${SCOPE_INDENT}${constants.SCATTER} (${item} ${constants.IN} ${collection}) {${EOL}`;

    return this.genBodyCloser(res, child, prosessed);
  }

  genIf(child, prosessed) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');

    let res = '';

    const expression = child.action.data.expression;
    res += `${SCOPE_INDENT}${constants.IF} (${expression}) ${constants.SCOPE_OPEN}${EOL}`;

    return this.genBodyCloser(res, child, prosessed);
  }

  genWhile(child, prosessed) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');

    let res = '';

    const expression = child.action.data.expression;
    res += `${SCOPE_INDENT}${constants.WHILE} (${expression}) ${constants.SCOPE_OPEN}${EOL}`;

    return this.genBodyCloser(res, child, prosessed);
  }

  genBodyCloser(res, child, prosessed) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');

    const innerBody = this.renderStepMainBody(child, prosessed);
    const bodyStringArr = innerBody.split(EOL);

    _.forEach(bodyStringArr, (str) => {
      if (str !== '') {
        res += `${SCOPE_INDENT}${str}${EOL}`;
      }
    });

    res += `${SCOPE_INDENT}${constants.SCOPE_CLOSE}${EOL}`;
    return res;
  }

  buildOutputMap(outputMappings) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');
    const DOUBLE_SCOPE_INDENT = `${SCOPE_INDENT}${SCOPE_INDENT}`;

    let res = '';
    if (outputMappings && _.size(outputMappings) > 0) {
      res += `${SCOPE_INDENT}${constants.OUTPUT} ${constants.SCOPE_OPEN}${EOL}`;

      _.forEach(outputMappings, (val, key) => {
        if (key.indexOf('.') >= 0) {
          res += `${this.buildPortValue(val)}${EOL}`;
        } else {
          res += `${DOUBLE_SCOPE_INDENT}${val.desc.type} ${key} ${constants.EQ} ${this.buildPortValue(val)}${EOL}`;
        }
      });

      res += `${SCOPE_INDENT}${constants.SCOPE_CLOSE}${EOL}`;
    }

    return res;
  }

  buildSegment(segmentName, data) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');
    const DOUBLE_SCOPE_INDENT = `${SCOPE_INDENT}${SCOPE_INDENT}`;

    const buildValue = value => ` ${constants.EQ} ${value.toString()}`;

    let res = '';
    if (data && _.size(data) > 0) {
      res += `${SCOPE_INDENT}${segmentName} ${constants.SCOPE_OPEN}${EOL}`;

      _.forEach(data, (val, key) => {
        res += `${DOUBLE_SCOPE_INDENT}${key}${buildValue(val)}${EOL}`;
      });

      res += `${SCOPE_INDENT}${constants.SCOPE_CLOSE}${EOL}`;
    }

    return res;
  }
}
<commit_msg>Fixed empty wf outputs that have the wildcard kind<commit_after>import _ from 'lodash';

import * as constants from '../constants';
import Settings from '../WDLSettings';
import Port from '../../../model/Port';

import { buildDeclarations } from '../utils/utils';

export default class WorkflowGenerator {
  constructor(wfStep, settings) {
    this.wfBodyElementGenerators = {
      call: this.genCall,
      scatter: this.genScatter,
      whileloop: this.genWhile,
      if: this.genIf,
    };
    this.wfName = wfStep.name;
    this.wfStep = wfStep;

    const action = wfStep.action;
    this.declarations = action.i;
    this.outputMappings = wfStep.o;

    this.children = wfStep.children;

    this.meta = action.data.meta;
    this.parameterMeta = action.data.parameterMeta;

    this.wfBlockString = '';

    this.settings = new Settings(settings);
  }

  renderWorkflow() {
    const EOL = this.settings.getValue('style.eol');

    if (this.wfBlockString === '') {
      this.wfBlockString += `${constants.WORKFLOW} ${this.wfName} ${constants.SCOPE_OPEN}${EOL}`;

      const prosessed = [];
      this.wfBlockString += this.renderStepMainBody(this.wfStep, prosessed);
      this.wfBlockString += this.buildSegment(constants.META, this.meta);
      this.wfBlockString += this.buildSegment(constants.PARAMETER_META, this.parameterMeta);
      this.wfBlockString += this.buildOutputMap(this.outputMappings);

      this.wfBlockString += `${constants.SCOPE_CLOSE}${EOL}${EOL}`;
    }
    return this.wfBlockString;
  }

  renderStepMainBody(step, prosessed) {
    const declarations = step.action.i;
    const children = step.children;

    let res = '';

    if (!step.type) {
      res += buildDeclarations(declarations, this.settings);
    }

    for (let i = 0, size = _.size(children); i < size; ++i) {
      const childName = this.getNextOrderedChild(children, prosessed);
      prosessed.push(childName);

      const child = children[childName];
      res += this.wfBodyElementGenerators[child.type || 'call'].call(this, child, prosessed);
    }

    return res;
  }

  getNextOrderedChild(children, processed) {
    let res = '';
    const isSubset = (source, target) => !_.difference(_.flatten(source), _.flatten(target)).length;

    _.forEach(children, (child) => {
      if (_.indexOf(processed, child.name) < 0) {
        const refs = this.findCallReferences(child);

        if (refs.length === 0 || isSubset(refs, processed)) {
          res = child.name;
          return false;
        }
      }

      return true;
    });

    if (res === '') {
      throw new Error('Cycled links are not allowed');
    }
    return res;
  }

  findCallReferences(child) {
    const res = [];

    _.forEach(child.i, (value) => {
      if (value.inputs && _.size(value.inputs) > 0) {
        if (_.size(value.inputs) > 1) {
          throw new Error('Multiple links into one input are prohibited');
        }

        const connection = value.inputs[0];
        if (connection.from instanceof Port && connection.from.step.name !== this.wfStep.name) {
          res.push(connection.from.step.name);
        }
      }
    });

    _.forEach(child.children, (item) => {
      res.concat(this.findCallReferences(item));
    });

    return res;
  }

  buildPortValue(value) {
    if (value.inputs && _.size(value.inputs) > 0) {
      if (_.size(value.inputs) > 1) {
        throw new Error('Multiple links into one input are prohibited');
      }

      const connection = value.inputs[0];
      if (connection.from instanceof Port) {
        const outStep = connection.from.step;
        const outStepName = outStep.name;
        const outVarName = connection.from.name;

        if (outStepName === this.wfName || outStep.type) {
          return outVarName;
        }
        return `${outStepName}.${outVarName}`;
      }
      return connection.from;
    }
    return '';
  }

  genCall(child) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');
    const DOUBLE_SCOPE_INDENT = `${SCOPE_INDENT}${SCOPE_INDENT}`;
    const TRIPLE_SCOPE_INDENT = `${DOUBLE_SCOPE_INDENT}${SCOPE_INDENT}`;

    let res = '';

    const val = child;
    let callString = `${SCOPE_INDENT}${constants.CALL} `;

    if (val.action.name === val.name) {
      callString += `${val.name}`;
    } else {
      callString += `${val.action.name} ${constants.AS} ${val.name}`;
    }

    res += `${callString}`;

    if (_.size(val.i) > 0) {
      let mappingWasFounded = false;

      _.forEach(val.i, (pVal, pKey) => {
        if (pVal.inputs && _.size(pVal.inputs) > 0) {
          if (!mappingWasFounded) {
            res += ` ${constants.SCOPE_OPEN}${EOL}`;
            res += `${DOUBLE_SCOPE_INDENT}${constants.INPUT}${constants.COLON}${EOL}`;
            mappingWasFounded = true;
          }
          res += `${TRIPLE_SCOPE_INDENT}${pKey} ${constants.EQ} ${this.buildPortValue(pVal)},${EOL}`;
        }
      });
      if (mappingWasFounded) {
        res += `${SCOPE_INDENT}${constants.SCOPE_CLOSE}`;
      }
    }

    return `${res}${EOL}`;
  }

  genScatter(child, prosessed) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');

    let res = '';

    const item = child.action.data.variable;
    const collection = child.action.data.collection;
    res += `${SCOPE_INDENT}${constants.SCATTER} (${item} ${constants.IN} ${collection}) {${EOL}`;

    return this.genBodyCloser(res, child, prosessed);
  }

  genIf(child, prosessed) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');

    let res = '';

    const expression = child.action.data.expression;
    res += `${SCOPE_INDENT}${constants.IF} (${expression}) ${constants.SCOPE_OPEN}${EOL}`;

    return this.genBodyCloser(res, child, prosessed);
  }

  genWhile(child, prosessed) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');

    let res = '';

    const expression = child.action.data.expression;
    res += `${SCOPE_INDENT}${constants.WHILE} (${expression}) ${constants.SCOPE_OPEN}${EOL}`;

    return this.genBodyCloser(res, child, prosessed);
  }

  genBodyCloser(res, child, prosessed) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');

    const innerBody = this.renderStepMainBody(child, prosessed);
    const bodyStringArr = innerBody.split(EOL);

    _.forEach(bodyStringArr, (str) => {
      if (str !== '') {
        res += `${SCOPE_INDENT}${str}${EOL}`;
      }
    });

    res += `${SCOPE_INDENT}${constants.SCOPE_CLOSE}${EOL}`;
    return res;
  }

  buildOutputMap(outputMappings) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');
    const DOUBLE_SCOPE_INDENT = `${SCOPE_INDENT}${SCOPE_INDENT}`;

    let res = '';
    if (outputMappings && _.size(outputMappings) > 0) {
      res += `${SCOPE_INDENT}${constants.OUTPUT} ${constants.SCOPE_OPEN}${EOL}`;

      _.forEach(outputMappings, (val, key) => {
        if (key.indexOf('.') >= 0) {
          res += `${DOUBLE_SCOPE_INDENT}${key}${EOL}`;
        } else {
          res += `${DOUBLE_SCOPE_INDENT}${val.desc.type} ${key} ${constants.EQ} ${this.buildPortValue(val)}${EOL}`;
        }
      });

      res += `${SCOPE_INDENT}${constants.SCOPE_CLOSE}${EOL}`;
    }

    return res;
  }

  buildSegment(segmentName, data) {
    const EOL = this.settings.getValue('style.eol');
    const SCOPE_INDENT = this.settings.getValue('style.scope_indent');
    const DOUBLE_SCOPE_INDENT = `${SCOPE_INDENT}${SCOPE_INDENT}`;

    const buildValue = value => ` ${constants.EQ} ${value.toString()}`;

    let res = '';
    if (data && _.size(data) > 0) {
      res += `${SCOPE_INDENT}${segmentName} ${constants.SCOPE_OPEN}${EOL}`;

      _.forEach(data, (val, key) => {
        res += `${DOUBLE_SCOPE_INDENT}${key}${buildValue(val)}${EOL}`;
      });

      res += `${SCOPE_INDENT}${constants.SCOPE_CLOSE}${EOL}`;
    }

    return res;
  }
}
<|endoftext|>


<commit_before>ngapp.service('contextMenuFactory', function(referenceService) {
    let uneditableValueTypes = [xelib.vtUnknown, xelib.vtArray, xelib.vtStruct];

    let divider = {
        visible: (scope, items) => {
            return items.length > 0 && !items.last().divider;
        },
        build: (scope, items) => items.push({ divider: true })
    };

    let testNodes = function(nodes, testFn) {
        return nodes.reduce(function (b, node) {
            return b && testFn(node);
        }, true);
    };

    let isFileNode = function(node) {
        return node.element_type === xelib.etFile;
    };

    let isRecordNode = function(node) {
        return node.element_type === xelib.etMainRecord;
    };

    let isGroupNode = function(node) {
        return node.element_type === xelib.etGroupRecord;
    };

    let isEditableNode = function(node) {
        return xelib.GetIsEditable(node.handle);
    };

    this.checkboxListItems = [{
        id: 'Select All',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Select All',
                hotkey: 'Ctrl+A',
                callback: scope.selectAll
            });
        }
    }, {
        id: 'Toggle selected',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Toggle selected',
                hotkey: 'Space',
                callback: () => scope.toggleSelected()
            });
        }
    }, {
        id: 'Check Selected',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Check Selected',
                hotkey: 'Ctrl+Space',
                callback: () => scope.toggleSelected(true)
            });
        }
    }, {
        id: 'Uncheck Selected',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Uncheck Selected',
                hotkey: 'Shift+Space',
                callback: () => scope.toggleSelected(false)
            });
        }
    }];

    this.treeViewItems = [{
        id: 'Add',
        visible: (scope) => {
            let node = scope.selectedNodes.last();
            return node && !isRecordNode(node) && xelib.GetCanAdd(node.handle);
        },
        build: (scope, items) => {
            let node = scope.selectedNodes.last(),
                addList = xelib.GetAddList(node.handle);
            items.push({
                label: `Add ${isFileNode(node) ? 'Group' : 'Record'}`,
                hotkey: 'Insert',
                disabled: !addList.length,
                children: addList.map(function(label) {
                    return {
                        label: label,
                        callback: () => scope.addElement(node, label)
                    };
                })
            });
        }
    }, {
        id: 'Add File',
        visible: (scope) => {
            return !scope.selectedNodes.length;
        },
        build: (scope, items) => {
            items.push({
                label: 'Add File',
                hotkey: 'Insert',
                callback: () => scope.addFile()
            });
        }
    }, {
        id: 'Delete',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            return testNodes(scope.selectedNodes, function(node) {
                return xelib.GetIsRemoveable(node.handle);
            });
        },
        build: (scope, items) => {
            items.push({
                label: 'Delete',
                hotkey: 'Del',
                callback: () => scope.deleteElements()
            });
        }
    }, {
        id: 'Refactor',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            let nodes = scope.selectedNodes,
                node = nodes.last();
            return isFileNode(node) && isEditableNode(node) ||
                testNodes(nodes, function(node) {
                    return isRecordNode(node) && isEditableNode(node);
                });
        },
        build: (scope, items) => {
            let node = scope.selectedNodes.last(),
                children = [];
            if (isFileNode(node)) {
                children.push({
                    label: 'Rename File',
                    hotkey: 'F2, Alt+Shift+R',
                    callback: () => scope.changeFileName(node)
                }, {
                    label: 'Change Author',
                    hotkey: 'Alt+Shift+A',
                    callback: () => scope.changeFileAuthor(node)
                }, {
                    label: 'Change Description',
                    hotkey: 'Alt+Shift+D',
                    callback: () => scope.changeFileDescription(node)
                });
            } else if (isRecordNode(node)) {
                children.push({
                    label: 'Rename',
                    hotkey: 'F2, Alt+Shift+R',
                    disabled: true,
                    callback: () => scope.$emit('openModal', 'refactor', {
                        refactorMode: 'Name'
                    })
                }, {
                    label: 'Change Editor IDs',
                    hotkey: 'Alt+Shift+E',
                    disabled: true,
                    callback: () => scope.$emit('openModal', 'refactor', {
                        refactorMode: 'EditorID'
                    })
                }, {
                    label: 'Change Form IDs',
                    hotkey: 'Alt+Shift+F',
                    disabled: true,
                    callback: () => scope.$emit('openModal', 'refactor', {
                        refactorMode: 'FormID'
                    })
                });
            }
            items.push({
                label: 'Refactor',
                children: children
            });
        }
    }, {
        id: 'Masters',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            let nodes = scope.selectedNodes, node = nodes.last();
            return isFileNode(node) && isEditableNode(node);
        },
        build: (scope, items) => {
            let node = scope.selectedNodes.last();
            items.push({
                label: "Masters",
                children: [{
                    label: 'Add Masters',
                    callback: () => scope.addMasters(node)
                }, {
                    label: 'Sort Masters',
                    callback: () => xelib.SortMasters(node.handle)
                }, {
                    label: 'Clean Masters',
                    callback: () => xelib.CleanMasters(node.handle)
                }]
            });
        }
    }, {
        id: 'Enable Editing',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            return testNodes(scope.selectedNodes, function(node) {
                return isFileNode(node) && !isEditableNode(node);
            });
        },
        build: (scope, items) => {
            items.push({
                label: 'Enable Editing',
                hotkey: 'Ctrl+E',
                callback: () => scope.enableEditing()
            })
        }
    }, {
        id: 'Build References',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            return testNodes(scope.selectedNodes, function(node) {
                return isFileNode(node) &&
                    referenceService.canBuildReferences(node.handle);
            });
        },
        build: (scope, items) => {
            items.push({
                label: 'Build References',
                hotkey: 'Ctrl+B',
                callback: () => scope.buildReferences()
            })
        }
    }, divider, {
        id: 'Automate',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Automate',
                hotkey: 'Ctrl+M',
                callback: () => scope.$emit('openModal', 'automate', {
                    targetScope: scope
                })
            })
        }
    }, {
        id: 'Advanced Search',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Advanced Search',
                hotkey: 'Ctrl+Shift+F',
                disabled: true,
                callback: () => scope.$emit('openModal', 'advancedSearch')
            })
        }
    }, {
        id: 'Open',
        visible: (scope) => {
            let node = scope.selectedNodes.last();
            return node && !isGroupNode(node);
        },
        build: (scope, items) => {
            let node = scope.selectedNodes.last();
            items.push({
                label: 'Open in Record View',
                hotkey: 'Enter',
                callback: () => scope.open(node)
            })
        }
    }, divider, {
        id: 'Copy to',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Copy into',
                hotkey: 'Ctrl+Alt+C',
                disabled: scope.selectedNodes.length === 0,
                callback: () => scope.copyInto()
            })
        }
    },{
        id: 'Copy',
        visible: (scope) => { return scope.selectedNodes.length > 0 },
        build: (scope, items) => {
            items.push({
                label: 'Copy',
                hotkey: 'Ctrl+C',
                disabled: !scope.canCopy(),
                callback: () => scope.copyNodes()
            })
        }
    }, {
        id: 'Copy Path',
        visible: (scope) => { return scope.selectedNodes.length > 0 },
        build: (scope, items) => {
            items.push({
                label: 'Copy Path',
                hotkey: 'Ctrl+Shift+C',
                disabled: scope.selectedNodes.length === 0,
                callback: () => scope.copyPaths()
            })
        }
    }, {
        id: 'Paste',
        visible: (scope) => { return scope.selectedNodes.length > 0 },
        build: (scope, items) => {
            items.push({
                label: 'Paste',
                hotkey: 'Ctrl+V',
                disabled: !scope.canPaste(),
                callback: () => scope.pasteNodes(true)
            })
        }
    }, {
        id: 'Paste as Override',
        visible: (scope) => { return scope.selectedNodes.length > 0 },
        build: (scope, items) => {
            items.push({
                label: 'Paste as Override',
                hotkey: 'Ctrl+Shift+V',
                disabled: !scope.canPaste(),
                callback: () => scope.pasteNodes()
            })
        }
    }];

    this.recordViewItems = [{
        id: 'Add',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            let node = scope.selectedNodes.last(),
                index = scope.focusedIndex - 1,
                handle = node.handles[index],
                record = index ? scope.overrides[index - 1] : scope.record,
                parentAvailable = !node.depth || (node.parent && node.parent.handles[index]);
            if (!xelib.GetIsEditable(record)) return false;
            return parentAvailable && (handle === 0 || node.value_type === xelib.vtArray);
        },
        build: (scope, items) => {
            let node = scope.selectedNodes.last(),
                index = scope.focusedIndex - 1;
            items.push({
                label: 'Add',
                hotkey: 'Insert',
                callback: () => scope.addElement(node, index)
            });
        }
    }, {
        id: 'Edit',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            let node = scope.selectedNodes.last(),
                index = scope.focusedIndex - 1,
                handle = node.handles[index],
                record = index === 0 ? scope.record : scope.overrides[index - 1];
            if (!xelib.GetIsEditable(record)) return false;
            return handle !== 0 && !uneditableValueTypes.includes(node.value_type);
        },
        build: (scope, items) => {
            let node = scope.selectedNodes.last(),
                index = scope.focusedIndex;
            items.push({
                label: 'Edit',
                hotkey: 'Enter',
                callback: () => scope.editElement(node, index)
            });
        }
    }, {
        id: 'Delete',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            let index = scope.focusedIndex - 1;
            return testNodes(scope.selectedNodes, function(node) {
                let handle = node.handles[index];
                return handle && xelib.GetIsRemoveable(handle);
            });
        },
        build: (scope, items) => {
            items.push({
                label: 'Delete',
                hotkey: 'Del',
                callback: () => scope.deleteElements()
            });
        }
    }, divider, {
        id: 'Copy',
        visible: () => { return true; },
        build: (scope, items) => {
            //let node = scope.selectedNodes.last();
            items.push({
                label: 'Copy',
                hotkey: 'Ctrl+C',
                disabled: true,//!node,
                callback: () => scope.copyNodes()
            })
        }
    }, {
        id: 'Copy Path',
        visible: () => { return true; },
        build: (scope, items) => {
            //let node = scope.selectedNodes.last();
            items.push({
                label: 'Copy Path',
                hotkey: 'Ctrl+Shift+C',
                disabled: true,//!node,
                callback: () => scope.copyNodePaths()
            })
        }
    }, {
        id: 'Paste',
        visible: () => { return true },
        build: (scope, items) => {
            items.push({
                label: 'Paste',
                hotkey: 'Ctrl+V',
                disabled: true,//!scope.canPaste(),
                callback: () => scope.paste()
            })
        }
    }]
});
<commit_msg>removed refactor submenu, now we just have a single refactor option<commit_after>ngapp.service('contextMenuFactory', function(referenceService) {
    let uneditableValueTypes = [xelib.vtUnknown, xelib.vtArray, xelib.vtStruct];

    let divider = {
        visible: (scope, items) => {
            return items.length > 0 && !items.last().divider;
        },
        build: (scope, items) => items.push({ divider: true })
    };

    let testNodes = function(nodes, testFn) {
        return nodes.reduce(function (b, node) {
            return b && testFn(node);
        }, true);
    };

    let isFileNode = function(node) {
        return node.element_type === xelib.etFile;
    };

    let isRecordNode = function(node) {
        return node.element_type === xelib.etMainRecord;
    };

    let isGroupNode = function(node) {
        return node.element_type === xelib.etGroupRecord;
    };

    let isEditableNode = function(node) {
        return xelib.GetIsEditable(node.handle);
    };

    this.checkboxListItems = [{
        id: 'Select All',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Select All',
                hotkey: 'Ctrl+A',
                callback: scope.selectAll
            });
        }
    }, {
        id: 'Toggle selected',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Toggle selected',
                hotkey: 'Space',
                callback: () => scope.toggleSelected()
            });
        }
    }, {
        id: 'Check Selected',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Check Selected',
                hotkey: 'Ctrl+Space',
                callback: () => scope.toggleSelected(true)
            });
        }
    }, {
        id: 'Uncheck Selected',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Uncheck Selected',
                hotkey: 'Shift+Space',
                callback: () => scope.toggleSelected(false)
            });
        }
    }];

    this.treeViewItems = [{
        id: 'Add',
        visible: (scope) => {
            let node = scope.selectedNodes.last();
            return node && !isRecordNode(node) && xelib.GetCanAdd(node.handle);
        },
        build: (scope, items) => {
            let node = scope.selectedNodes.last(),
                addList = xelib.GetAddList(node.handle);
            items.push({
                label: `Add ${isFileNode(node) ? 'Group' : 'Record'}`,
                hotkey: 'Insert',
                disabled: !addList.length,
                children: addList.map(function(label) {
                    return {
                        label: label,
                        callback: () => scope.addElement(node, label)
                    };
                })
            });
        }
    }, {
        id: 'Add File',
        visible: (scope) => {
            return !scope.selectedNodes.length;
        },
        build: (scope, items) => {
            items.push({
                label: 'Add File',
                hotkey: 'Insert',
                callback: () => scope.addFile()
            });
        }
    }, {
        id: 'Delete',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            return testNodes(scope.selectedNodes, function(node) {
                return xelib.GetIsRemoveable(node.handle);
            });
        },
        build: (scope, items) => {
            items.push({
                label: 'Delete',
                hotkey: 'Del',
                callback: () => scope.deleteElements()
            });
        }
    }, {
        id: 'Refactor',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            let nodes = scope.selectedNodes,
                node = nodes.last();
            return isFileNode(node) && isEditableNode(node) ||
                testNodes(nodes, function(node) {
                    return isRecordNode(node) && isEditableNode(node);
                });
        },
        build: (scope, items) => {
            let node = scope.selectedNodes.last(),
                modal = `refactor${isFileNode(node) ? 'File' : 'Records'}`;
            items.push({
                label: 'Refactor',
                hotkey: 'Alt+Shift+R',
                callback: () => scope.$emit('openModal', modal, {
                    nodes: scope.selectedNodes
                })
            });
        }
    }, {
        id: 'Masters',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            let nodes = scope.selectedNodes, node = nodes.last();
            return isFileNode(node) && isEditableNode(node);
        },
        build: (scope, items) => {
            let node = scope.selectedNodes.last();
            items.push({
                label: "Masters",
                children: [{
                    label: 'Add Masters',
                    callback: () => scope.addMasters(node)
                }, {
                    label: 'Sort Masters',
                    callback: () => xelib.SortMasters(node.handle)
                }, {
                    label: 'Clean Masters',
                    callback: () => xelib.CleanMasters(node.handle)
                }]
            });
        }
    }, {
        id: 'Enable Editing',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            return testNodes(scope.selectedNodes, function(node) {
                return isFileNode(node) && !isEditableNode(node);
            });
        },
        build: (scope, items) => {
            items.push({
                label: 'Enable Editing',
                hotkey: 'Ctrl+E',
                callback: () => scope.enableEditing()
            })
        }
    }, {
        id: 'Build References',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            return testNodes(scope.selectedNodes, function(node) {
                return isFileNode(node) &&
                    referenceService.canBuildReferences(node.handle);
            });
        },
        build: (scope, items) => {
            items.push({
                label: 'Build References',
                hotkey: 'Ctrl+B',
                callback: () => scope.buildReferences()
            })
        }
    }, divider, {
        id: 'Automate',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Automate',
                hotkey: 'Ctrl+M',
                callback: () => scope.$emit('openModal', 'automate', {
                    targetScope: scope
                })
            })
        }
    }, {
        id: 'Advanced Search',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Advanced Search',
                hotkey: 'Ctrl+Shift+F',
                disabled: true,
                callback: () => scope.$emit('openModal', 'advancedSearch')
            })
        }
    }, {
        id: 'Open',
        visible: (scope) => {
            let node = scope.selectedNodes.last();
            return node && !isGroupNode(node);
        },
        build: (scope, items) => {
            let node = scope.selectedNodes.last();
            items.push({
                label: 'Open in Record View',
                hotkey: 'Enter',
                callback: () => scope.open(node)
            })
        }
    }, divider, {
        id: 'Copy to',
        visible: () => { return true; },
        build: (scope, items) => {
            items.push({
                label: 'Copy into',
                hotkey: 'Ctrl+Alt+C',
                disabled: scope.selectedNodes.length === 0,
                callback: () => scope.copyInto()
            })
        }
    },{
        id: 'Copy',
        visible: (scope) => { return scope.selectedNodes.length > 0 },
        build: (scope, items) => {
            items.push({
                label: 'Copy',
                hotkey: 'Ctrl+C',
                disabled: !scope.canCopy(),
                callback: () => scope.copyNodes()
            })
        }
    }, {
        id: 'Copy Path',
        visible: (scope) => { return scope.selectedNodes.length > 0 },
        build: (scope, items) => {
            items.push({
                label: 'Copy Path',
                hotkey: 'Ctrl+Shift+C',
                disabled: scope.selectedNodes.length === 0,
                callback: () => scope.copyPaths()
            })
        }
    }, {
        id: 'Paste',
        visible: (scope) => { return scope.selectedNodes.length > 0 },
        build: (scope, items) => {
            items.push({
                label: 'Paste',
                hotkey: 'Ctrl+V',
                disabled: !scope.canPaste(),
                callback: () => scope.pasteNodes(true)
            })
        }
    }, {
        id: 'Paste as Override',
        visible: (scope) => { return scope.selectedNodes.length > 0 },
        build: (scope, items) => {
            items.push({
                label: 'Paste as Override',
                hotkey: 'Ctrl+Shift+V',
                disabled: !scope.canPaste(),
                callback: () => scope.pasteNodes()
            })
        }
    }];

    this.recordViewItems = [{
        id: 'Add',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            let node = scope.selectedNodes.last(),
                index = scope.focusedIndex - 1,
                handle = node.handles[index],
                record = index ? scope.overrides[index - 1] : scope.record,
                parentAvailable = !node.depth || (node.parent && node.parent.handles[index]);
            if (!xelib.GetIsEditable(record)) return false;
            return parentAvailable && (handle === 0 || node.value_type === xelib.vtArray);
        },
        build: (scope, items) => {
            let node = scope.selectedNodes.last(),
                index = scope.focusedIndex - 1;
            items.push({
                label: 'Add',
                hotkey: 'Insert',
                callback: () => scope.addElement(node, index)
            });
        }
    }, {
        id: 'Edit',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            let node = scope.selectedNodes.last(),
                index = scope.focusedIndex - 1,
                handle = node.handles[index],
                record = index === 0 ? scope.record : scope.overrides[index - 1];
            if (!xelib.GetIsEditable(record)) return false;
            return handle !== 0 && !uneditableValueTypes.includes(node.value_type);
        },
        build: (scope, items) => {
            let node = scope.selectedNodes.last(),
                index = scope.focusedIndex;
            items.push({
                label: 'Edit',
                hotkey: 'Enter',
                callback: () => scope.editElement(node, index)
            });
        }
    }, {
        id: 'Delete',
        visible: (scope) => {
            if (!scope.selectedNodes.length) return;
            let index = scope.focusedIndex - 1;
            return testNodes(scope.selectedNodes, function(node) {
                let handle = node.handles[index];
                return handle && xelib.GetIsRemoveable(handle);
            });
        },
        build: (scope, items) => {
            items.push({
                label: 'Delete',
                hotkey: 'Del',
                callback: () => scope.deleteElements()
            });
        }
    }, divider, {
        id: 'Copy',
        visible: () => { return true; },
        build: (scope, items) => {
            //let node = scope.selectedNodes.last();
            items.push({
                label: 'Copy',
                hotkey: 'Ctrl+C',
                disabled: true,//!node,
                callback: () => scope.copyNodes()
            })
        }
    }, {
        id: 'Copy Path',
        visible: () => { return true; },
        build: (scope, items) => {
            //let node = scope.selectedNodes.last();
            items.push({
                label: 'Copy Path',
                hotkey: 'Ctrl+Shift+C',
                disabled: true,//!node,
                callback: () => scope.copyNodePaths()
            })
        }
    }, {
        id: 'Paste',
        visible: () => { return true },
        build: (scope, items) => {
            items.push({
                label: 'Paste',
                hotkey: 'Ctrl+V',
                disabled: true,//!scope.canPaste(),
                callback: () => scope.paste()
            })
        }
    }]
});
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table';
import { Button, OverlayTrigger, Popover } from 'react-bootstrap';
import { Icon } from 'react-fa';
import Linkout from '../Linkout';
import { MiniStrip, Block } from '../Ribbon';
import './OrthologResult.css';
import '../ribbon.css';

const sourceTitles = ['Compara','HGNC','Homologene','Inparanoid','Isobase','OMA','OrthoDB','orthoMCL','Panther','Phylome','RoundUp','TreeFam','ZFIN'];

export default class OrthologResult extends Component {

    constructor(props) {
        super(props);
    }

    componentDidMount() {
        console.debug("orthologs mounted");
        const { gene, taxid } = this.props.params;
        //If we got here via a page refresh, reload the orthologs.
        if (this.props.orthologs && this.props.orthologs.length == 0) {
            this.props.fetchOrtholog(gene,taxid);
        }
    }

    miniReport(row) {
        console.log('[miniReport]');

            console.log(row);

    }

    renderSpecies(speciesTxt) {
        if( speciesTxt ) {
            // speciesTxt should look like "Species name (common name[, another common name])"
            var spTokens = speciesTxt.match(/(.*) \((.*)\)/);
            // spTokens[1] is scientific name, spTokens[2] is common name(s)
            var Gspc = spTokens[1].replace(/^(.).*\s/, "$1. ");
           // return({Gspc} ({spTokens[2]}));
            return({Gspc});
        }
        return null;
    }

    renderLinkouts(linkouts) {
        if (linkouts) {
            return (

                    {linkouts.map(l => {
                        if (!l.startsWith("OMIM") && !l.startsWith("Ensembl_")) {
                            return (

                                    <Linkout id={l} />

                            );
                        }
                    })}

            );
        }
        return null;
    }

    sourceTitles() {
        let titles = [];
        for (let s of sourceTitles) {
            titles.push(
{s}
);
        }

        return titles;
    }

    GOtitles() {
        let titles = [];
        for( let g of ['Mol Func','Cell Comp','Biol Proc'] ) {
            titles.push(
{g}
);
        }
        return titles;
    }

    renderSource(sources) {
        let sourceChecks = [];
        if (sources) {
            for (let s of sourceTitles) {
                let styles = {
                    "paddingRight": "0.2em",
                    "paddingLeft": "0.2em"
                };

                if (sources.findIndex((el,i) => el.toUpperCase() === s.toUpperCase()) != -1) {
                    sourceChecks.push(
<Icon name="check-square-o" fixedWidth={true} />
);
                }
                else {
                    sourceChecks.push(
<Icon name="square-o" fixedWidth={true} />
);
                }

            }

            return (
{sourceChecks}
);

        }
        return null;
    }

    renderCheck(val) {
        if (val && val.toUpperCase() == 'YES') {
            return <Icon name="check" />;
        }
        return null;
    }

    renderGO( GOobj ) {
        // GOobj: { C: { name:'Cellular Component', terms: [{},{},...], count: # }, P: {}, F: {} }
        return(<MiniStrip ribbondata={GOobj} />)
      //  return
{Object.keys(GOobj)}
;
    }

    renderPubs( publications ) {
        return ({publications.length});
    }

    sortByScore(a, b, order, field, score) {
        if (order === 'desc') {
            if (a.score > b.score) {
                return -1;
            }
            else if (a.score < b.score) {
                return 1;
            }
            return 0;
        }

        if (a.score  b.score) {
            return 1;
        }
        return 0;
    }

    renderSortIndicator(order) {
        let icon = <Icon name="sort" className="text-muted" />;

        if (order === 'desc') {
             icon = <Icon name="sort-desc" />;
        }
        else if (order === 'asc') {
            icon = <Icon name="sort-asc" />;
        }
        return (

                {icon}

        );
    }

    render() {
        console.debug("Rendering orthologs");
        const { gene, taxid } = this.props.params;
        const { orthologs } = this.props;
        const species = (orthologs[0]) ? orthologs[0].query_species : '';

        var tableOptionsObj = { onRowClick: this.miniReport };

        console.debug(orthologs);

        // if 'onClick={this.miniReport}' is added to the top-level div, the miniReport function fires (but gets no row information)
        return (

Orthologs of the {species} gene {gene}

                <BootstrapTable striped={true} hover={true} data={orthologs} options={tableOptionsObj}>
                    <TableHeaderColumn isKey={true}
                                       dataField="ortholog_gene"
                                       dataSort={true}
                                       width="100"
                                       caretRender={this.renderSortIndicator}>
                        Gene
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="target_species"
                                       caretRender={this.renderSortIndicator}
                                       dataSort={true}
                                       dataFormat={this.renderSpecies}
                                       width="120">
                        Organism
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="ortholog_gene_reports"
                                       dataFormat={this.renderLinkouts}>
                        Linkouts
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="best_score"
                                       dataAlign="center"
                                       width="50"
                                       dataFormat={this.renderCheck}
                                       caretRender={this.renderSortIndicator}
                                       dataSort={true}
                                       columnClassName={(c,r,ri,ci) =>  (c && c.toUpperCase() == 'YES') ? "success" : "" }>
                        <OverlayTrigger trigger="click" placement="top" overlay={<Popover id="best_score" title="Best Score">Indicates that the orthology call has the highest score when going from the query organism to the target organism.</Popover>}>
                             e.stopPropagation() } ><Icon name="info-circle" />
                        </OverlayTrigger>
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="best_reverse_score"
                                       dataAlign="center"
                                       width="50"
                                       dataFormat={this.renderCheck}
                                       caretRender={this.renderSortIndicator}
                                       dataSort={true}
                                       columnClassName={(c,r,ri,ci) =>  (c && c.toUpperCase() == 'YES') ? "success" : "" }>
                        <OverlayTrigger trigger="click" placement="top" overlay={<Popover id="best_reverse_score" title="Best Score - Reverse Search">Indicates that the orthology call has the highest score when going from the target organism to the query organism.</Popover>}>
                             e.stopPropagation() } ><Icon name="info-circle" />
                        </OverlayTrigger>
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="source"
                                       dataAlign="center"
                                       width="330"
                                       className="verticalTitlesTH"
                                       dataFormat={this.renderSource}
                                       caretRender={this.renderSortIndicator}
                                       dataSort={true}
                                       sortFunc={this.sortByScore}>

Prediction Dervied From

                            {this.sourceTitles()}
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="GO"
                                       dataAlign="center"
                                       className="verticalTitlesTH"
                                       dataFormat={this.renderGO}
                                       width="90">

                            GO
                            <OverlayTrigger trigger="click" placement="top" overlay={<Popover id="GO_terms" title="Gene Ontology">Color intensity indicates number of curated terms. Hover/tap to display exact number.</Popover>}>
                                 e.stopPropagation() } ><Icon name="info-circle" />
                            </OverlayTrigger>

                        {this.GOtitles()}
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="publications_number"
                                       caretRender={this.renderSortIndicator}
                                       dataSort={true}
                                       dataAlign="center"
                                       width="100">
                        Publications
                    </TableHeaderColumn>
                </BootstrapTable>

        );
    }
}
<commit_msg>Fixes bug that caused Ortholog result rows to be omitted from table<commit_after>import React, {Component} from 'react';
import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table';
import { Button, OverlayTrigger, Popover } from 'react-bootstrap';
import { Icon } from 'react-fa';
import Linkout from '../Linkout';
import { MiniStrip, Block } from '../Ribbon';
import './OrthologResult.css';
import '../ribbon.css';

const sourceTitles = ['Compara','HGNC','Homologene','Inparanoid','Isobase','OMA','OrthoDB','orthoMCL','Panther','Phylome','RoundUp','TreeFam','ZFIN'];

export default class OrthologResult extends Component {

    constructor(props) {
        super(props);
    }

    componentDidMount() {
        console.debug("orthologs mounted");
        const { gene, taxid } = this.props.params;
        //If we got here via a page refresh, reload the orthologs.
        if (this.props.orthologs && this.props.orthologs.length == 0) {
            this.props.fetchOrtholog(gene,taxid);
        }
    }

    miniReport(row) {
        console.log('[miniReport]');

            console.log(row);

    }

    renderID( reports ) {
        return({reports[0]})
    }

    renderSpecies(speciesTxt) {
        if( speciesTxt ) {
            // speciesTxt should look like "Species name (common name[, another common name])"
            var spTokens = speciesTxt.match(/(.*) \((.*)\)/);
            // spTokens[1] is scientific name, spTokens[2] is common name(s)
            var Gspc = spTokens[1].replace(/^(.).*\s/, "$1. ");
           // return({Gspc} ({spTokens[2]}));
            return({Gspc});
        }
        return null;
    }

    renderLinkouts(linkouts) {
        if (linkouts) {
            return (

                    {linkouts.map(l => {
                        if (!l.startsWith("OMIM") && !l.startsWith("Ensembl_")) {
                            return (

                                    <Linkout id={l} />

                            );
                        }
                    })}

            );
        }
        return null;
    }

    sourceTitles() {
        let titles = [];
        for (let s of sourceTitles) {
            titles.push(
{s}
);
        }

        return titles;
    }

    GOtitles() {
        let titles = [];
        for( let g of ['Mol Func','Cell Comp','Biol Proc'] ) {
            titles.push(
{g}
);
        }
        return titles;
    }

    renderSource(sources) {
        let sourceChecks = [];
        if (sources) {
            for (let s of sourceTitles) {
                let styles = {
                    "paddingRight": "0.2em",
                    "paddingLeft": "0.2em"
                };

                if (sources.findIndex((el,i) => el.toUpperCase() === s.toUpperCase()) != -1) {
                    sourceChecks.push(
<Icon name="check-square-o" fixedWidth={true} />
);
                }
                else {
                    sourceChecks.push(
<Icon name="square-o" fixedWidth={true} />
);
                }

            }

            return (
{sourceChecks}
);

        }
        return null;
    }

    renderCheck(val) {
        if (val && val.toUpperCase() == 'YES') {
            return <Icon name="check" />;
        }
        return null;
    }

    renderGO( GOobj ) {
        // GOobj: { C: { name:'Cellular Component', terms: [{},{},...], count: # }, P: {}, F: {} }
        return(<MiniStrip ribbondata={GOobj} />)
      //  return
{Object.keys(GOobj)}
;
    }

    renderPubs( publications ) {
        return ({publications.length});
    }

    sortByScore(a, b, order, field, score) {
        if (order === 'desc') {
            if (a.score > b.score) {
                return -1;
            }
            else if (a.score < b.score) {
                return 1;
            }
            return 0;
        }

        if (a.score  b.score) {
            return 1;
        }
        return 0;
    }

    renderSortIndicator(order) {
        let icon = <Icon name="sort" className="text-muted" />;

        if (order === 'desc') {
             icon = <Icon name="sort-desc" />;
        }
        else if (order === 'asc') {
            icon = <Icon name="sort-asc" />;
        }
        return (

                {icon}

        );
    }

    render() {
        console.debug("Rendering orthologs");
        const { gene, taxid } = this.props.params;
        const { orthologs } = this.props;
        const species = (orthologs[0]) ? orthologs[0].query_species : '';

        var tableOptionsObj = { onRowClick: this.miniReport };

        console.debug(orthologs);

        // if 'onClick={this.miniReport}' is added to the top-level div, the miniReport function fires (but gets no row information)
        return (

Orthologs of the {species} gene {gene}

                <BootstrapTable striped={true} hover={true} data={orthologs} options={tableOptionsObj}>
                    <TableHeaderColumn isKey={true}
                                       dataField="ortholog_gene_reports"
                                       dataFormat={this.renderID}
                                       hidden={true}>
                        ID
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="ortholog_gene"
                                       dataSort={true}
                                       width="100"
                                       caretRender={this.renderSortIndicator}>
                        Gene
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="target_species"
                                       caretRender={this.renderSortIndicator}
                                       dataSort={true}
                                       dataFormat={this.renderSpecies}
                                       width="120">
                        Organism
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="ortholog_gene_reports"
                                       dataFormat={this.renderLinkouts}>
                        Linkouts
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="best_score"
                                       dataAlign="center"
                                       width="50"
                                       dataFormat={this.renderCheck}
                                       caretRender={this.renderSortIndicator}
                                       dataSort={true}
                                       columnClassName={(c,r,ri,ci) =>  (c && c.toUpperCase() == 'YES') ? "success" : "" }>
                        <OverlayTrigger trigger="click" placement="top" overlay={<Popover id="best_score" title="Best Score">Indicates that the orthology call has the highest score when going from the query organism to the target organism.</Popover>}>
                             e.stopPropagation() } ><Icon name="info-circle" />
                        </OverlayTrigger>
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="best_reverse_score"
                                       dataAlign="center"
                                       width="50"
                                       dataFormat={this.renderCheck}
                                       caretRender={this.renderSortIndicator}
                                       dataSort={true}
                                       columnClassName={(c,r,ri,ci) =>  (c && c.toUpperCase() == 'YES') ? "success" : "" }>
                        <OverlayTrigger trigger="click" placement="top" overlay={<Popover id="best_reverse_score" title="Best Score - Reverse Search">Indicates that the orthology call has the highest score when going from the target organism to the query organism.</Popover>}>
                             e.stopPropagation() } ><Icon name="info-circle" />
                        </OverlayTrigger>
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="source"
                                       dataAlign="center"
                                       width="330"
                                       className="verticalTitlesTH"
                                       dataFormat={this.renderSource}
                                       caretRender={this.renderSortIndicator}
                                       dataSort={true}
                                       sortFunc={this.sortByScore}>

Prediction Dervied From

                            {this.sourceTitles()}
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="GO"
                                       dataAlign="center"
                                       className="verticalTitlesTH"
                                       dataFormat={this.renderGO}
                                       width="90">

                            GO
                            <OverlayTrigger trigger="click" placement="top" overlay={<Popover id="GO_terms" title="Gene Ontology">Color intensity indicates number of curated terms. Hover/tap to display exact number.</Popover>}>
                                 e.stopPropagation() } ><Icon name="info-circle" />
                            </OverlayTrigger>

                        {this.GOtitles()}
                    </TableHeaderColumn>
                    <TableHeaderColumn dataField="publications_number"
                                       caretRender={this.renderSortIndicator}
                                       dataSort={true}
                                       dataAlign="center"
                                       width="100">
                        Publications
                    </TableHeaderColumn>
                </BootstrapTable>

        );
    }
}
<|endoftext|>


<commit_before>'use strict';
/*jshint browser:true */

var MediaObject = require('./media-object');
var EmbeddedVimeoPlayer = require('../embedded-vimeo-player');
var getVimeoId = require('../get-vimeo-id');
var TweenloopInterval = require('../tween-loop-interval');
var TWEEN = require('tween.js');

function VideoMediaObject(obj, ops) {
    this._loading = false;
    this._playbackTimeInterval = null; //APEP: ##Hack## for buffering media removal at the end
    MediaObject.call(this, obj, ops);
}

VideoMediaObject.prototype = Object.create(MediaObject.prototype);
VideoMediaObject.prototype.constructor = VideoMediaObject;

VideoMediaObject.typeName = 'video';

VideoMediaObject.prototype.play = function () {
    this._loading = false;
    // vimeo player complains if you pass it 0, so we pass it just above zero
    if(this._player.isVimeo) {
        this._player.vimeo_player.setVolume(this.getVolume() || 0.00001);
        this._player.vimeo_player.play();
        this._player.vimeo_player.setLoop(this.getLooping() || false);
    } else {
        this._player.raw_player.play();
    }

    // setup transition stuff
    var transitionSeconds = this._ops.transitionDuration / 1000;
    this._player._element.style.transition = 'opacity ' + (this._ops.transitionDuration / 1000) + 's ease-in-out';

    if(this._player.isVimeo) {
        this._player.onPlayProgress(function (data) {
            if ((data.duration - data.seconds) < transitionSeconds || data.duration < transitionSeconds) {
                console.log("Transition video player out - vimeo");
                this.transition();
            }
        }.bind(this));
    } else {
        var self = this;
        this._player._element.addEventListener('ended',function(e) {
            self.transition();
        },false);

        //APEP: ##Hack## for buffering media removal at the end
        if(this.play_duration !== null && this.play_duration > 0) {
            if(this._playbackTimeInterval) clearTimeout(this._playbackTimeInterval);
            this._playbackTimeInterval = setTimeout(function() {
                console.log("Buffering play duration failure - transition media object: ", self);
                self.transition();
            }, this.play_duration * 1.15 * 1000);
        }

    }

    MediaObject.prototype.play.call(this);
};

VideoMediaObject.prototype.makeElement = function (callback) {

    var isVimeo = this._obj.url.indexOf("vimeo.com") !== -1;
    var videoUrl = isVimeo ? getVimeoId(this._obj.url) : this._obj.url;

    var player = new EmbeddedVimeoPlayer(isVimeo, videoUrl);

    this._loading = true;

    this._player = player;

    if(!this._player.isVimeo) { //APEP: ##Hack## for buffering media removal at the end
        try {
            //APEP: ##Hack## for buffering media removal at the end
            this._player.raw_player.retrieveManifest(this._player.player_url, function(manifest){
                this.play_duration = manifest.Period.duration;
                callback();
            }.bind(this));
        } catch(e) {
            console.log("e: ", e);
            return callback();
        }
    }

    return callback();

};

VideoMediaObject.prototype.getLooping = function () {
    return !(this.autoreplay == undefined || this.autoreplay < 1);
};
VideoMediaObject.prototype.getVolume = function () {
    var volume = this._obj.volume;
    if (isNaN(volume)) {
        volume = 100;
    }
    return volume / 100;
};

VideoMediaObject.prototype.transition = function () {

    //APEP: ##Hack## for buffering media removal at the end
    if(this._playbackTimeInterval) clearTimeout(this._playbackTimeInterval);

    if (this._loading) {
        this._loading = false;
        this._player.onReady(null);
    } else if (this._playing) {
        this._playing = false;
        this.emit('transition', this);

        var position = {vol: this.getVolume()},
            target = {vol: 0.90},
            self = this;
        new TWEEN.Tween(position)
            .to(target, this._ops.transitionDuration)
            .onUpdate(function () {
                if(self._player.isVimeo)
                    self._player.vimeo_player.setVolume(position.vol);
                else
                    self._player.raw_player.setVolume(position.vol); //TODO fix
            })
            .onComplete(function () {
                self.emit('done', self);
            })
            .onStop(function () {
                self.emit('done', self);
            })
            .start();
    }

    MediaObject.prototype.transition.call(this);
};

VideoMediaObject.prototype.stop = function () {
    if (this._loading || this._playing) {
        this._loading = this._playing = false;
        this.emit('done', this);
    }
};

VideoMediaObject.prototype.onReady = function (callback) {
    this._player.onReady(callback);
};

module.exports = VideoMediaObject;
<commit_msg>After video is completed, during transition, ensure the player is reset in an attempt to clear GPU memory<commit_after>'use strict';
/*jshint browser:true */

var MediaObject = require('./media-object');
var EmbeddedVimeoPlayer = require('../embedded-vimeo-player');
var getVimeoId = require('../get-vimeo-id');
var TweenloopInterval = require('../tween-loop-interval');
var TWEEN = require('tween.js');

function VideoMediaObject(obj, ops) {
    this._loading = false;
    this._playbackTimeInterval = null; //APEP: ##Hack## for buffering media removal at the end
    MediaObject.call(this, obj, ops);
}

VideoMediaObject.prototype = Object.create(MediaObject.prototype);
VideoMediaObject.prototype.constructor = VideoMediaObject;

VideoMediaObject.typeName = 'video';

VideoMediaObject.prototype.play = function () {
    this._loading = false;
    // vimeo player complains if you pass it 0, so we pass it just above zero
    if(this._player.isVimeo) {
        this._player.vimeo_player.setVolume(this.getVolume() || 0.00001);
        this._player.vimeo_player.play();
        this._player.vimeo_player.setLoop(this.getLooping() || false);
    } else {
        this._player.raw_player.play();
    }

    // setup transition stuff
    var transitionSeconds = this._ops.transitionDuration / 1000;
    this._player._element.style.transition = 'opacity ' + (this._ops.transitionDuration / 1000) + 's ease-in-out';

    if(this._player.isVimeo) {
        this._player.onPlayProgress(function (data) {
            if ((data.duration - data.seconds) < transitionSeconds || data.duration < transitionSeconds) {
                console.log("Transition video player out - vimeo");
                this.transition();
            }
        }.bind(this));
    } else {
        var self = this;
        this._player._element.addEventListener('ended',function(e) {
            self.transition();
        },false);

        //APEP: ##Hack## for buffering media removal at the end
        if(this.play_duration !== null && this.play_duration > 0) {
            if(this._playbackTimeInterval) clearTimeout(this._playbackTimeInterval);
            this._playbackTimeInterval = setTimeout(function() {
                console.log("Buffering play duration failure - transition media object: ", self);
                self.transition();
            }, this.play_duration * 1.15 * 1000);
        }

    }

    MediaObject.prototype.play.call(this);
};

VideoMediaObject.prototype.makeElement = function (callback) {

    var isVimeo = this._obj.url.indexOf("vimeo.com") !== -1;
    var videoUrl = isVimeo ? getVimeoId(this._obj.url) : this._obj.url;

    var player = new EmbeddedVimeoPlayer(isVimeo, videoUrl);

    this._loading = true;

    this._player = player;

    if(!this._player.isVimeo) { //APEP: ##Hack## for buffering media removal at the end
        try {
            //APEP: ##Hack## for buffering media removal at the end
            this._player.raw_player.retrieveManifest(this._player.player_url, function(manifest){
                this.play_duration = manifest.Period.duration;
                callback();
            }.bind(this));
        } catch(e) {
            console.log("e: ", e);
            return callback();
        }
    }

    return callback();

};

VideoMediaObject.prototype.getLooping = function () {
    return !(this.autoreplay == undefined || this.autoreplay < 1);
};
VideoMediaObject.prototype.getVolume = function () {
    var volume = this._obj.volume;
    if (isNaN(volume)) {
        volume = 100;
    }
    return volume / 100;
};

VideoMediaObject.prototype.transition = function () {

    //APEP: ##Hack## for buffering media removal at the end
    if(this._playbackTimeInterval) clearTimeout(this._playbackTimeInterval);

    if (this._loading) {
        this._loading = false;
        this._player.onReady(null);
    } else if (this._playing) {
        this._playing = false;
        this.emit('transition', this);

        var position = {vol: this.getVolume()},
            target = {vol: 0.90},
            self = this;
        new TWEEN.Tween(position)
            .to(target, this._ops.transitionDuration)
            .onUpdate(function () {
                if(self._player.isVimeo)
                    self._player.vimeo_player.setVolume(position.vol);
                else
                    self._player.raw_player.setVolume(position.vol); //TODO fix
            })
            .onComplete(function () {
                if(!self._player.isVimeo) {
                    //APEP: Ensure player is reset in attempt for it to clean its GPU memory
                    self._player.raw_player.reset();
                }
                self.emit('done', self);
            })
            .onStop(function () {
                if(!self._player.isVimeo) {
                    //APEP: Ensure player is reset in attempt for it to clean its GPU memory
                    self._player.raw_player.reset();
                }
                self.emit('done', self);
            })
            .start();
    }

    MediaObject.prototype.transition.call(this);
};

VideoMediaObject.prototype.stop = function () {
    if (this._loading || this._playing) {
        this._loading = this._playing = false;
        this.emit('done', this);
    }
};

VideoMediaObject.prototype.onReady = function (callback) {
    this._player.onReady(callback);
};

module.exports = VideoMediaObject;
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'
import Helmet from 'react-helmet'
import { shallowEqual } from 'react-pure-render'

import CurrentStatusMessage from '../../common/containers/CurrentStatusMessage'
import ConfirmModal from '../../common/components/ConfirmModal.js'
import CurrentStatusModal from '../../common/containers/CurrentStatusModal'
import EditorMap from './map/EditorMap'
import EditorHelpModal from './EditorHelpModal'
import EditorSidebar from './EditorSidebar'
import ActiveEntityList from '../containers/ActiveEntityList'
import EntityDetails from './EntityDetails'
import ActiveTimetableEditor from '../containers/ActiveTimetableEditor'
import ActiveFeedInfoPanel from '../containers/ActiveFeedInfoPanel'

import { getConfigProperty } from '../../common/util/config'

export default class GtfsEditor extends Component {
  static propTypes = {
    currentTable: PropTypes.string,
    feedSourceId: PropTypes.string,
    feedSource: PropTypes.object,
    project: PropTypes.object,
    user: PropTypes.object,
    tableData: PropTypes.object,
    feedInfo: PropTypes.object,
    mapState: PropTypes.object,

    entities: PropTypes.array,

    onComponentMount: PropTypes.func,
    onComponentUpdate: PropTypes.func,
    clearGtfsContent: PropTypes.func,
    getGtfsTable: PropTypes.func,
    fetchTripPatternsForRoute: PropTypes.func,
    fetchTripsForCalendar: PropTypes.func,
    saveTripsForCalendar: PropTypes.func,
    deleteTripsForCalendar: PropTypes.func,
    setActiveEntity: PropTypes.func,
    updateActiveEntity: PropTypes.func,
    saveActiveEntity: PropTypes.func,
    resetActiveEntity: PropTypes.func,
    deleteEntity: PropTypes.func,
    cloneEntity: PropTypes.func,
    newGtfsEntity: PropTypes.func,
    setTutorialHidden: PropTypes.func,

    sidebarExpanded: PropTypes.bool,

    activeEntity: PropTypes.object,
    activeEntityId: PropTypes.string,
    subEntityId: PropTypes.string,
    activeSubSubEntity: PropTypes.string,
    activeComponent: PropTypes.string,
    subSubComponent: PropTypes.string
  }
  constructor (props) {
    super(props)

    this.state = {
      activeTableId: this.props.currentTable
    }
  }
  componentWillMount () {
    this.props.onComponentMount(this.props)
  }
  componentDidUpdate (prevProps) {
    // handles back button
    this.props.onComponentUpdate(prevProps, this.props)
  }
  componentWillReceiveProps (nextProps) {
    // clear GTFS content if feedSource changes (i.e., user switches feed sources)
    if (nextProps.feedSourceId !== this.props.feedSourceId) {
      this.props.clearGtfsContent()
      this.props.onComponentMount(nextProps)
      this.props.getGtfsTable('calendar', this.props.feedSourceId)
    }
    // fetch table if it doesn't exist already and user changes tabs
    if (nextProps.activeComponent && nextProps.activeComponent !== this.props.activeComponent && !nextProps.tableData[nextProps.activeComponent]) {
      this.props.getGtfsTable(nextProps.activeComponent, nextProps.feedSource.id)
    }
    // fetch sub components of active entity on active entity switch (e.g., fetch trip patterns when route changed)
    if (nextProps.activeComponent === 'route' && nextProps.feedSource && nextProps.activeEntity && (!this.props.activeEntity || nextProps.activeEntity.id !== this.props.activeEntity.id)) {
      this.props.fetchTripPatternsForRoute(nextProps.feedSource.id, nextProps.activeEntity.id)
    }
    // fetch required sub sub component entities if active sub entity changes
    if (nextProps.subSubComponent && nextProps.activeSubSubEntity && !shallowEqual(nextProps.activeSubSubEntity, this.props.activeSubSubEntity)) {
      switch (nextProps.subSubComponent) {
        case 'timetable':
          let pattern = nextProps.activeEntity.tripPatterns.find(p => p.id === nextProps.subEntityId)
          // fetch trips if they haven't been fetched
          if (!pattern[nextProps.activeSubSubEntity]) {
            this.props.fetchTripsForCalendar(nextProps.feedSource.id, pattern, nextProps.activeSubSubEntity)
          }
          break
      }
    }
  }

  showConfirmModal (props) {
    this.refs.confirmModal.open(props)
  }
  getMapOffset (activeComponent, dWidth, activeEntityId, lWidth) {
    return activeComponent === 'feedinfo'
      ? dWidth
      : activeEntityId
      ? lWidth + dWidth
      : activeComponent
      ? lWidth
      : 0
  }
  render () {
    const {
      feedSource,
      user,
      activeEntityId,
      tableData,
      entities,
      activeComponent,
      sidebarExpanded,
      feedInfo,
      setActiveEntity,
      subSubComponent,
      activeEntity,
      subEntityId,
      activeSubSubEntity,
      deleteEntity,
      updateActiveEntity,
      resetActiveEntity,
      saveActiveEntity,
      fetchTripsForCalendar,
      updateCellValue,
      cloneEntity,
      newGtfsEntity,
      mapState,
      hideTutorial,
      setTutorialHidden,
      project
    } = this.props
    const editingIsDisabled = feedSource ? !user.permissions.hasFeedPermission(feedSource.projectId, feedSource.id, 'edit-gtfs') : true
    if (feedSource && editingIsDisabled) {
      console.log('editing disabled')
      // browserHistory.push(`/feed/${feedSource.id}`)
    }
    let LIST_WIDTH = 220
    let DETAILS_WIDTH = 300
    let entityDetails = activeEntityId
      ? <EntityDetails
        width={DETAILS_WIDTH}
        key='entity-details'
        offset={LIST_WIDTH}
        stops={tableData.stop}
        showConfirmModal={(props) => this.showConfirmModal(props)}
        {...this.props}
        getGtfsEntity={(type, id) => {
          return entities.find(ent => ent.id === id)
        }}
        getGtfsEntityIndex={(type, id) => {
          return entities.findIndex(ent => ent.id === id)
        }} />
      : null
    const defaultTitle = `${getConfigProperty('application.title')}: GTFS Editor`
    return (

        <Helmet
          defaultTitle={defaultTitle}
          titleTemplate={`${defaultTitle} - %s`} />
        <EditorSidebar
          activeComponent={activeComponent}
          expanded={sidebarExpanded}
          feedSource={feedSource}
          feedInfo={feedInfo}
          setActiveEntity={setActiveEntity} />

          {subSubComponent === 'timetable' // && activeEntity
            ? <ActiveTimetableEditor
              feedSource={feedSource}
              route={activeEntity}
              showConfirmModal={(props) => this.showConfirmModal(props)}
              activePatternId={subEntityId}
              activeScheduleId={activeSubSubEntity}
              setActiveEntity={setActiveEntity}
              tableData={tableData}
              deleteEntity={deleteEntity}
              updateActiveEntity={updateActiveEntity}
              resetActiveEntity={resetActiveEntity}
              saveActiveEntity={saveActiveEntity}
              fetchTripsForCalendar={fetchTripsForCalendar}
              sidebarExpanded={sidebarExpanded}
              updateCellValue={updateCellValue} />
            : activeComponent === 'feedinfo'
            ? <EntityDetails
              width={DETAILS_WIDTH}
              {...this.props} />
            : activeComponent
            ? [
              <ActiveEntityList
                width={LIST_WIDTH}
                setActiveEntity={setActiveEntity}
                cloneEntity={cloneEntity}
                updateActiveEntity={updateActiveEntity}
                deleteEntity={deleteEntity}
                newGtfsEntity={newGtfsEntity}
                showConfirmModal={(props) => this.showConfirmModal(props)}
                entities={entities}
                activeEntityId={activeEntityId}
                activeComponent={activeComponent}
                feedSource={feedSource}
                key='entity-list' />,
              entityDetails
            ]
            : null
          }
          <EditorMap
            offset={this.getMapOffset(activeComponent, DETAILS_WIDTH, activeEntityId, LIST_WIDTH)}
            hidden={subSubComponent === 'timetable'}
            stops={tableData.stop || []}
            showConfirmModal={(props) => this.showConfirmModal(props)}
            drawStops={mapState.zoom > 14} // && (activeComponent === 'stop' || editSettings.addStops)}
            zoomToTarget={mapState.target}
            sidebarExpanded={sidebarExpanded}
            {...this.props} />
          {!activeComponent && !hideTutorial
            ? <EditorHelpModal
              show
              setTutorialHidden={setTutorialHidden} />
            : null
          }
          <ActiveFeedInfoPanel
            feedSource={feedSource}
            project={project}
            showConfirmModal={(props) => this.showConfirmModal(props)}
            setActiveEntity={setActiveEntity}
            feedInfo={feedInfo} />

        <CurrentStatusMessage />
        <ConfirmModal ref='confirmModal' />
        <CurrentStatusModal ref='statusModal' />

    )
  }
}
<commit_msg>hide help modal permanently for now<commit_after>import React, {Component, PropTypes} from 'react'
import Helmet from 'react-helmet'
import { shallowEqual } from 'react-pure-render'

import CurrentStatusMessage from '../../common/containers/CurrentStatusMessage'
import ConfirmModal from '../../common/components/ConfirmModal.js'
import CurrentStatusModal from '../../common/containers/CurrentStatusModal'
import EditorMap from './map/EditorMap'
import EditorHelpModal from './EditorHelpModal'
import EditorSidebar from './EditorSidebar'
import ActiveEntityList from '../containers/ActiveEntityList'
import EntityDetails from './EntityDetails'
import ActiveTimetableEditor from '../containers/ActiveTimetableEditor'
import ActiveFeedInfoPanel from '../containers/ActiveFeedInfoPanel'

import { getConfigProperty } from '../../common/util/config'

export default class GtfsEditor extends Component {
  static propTypes = {
    currentTable: PropTypes.string,
    feedSourceId: PropTypes.string,
    feedSource: PropTypes.object,
    project: PropTypes.object,
    user: PropTypes.object,
    tableData: PropTypes.object,
    feedInfo: PropTypes.object,
    mapState: PropTypes.object,

    entities: PropTypes.array,

    onComponentMount: PropTypes.func,
    onComponentUpdate: PropTypes.func,
    clearGtfsContent: PropTypes.func,
    getGtfsTable: PropTypes.func,
    fetchTripPatternsForRoute: PropTypes.func,
    fetchTripsForCalendar: PropTypes.func,
    saveTripsForCalendar: PropTypes.func,
    deleteTripsForCalendar: PropTypes.func,
    setActiveEntity: PropTypes.func,
    updateActiveEntity: PropTypes.func,
    saveActiveEntity: PropTypes.func,
    resetActiveEntity: PropTypes.func,
    deleteEntity: PropTypes.func,
    cloneEntity: PropTypes.func,
    newGtfsEntity: PropTypes.func,
    setTutorialHidden: PropTypes.func,

    sidebarExpanded: PropTypes.bool,

    activeEntity: PropTypes.object,
    activeEntityId: PropTypes.string,
    subEntityId: PropTypes.string,
    activeSubSubEntity: PropTypes.string,
    activeComponent: PropTypes.string,
    subSubComponent: PropTypes.string
  }
  constructor (props) {
    super(props)

    this.state = {
      activeTableId: this.props.currentTable
    }
  }
  componentWillMount () {
    this.props.onComponentMount(this.props)
  }
  componentDidUpdate (prevProps) {
    // handles back button
    this.props.onComponentUpdate(prevProps, this.props)
  }
  componentWillReceiveProps (nextProps) {
    // clear GTFS content if feedSource changes (i.e., user switches feed sources)
    if (nextProps.feedSourceId !== this.props.feedSourceId) {
      this.props.clearGtfsContent()
      this.props.onComponentMount(nextProps)
      this.props.getGtfsTable('calendar', this.props.feedSourceId)
    }
    // fetch table if it doesn't exist already and user changes tabs
    if (nextProps.activeComponent && nextProps.activeComponent !== this.props.activeComponent && !nextProps.tableData[nextProps.activeComponent]) {
      this.props.getGtfsTable(nextProps.activeComponent, nextProps.feedSource.id)
    }
    // fetch sub components of active entity on active entity switch (e.g., fetch trip patterns when route changed)
    if (nextProps.activeComponent === 'route' && nextProps.feedSource && nextProps.activeEntity && (!this.props.activeEntity || nextProps.activeEntity.id !== this.props.activeEntity.id)) {
      this.props.fetchTripPatternsForRoute(nextProps.feedSource.id, nextProps.activeEntity.id)
    }
    // fetch required sub sub component entities if active sub entity changes
    if (nextProps.subSubComponent && nextProps.activeSubSubEntity && !shallowEqual(nextProps.activeSubSubEntity, this.props.activeSubSubEntity)) {
      switch (nextProps.subSubComponent) {
        case 'timetable':
          let pattern = nextProps.activeEntity.tripPatterns.find(p => p.id === nextProps.subEntityId)
          // fetch trips if they haven't been fetched
          if (!pattern[nextProps.activeSubSubEntity]) {
            this.props.fetchTripsForCalendar(nextProps.feedSource.id, pattern, nextProps.activeSubSubEntity)
          }
          break
      }
    }
  }

  showConfirmModal (props) {
    this.refs.confirmModal.open(props)
  }
  getMapOffset (activeComponent, dWidth, activeEntityId, lWidth) {
    return activeComponent === 'feedinfo'
      ? dWidth
      : activeEntityId
      ? lWidth + dWidth
      : activeComponent
      ? lWidth
      : 0
  }
  render () {
    const {
      feedSource,
      user,
      activeEntityId,
      tableData,
      entities,
      activeComponent,
      sidebarExpanded,
      feedInfo,
      setActiveEntity,
      subSubComponent,
      activeEntity,
      subEntityId,
      activeSubSubEntity,
      deleteEntity,
      updateActiveEntity,
      resetActiveEntity,
      saveActiveEntity,
      fetchTripsForCalendar,
      updateCellValue,
      cloneEntity,
      newGtfsEntity,
      mapState,
      hideTutorial,
      setTutorialHidden,
      project
    } = this.props
    const editingIsDisabled = feedSource ? !user.permissions.hasFeedPermission(feedSource.projectId, feedSource.id, 'edit-gtfs') : true
    if (feedSource && editingIsDisabled) {
      console.log('editing disabled')
      // browserHistory.push(`/feed/${feedSource.id}`)
    }
    let LIST_WIDTH = 220
    let DETAILS_WIDTH = 300
    let entityDetails = activeEntityId
      ? <EntityDetails
        width={DETAILS_WIDTH}
        key='entity-details'
        offset={LIST_WIDTH}
        stops={tableData.stop}
        showConfirmModal={(props) => this.showConfirmModal(props)}
        {...this.props}
        getGtfsEntity={(type, id) => {
          return entities.find(ent => ent.id === id)
        }}
        getGtfsEntityIndex={(type, id) => {
          return entities.findIndex(ent => ent.id === id)
        }} />
      : null
    const defaultTitle = `${getConfigProperty('application.title')}: GTFS Editor`
    return (

        <Helmet
          defaultTitle={defaultTitle}
          titleTemplate={`${defaultTitle} - %s`} />
        <EditorSidebar
          activeComponent={activeComponent}
          expanded={sidebarExpanded}
          feedSource={feedSource}
          feedInfo={feedInfo}
          setActiveEntity={setActiveEntity} />

          {subSubComponent === 'timetable' // && activeEntity
            ? <ActiveTimetableEditor
              feedSource={feedSource}
              route={activeEntity}
              showConfirmModal={(props) => this.showConfirmModal(props)}
              activePatternId={subEntityId}
              activeScheduleId={activeSubSubEntity}
              setActiveEntity={setActiveEntity}
              tableData={tableData}
              deleteEntity={deleteEntity}
              updateActiveEntity={updateActiveEntity}
              resetActiveEntity={resetActiveEntity}
              saveActiveEntity={saveActiveEntity}
              fetchTripsForCalendar={fetchTripsForCalendar}
              sidebarExpanded={sidebarExpanded}
              updateCellValue={updateCellValue} />
            : activeComponent === 'feedinfo'
            ? <EntityDetails
              width={DETAILS_WIDTH}
              {...this.props} />
            : activeComponent
            ? [
              <ActiveEntityList
                width={LIST_WIDTH}
                setActiveEntity={setActiveEntity}
                cloneEntity={cloneEntity}
                updateActiveEntity={updateActiveEntity}
                deleteEntity={deleteEntity}
                newGtfsEntity={newGtfsEntity}
                showConfirmModal={(props) => this.showConfirmModal(props)}
                entities={entities}
                activeEntityId={activeEntityId}
                activeComponent={activeComponent}
                feedSource={feedSource}
                key='entity-list' />,
              entityDetails
            ]
            : null
          }
          <EditorMap
            offset={this.getMapOffset(activeComponent, DETAILS_WIDTH, activeEntityId, LIST_WIDTH)}
            hidden={subSubComponent === 'timetable'}
            stops={tableData.stop || []}
            showConfirmModal={(props) => this.showConfirmModal(props)}
            drawStops={mapState.zoom > 14} // && (activeComponent === 'stop' || editSettings.addStops)}
            zoomToTarget={mapState.target}
            sidebarExpanded={sidebarExpanded}
            {...this.props} />
          {!activeComponent && !hideTutorial && false
            ? <EditorHelpModal
              show
              setTutorialHidden={setTutorialHidden} />
            : null
          }
          <ActiveFeedInfoPanel
            feedSource={feedSource}
            project={project}
            showConfirmModal={(props) => this.showConfirmModal(props)}
            setActiveEntity={setActiveEntity}
            feedInfo={feedInfo} />

        <CurrentStatusMessage />
        <ConfirmModal ref='confirmModal' />
        <CurrentStatusModal ref='statusModal' />

    )
  }
}
<|endoftext|>


<commit_before>const imcms = require('imcms');
const sessionTimeoutManagement = require('imcms-session-timeout-management');
const $ = require('jquery');
const logger = require('imcms-logger');

const API_PREFIX = "/api";
let counter = 0;

function logAjaxRequest(type, url, count, data) {
    const withData = (data !== null && data !== undefined);

    const groupLogger = logger.group(`${type} ${url}`);

    groupLogger.start();

    groupLogger.log(`%c AJAX ${type} call: ${url}${withData ? ' with request: ' : ''}`, 'color: blue;');
    withData && groupLogger.log(data);
    groupLogger.time(`${count} : ${url}`);
}

function logAjaxResponse(type, url, count, response) {
    const hasResponse = (response !== null) && (response !== undefined) && (response !== ''); // false and 0 is legal

    const groupLogger = logger.group(`${type} ${url}`);

    groupLogger.timeEnd(`${count} : ${url}`);
    groupLogger.log(`%c AJAX ${type} call done: ${url}${hasResponse ? ' response: ' : ''}`, 'color: green;');
    hasResponse && groupLogger.log(response);

    groupLogger.finish();
}

function ajax(data, callback) {
    const url = imcms.contextPath + API_PREFIX + this.url;
    const type = this.type;
    const contentType = (this.contentType === undefined) // exactly
        ? ('application/' + (this.json ? 'json' : 'x-www-form-urlencoded') + '; charset=UTF-8')
        : this.contentType;

    const count = ++counter;

    const ajaxObj = {
        url: url,
        type: type,
        contentType: contentType,
        processData: this.processData,
        data: this.json ? JSON.stringify(data) : data,

        success: function (response) {
            sessionTimeoutManagement.initOrUpdateSessionTimeout();
            logAjaxResponse(type, url, count, response);
            callback && callback(response);
        },

        error: response => {
            console.groupEnd();
            console.error(response);
        }
    };

    logAjaxRequest(type, url, count, data);

    return $.ajax(ajaxObj);
}

function get(path) {
    return ajax.bind({url: path, type: "GET", json: false});
}

function post(path) {
    return ajax.bind({url: path, type: "POST", json: true});
}

function postFiles(path) {
    return ajax.bind({url: path, type: "POST", json: false, contentType: false, processData: false});
}

function patch(path) {
    return ajax.bind({url: path, type: "PATCH", json: true});
}

function put(path) {
    return ajax.bind({url: path, type: "PUT", json: true});
}

function remove(path) {
    return ajax.bind({url: path, type: "DELETE", json: true});
}

class API {
    constructor(url) {
        this.create = post(url);
        this.read = get(url);
        this.update = patch(url);
        this.replace = put(url);
        this.remove = remove(url);
        this.postFiles = postFiles(url);
    }
}

module.exports = {
    ajax: ajax,
    /**
     * Always call {@code new API()}
     */
    API: API
};
<commit_msg>Issue IMCMS-505: Button for recache - Add condition;<commit_after>const imcms = require('imcms');
const sessionTimeoutManagement = require('imcms-session-timeout-management');
const $ = require('jquery');
const logger = require('imcms-logger');

const API_PREFIX = "/api";
let counter = 0;

function logAjaxRequest(type, url, count, data) {
    const withData = (data !== null && data !== undefined);

    const groupLogger = logger.group(`${type} ${url}`);

    groupLogger.start();

    groupLogger.log(`%c AJAX ${type} call: ${url}${withData ? ' with request: ' : ''}`, 'color: blue;');
    withData && groupLogger.log(data);
    groupLogger.time(`${count} : ${url}`);
}

function logAjaxResponse(type, url, count, response) {
    const hasResponse = (response !== null) && (response !== undefined) && (response !== ''); // false and 0 is legal

    const groupLogger = logger.group(`${type} ${url}`);

    groupLogger.timeEnd(`${count} : ${url}`);
    groupLogger.log(`%c AJAX ${type} call done: ${url}${hasResponse ? ' response: ' : ''}`, 'color: green;');
    hasResponse && groupLogger.log(response);

    groupLogger.finish();
}

function ajax(data, callback) {
    const url = (this.withoutPrefix === undefined)
        ? imcms.contextPath + API_PREFIX + this.url
        : imcms.contextPath + this.url;
    const type = this.type;
    const contentType = (this.contentType === undefined) // exactly
        ? ('application/' + (this.json ? 'json' : 'x-www-form-urlencoded') + '; charset=UTF-8')
        : this.contentType;

    const count = ++counter;

    const ajaxObj = {
        url: url,
        type: type,
        contentType: contentType,
        processData: this.processData,
        data: this.json ? JSON.stringify(data) : data,

        success: function (response) {
            sessionTimeoutManagement.initOrUpdateSessionTimeout();
            logAjaxResponse(type, url, count, response);
            callback && callback(response);
        },

        error: response => {
            console.groupEnd();
            console.error(response);
        }
    };

    logAjaxRequest(type, url, count, data);

    return $.ajax(ajaxObj);
}

function get(path) {
    return ajax.bind({url: path, type: "GET", json: false});
}

function post(path) {
    return ajax.bind({url: path, type: "POST", json: true});
}

function postFiles(path) {
    return ajax.bind({url: path, type: "POST", json: false, contentType: false, processData: false});
}

function patch(path) {
    return ajax.bind({url: path, type: "PATCH", json: true});
}

function put(path) {
    return ajax.bind({url: path, type: "PUT", json: true});
}

function remove(path) {
    return ajax.bind({url: path, type: "DELETE", json: true});
}

class API {
    constructor(url) {
        this.create = post(url);
        this.read = get(url);
        this.update = patch(url);
        this.replace = put(url);
        this.remove = remove(url);
        this.postFiles = postFiles(url);
    }
}

module.exports = {
    ajax: ajax,
    /**
     * Always call {@code new API()}
     */
    API: API
};
<|endoftext|>