<commit_before>import React, { PureComponent } from 'react';
import { createTicket } from '../../../helpers/cw';
import TicketModal from './TicketModal';
import TicketForm from './TicketForm';
import sortBy from 'sort-by';
import { getPhases } from '../helpers'

class CreateTicketForm extends PureComponent {
  emptyTicketState = {
    budget: '',
    description: '',
    expanded: false,
    hasCompletedTicket: false,
    newTicketId: '',
    phases: [],
    phaseValue: '',
    projects: [],
    projectValue: '',
    selectedPhase: {},
    summary: '',
    ticketType: 'project',
  }

  state = {
    ...this.emptyTicketState,
    selectedProject: {},
  }

  componentDidMount = () => {
    this.getProjects();
  }

  componentDidUpdate = (prevProps) => {
    if (prevProps.projects !== this.props.projects) {
      this.getProjects();
    }

    if (prevProps.selectedProject !== this.props.selectedProject) {
      const { selectedProject } = this.props;

      if (this.state.expanded) {
        this.resetTicketDetails();
      }

      if (selectedProject['project.name']) {
        this.setState({
          selectedProject: this.state.projects.filter(project => (
            project.name === `${selectedProject['company.name']} — ${selectedProject['project.name']}`)
          ),
          phases: getPhases(this.props.selectedProject, this.props.tickets)
        });
      }
    }
  }

  getProjects = () => {
    let projects = [];

    this.props.projects.map(project => {
      projects.push({
        name: `${project.company.name} — ${project.project.name}`,
        id: project.project.id,
        companyId: project.company.id
      });
    });

    this.setState({
      projects
    });
  }

  createNewTicket = () => {
    const projectTicketDetails = ({
      summary: this.state.summary,
      recordType: 'ProjectTicket',
      company: { id: this.state.selectedProject[0].companyId },
      project: { id: this.state.selectedProject[0].id },
      phase: { id: this.state.selectedPhase[0].phaseId },
      budgetHours: this.state.budget,
      initialDescription: this.state.description,
    });

    // @todo allow service ticket creation
    const serviceTicketDetails = ({
      summary: this.state.summary,
      recordType: 'ServiceTicket',
      company: { id: this.state.selectedProject[0].companyId },
      agreement: '', // where is this?
      budgetHours: this.state.budget,
      initialDescription: this.state.description,
    });

    if (this.state.ticketType === 'project') {
      createTicket(projectTicketDetails).then(res => {
        this.setState({
          newTicketId: res.result.id,
        });
        this.props.addNewTicketToColumns(res);
      });
    }

    if (this.state.ticketType === 'service') {
      createTicket(serviceTicketDetails);
    }
  };

  toggleTicketModal = () => {
    this.setState({
      expanded: !this.state.expanded
    });
  }

  resetTicketDetails = () => {
    this.setState({
      ...this.emptyTicketState,
      expanded: true,
      phases: getPhases(this.props.selectedProject, this.props.tickets)
    });
  }

  setDescription = description => {
    this.setState({ description });
  }

  setPhaseValue = phaseValue => {
    this.setState({
      phaseValue,
      selectedPhase: this.state.phases.filter(phase => phase.path === phaseValue),
    })
  }

  setSummary = summary => {
    this.setState({ summary });
  }

  setBudget = budget => {
    this.setState({ budget });
  }

  setTicketCompleted = hasCompletedTicket => {
    this.setState({ hasCompletedTicket });
  }

  render() {
    return (

        {this.props.selectedProject['company.name'] && (
          <button
            className="btn btn-default btn-md btn-expand"
            type="button"
            onClick={() => this.toggleTicketModal()}
          >
            {this.state.expanded ? '—' : '＋'} Create Ticket
          </button>
        )}
        <TicketModal
          contentLabel="Create Ticket Modal"
          expanded={this.state.expanded}
          toggleTicketModal={this.toggleTicketModal}
        >
          <TicketForm
            budget={this.state.budget}
            createNewTicket={this.createNewTicket}
            description={this.state.description}
            expanded={this.state.expanded}
            hasCompletedTicket={this.state.hasCompletedTicket}
            newTicketId={this.state.newTicketId}
            phases={this.state.phases}
            phaseValue={this.state.phaseValue}
            projects={this.state.projects}
            projectValue={this.state.projectValue}
            resetTicketDetails={this.resetTicketDetails}
            selectedPhase={this.state.selectedPhase}
            selectedProject={this.props.selectedProject}
            setBudget={this.setBudget}
            setDescription={this.setDescription}
            setPhaseValue={this.setPhaseValue}
            setSummary={this.setSummary}
            setTicketCompleted={this.setTicketCompleted}
            summary={this.state.summary}
            ticketType={this.state.ticketType}
            toggleTicketModal={this.toggleTicketModal}
          />
        </TicketModal>

    )
  }
}

export default CreateTicketForm;
<commit_msg>Bring getPhases function back into old file to fix issues<commit_after>import React, { PureComponent } from 'react';
import { createTicket } from '../../../helpers/cw';
import TicketModal from './TicketModal';
import TicketForm from './TicketForm';
import sortBy from 'sort-by';

class CreateTicketForm extends PureComponent {
  emptyTicketState = {
    budget: '',
    description: '',
    expanded: false,
    hasCompletedTicket: false,
    newTicketId: '',
    phases: [],
    phaseValue: '',
    projects: [],
    projectValue: '',
    selectedPhase: {},
    summary: '',
    ticketType: 'project',
  }

  state = {
    ...this.emptyTicketState,
    selectedProject: {},
  }

  componentDidMount = () => {
    this.getProjects();
  }

  componentDidUpdate = (prevProps) => {
    if (prevProps.projects !== this.props.projects) {
      this.getProjects();
    }

    if (prevProps.selectedProject !== this.props.selectedProject) {
      const { selectedProject } = this.props;

      if (this.state.expanded) {
        this.resetTicketDetails();
      }

      if (selectedProject['project.name']) {
        this.getPhases(this.state.projects.filter(project => (
          project.name === selectedProject['project.name'] &&
          project.company.name === selectedProject['project.company']
        )));

        this.setState({
          selectedProject: this.state.projects.filter(project => (
            project.name === `${selectedProject['company.name']} — ${selectedProject['project.name']}`)
          )
        });
      }
    }
  }

  getPhases = () => {
    let phases = [];
    const { selectedProject } = this.props;

    this.props.tickets.map(ticket => {
      if (selectedProject['project.name'] === ticket.project.name && selectedProject['company.name'] === ticket.company.name) {
        phases.push({
          path: ticket.phase.path,
          phaseId: ticket.phase.id,
          ticketId: ticket.id
        });
      }
    });

    const deduplicatedPhases = phases.reduce((uniquePhases, currentPhase) => {
      if (!uniquePhases.some(phase => phase.path === currentPhase.path)) {
        uniquePhases.push(currentPhase);
      }
      return uniquePhases;
    }, []);

    const sortedDeduplicatedPhases = deduplicatedPhases.sort(sortBy('path'));

    this.setState({
      phases: sortedDeduplicatedPhases
    });
  }

  getProjects = () => {
    let projects = [];

    this.props.projects.map(project => {
      projects.push({
        name: `${project.company.name} — ${project.project.name}`,
        id: project.project.id,
        companyId: project.company.id
      });
    });

    this.setState({
      projects
    });
  }

  createNewTicket = () => {
    const projectTicketDetails = ({
      summary: this.state.summary,
      recordType: 'ProjectTicket',
      company: { id: this.state.selectedProject[0].companyId },
      project: { id: this.state.selectedProject[0].id },
      phase: { id: this.state.selectedPhase[0].phaseId },
      budgetHours: this.state.budget,
      initialDescription: this.state.description,
    });

    // @todo allow service ticket creation
    const serviceTicketDetails = ({
      summary: this.state.summary,
      recordType: 'ServiceTicket',
      company: { id: this.state.selectedProject[0].companyId },
      agreement: '', // where is this?
      budgetHours: this.state.budget,
      initialDescription: this.state.description,
    });

    if (this.state.ticketType === 'project') {
      createTicket(projectTicketDetails).then(res => {
        this.setState({
          newTicketId: res.result.id,
        });
        this.props.addNewTicketToColumns(res);
      });
    }

    if (this.state.ticketType === 'service') {
      createTicket(serviceTicketDetails);
    }
  };

  toggleTicketModal = () => {
    this.setState({
      expanded: !this.state.expanded
    });
  }

  resetTicketDetails = () => {
    this.setState({
      ...this.emptyTicketState,
      expanded: true,
    });

    this.getPhases();
  }

  setDescription = description => {
    this.setState({ description });
  }

  setPhaseValue = phaseValue => {
    this.setState({
      phaseValue,
      selectedPhase: this.state.phases.filter(phase => phase.path === phaseValue),
    })
  }

  setSummary = summary => {
    this.setState({ summary });
  }

  setBudget = budget => {
    this.setState({ budget });
  }

  setTicketCompleted = hasCompletedTicket => {
    this.setState({ hasCompletedTicket });
  }

  render() {
    return (

        {this.props.selectedProject['company.name'] && (
          <button
            className="btn btn-default btn-md btn-expand"
            type="button"
            onClick={() => this.toggleTicketModal()}
          >
            {this.state.expanded ? '—' : '＋'} Create Ticket
          </button>
        )}
        <TicketModal
          contentLabel="Create Ticket Modal"
          expanded={this.state.expanded}
          toggleTicketModal={this.toggleTicketModal}
        >
          <TicketForm
            budget={this.state.budget}
            createNewTicket={this.createNewTicket}
            description={this.state.description}
            expanded={this.state.expanded}
            hasCompletedTicket={this.state.hasCompletedTicket}
            newTicketId={this.state.newTicketId}
            phases={this.state.phases}
            phaseValue={this.state.phaseValue}
            projects={this.state.projects}
            projectValue={this.state.projectValue}
            resetTicketDetails={this.resetTicketDetails}
            selectedPhase={this.state.selectedPhase}
            selectedProject={this.props.selectedProject}
            setBudget={this.setBudget}
            setDescription={this.setDescription}
            setPhaseValue={this.setPhaseValue}
            setSummary={this.setSummary}
            setTicketCompleted={this.setTicketCompleted}
            summary={this.state.summary}
            ticketType={this.state.ticketType}
            toggleTicketModal={this.toggleTicketModal}
          />
        </TicketModal>

    )
  }
}

export default CreateTicketForm;
<|endoftext|>


<commit_before>import Backbone from 'backbone';
const $ = Backbone.$;

export default {
  run(em, sender) {
    this.sender = sender;
    if (!this.$cn) {
      var config = em.getConfig(),
        panels = em.Panels;
      // Main container
      this.$cn = $('

');
      // Secondary container
      this.$cn2 = $('

');
      this.$cn.append(this.$cn2);

      // Device Manager
      var dvm = em.DeviceManager;
      if (dvm && config.showDevices) {
        var devicePanel = panels.addPanel({ id: 'devices-c' });
        devicePanel
          .set('appendContent', dvm.render())
          .trigger('change:appendContent');
      }

      // Class Manager container
      var clm = em.SelectorManager;
      if (clm) this.$cn2.append(clm.render([]));
      this.$cn2.append(em.StyleManager.render());
      var smConfig = em.StyleManager.getConfig();
      const pfx = smConfig.stylePrefix;
      // Create header
      this.$header = $(
        `<div class="${pfx}header">${em.t('styleManager.empty')}</div>`
      );
      this.$cn.append(this.$header);

      // Create panel if not exists
      if (!panels.getPanel('views-container'))
        this.panel = panels.addPanel({ id: 'views-container' });
      else this.panel = panels.getPanel('views-container');

      // Add all containers to the panel
      this.panel.set('appendContent', this.$cn).trigger('change:appendContent');

      this.target = em.editor;
      this.listenTo(this.target, 'component:toggled', this.toggleSm);
    }
    this.toggleSm();
  },

  /**
   * Toggle Style Manager visibility
   * @private
   */
  toggleSm() {
    const { target, sender } = this;
    if (sender && sender.get && !sender.get('active')) return;
    const { componentFirst } = target.get('SelectorManager').getConfig();
    const selectedAll = target.getSelectedAll().length;

    if (selectedAll === 1 || (selectedAll > 1 && componentFirst)) {
      this.$cn2.show();
      this.$header.hide();
    } else {
      this.$cn2.hide();
      this.$header.show();
    }
  },

  stop() {
    // Hide secondary container if exists
    if (this.$cn2) this.$cn2.hide();

    // Hide header container if exists
    if (this.$header) this.$header.hide();
  }
};
<commit_msg>Fix default rendering of StyleManager. Closes #3835<commit_after>import Backbone from 'backbone';
const $ = Backbone.$;

export default {
  run(em, sender) {
    this.sender = sender;

    if (!this.$cn) {
      const config = em.getConfig();
      const panels = em.Panels;
      const trgEvCnt = 'change:appendContent';
      this.$cn = $('

');
      this.$cn2 = $('

');
      this.$cn.append(this.$cn2);

      // Device Manager
      const dvm = em.DeviceManager;
      if (dvm && config.showDevices) {
        const devicePanel = panels.addPanel({ id: 'devices-c' });
        const dvEl = dvm.render();
        devicePanel.set('appendContent', dvEl).trigger(trgEvCnt);
      }

      // Class Manager container
      const slm = em.SelectorManager;
      const slmConfig = slm.getConfig();
      if (!slmConfig.appendTo) {
        this.$cn2.append(slm.render([]));
      }

      // Style Manager
      const sm = em.StyleManager;
      const smConfig = sm.getConfig();
      if (!smConfig.appendTo) {
        this.$cn2.append(sm.render());
        const pfx = smConfig.stylePrefix;
        this.$header = $(
          `<div class="${pfx}header">${em.t('styleManager.empty')}</div>`
        );
        this.$cn.append(this.$header);
      }

      // Create panel if not exists
      const pnCnt = 'views-container';
      this.panel = panels.getPanel(pnCnt);
      if (!this.panel) this.panel = panels.addPanel({ id: pnCnt });

      // Add all containers to the panel
      this.panel.set('appendContent', this.$cn).trigger(trgEvCnt);

      this.target = em.editor;
      this.listenTo(this.target, 'component:toggled', this.toggleSm);
    }

    this.toggleSm();
  },

  /**
   * Toggle Style Manager visibility
   * @private
   */
  toggleSm() {
    const { target, sender } = this;
    if (sender && sender.get && !sender.get('active')) return;
    const { componentFirst } = target.get('SelectorManager').getConfig();
    const selectedAll = target.getSelectedAll().length;

    if (selectedAll === 1 || (selectedAll > 1 && componentFirst)) {
      this.$cn2 && this.$cn2.show();
      this.$header && this.$header.hide();
    } else {
      this.$cn2 && this.$cn2.hide();
      this.$header && this.$header.show();
    }
  },

  stop() {
    this.$cn2 && this.$cn2.hide();
    this.$header && this.$header.hide();
  }
};
<|endoftext|>


<commit_before>'use strict';

import _ from 'lodash';
import {NAMING_SERVICE} from 'core/naming/naming.service';

let angular = require('angular');

module.exports = angular.module('spinnaker.core.pipeline.config.validator.service', [
  require('../pipelineConfigProvider.js'),
  require('../services/pipelineConfigService.js'),
  NAMING_SERVICE,
])
  .factory('pipelineConfigValidator', function($log, pipelineConfig, pipelineConfigService, namingService, $q) {

    // Stores application pipeline configs so we don't needlessly fetch them every time we validate the pipeline
    let pipelineCache = new Map();

    let clearCache = () => {
      pipelineCache.clear();
    };

    let addTriggers = (pipelines, pipelineIdToFind, stagesToTest) => {
      let [match] = pipelines.filter(p => p.id === pipelineIdToFind);
      if (match) {
        stagesToTest.push(...match.triggers);
      }
    };

    function addExternalTriggers(trigger, stagesToTest, deferred) {
      pipelineConfigService.getPipelinesForApplication(trigger.application).then(pipelines => {
        pipelineCache.set(trigger.application, pipelines);
        addTriggers(pipelines, trigger.pipeline, stagesToTest);
        deferred.resolve();
      });
    }

    function addPipelineTriggers(pipeline, stagesToTest) {
      let pipelineTriggers = pipeline.triggers.filter(t => t.type === 'pipeline');
      let parentTriggersToCheck = [];
      pipelineTriggers.forEach(trigger => {
        let deferred = $q.defer();
        if (pipelineCache.has(trigger.application)) {
          addTriggers(pipelineCache.get(trigger.application), trigger.pipeline, stagesToTest);
        } else {
          addExternalTriggers(trigger, stagesToTest, deferred);
          parentTriggersToCheck.push(deferred.promise);
        }
      });
      return parentTriggersToCheck;
    }

    var validators = {
      stageOrTriggerBeforeType: function(pipeline, index, validationConfig, messages) {
        var stageTypes = validationConfig.stageTypes || [validationConfig.stageType];
        var stagesToTest = pipeline.stages.slice(0, index + 1);
        if (pipeline.parallel) {
          stagesToTest = pipelineConfigService.getAllUpstreamDependencies(pipeline, pipeline.stages[index]);
        }
        stagesToTest = stagesToTest.concat(pipeline.triggers);

        var parentTriggersToCheck = validationConfig.checkParentTriggers ? addPipelineTriggers(pipeline, stagesToTest) : [];
        return $q.all(parentTriggersToCheck).then(() => {
          if (stagesToTest.every((stage) => !stageTypes.includes(stage.type))) {
            messages.push(validationConfig.message);
          }
        });
      },
      stageBeforeType: function(pipeline, index, validationConfig, messages) {
        if (pipeline.strategy === true && pipeline.stages[index].type === 'deploy') {
          return;
        }

        var stageTypes = validationConfig.stageTypes || [validationConfig.stageType];
        var stagesToTest = pipeline.stages.slice(0, index + 1);
        if (pipeline.parallel) {
          stagesToTest = pipelineConfigService.getAllUpstreamDependencies(pipeline, pipeline.stages[index]);
        }
        if (stagesToTest.every((stage) => !stageTypes.includes(stage.type))) {
          messages.push(validationConfig.message);
        }
      },
      checkRequiredField: function(pipeline, stage, validationConfig, config, messages) {
        if (pipeline.strategy === true && ['cluster', 'regions', 'zones', 'credentials'].includes(validationConfig.fieldName)) {
          return;
        }

        let fieldLabel = validationConfig.fieldLabel || validationConfig.fieldName;
        fieldLabel = fieldLabel.charAt(0).toUpperCase() + fieldLabel.substr(1);
        let validationMessage = validationConfig.message ||
          '' + fieldLabel + ' is a required field for ' + config.label + ' stages.';
        var field = stage,
            parts = validationConfig.fieldName.split('.'),
            fieldNotFound = false;

        parts.forEach(function(part) {
          if (field[part] === undefined) {
            fieldNotFound = true;
            return;
          }
          field = field[part];
        });

        if (fieldNotFound ||
          (!field && field !== 0) ||
          (field && field instanceof Array && field.length === 0)
          ) {
          messages.push(validationMessage);
        }
      },
      targetImpedance: function(pipeline, index, validationConfig, messages) {
        var stage = pipeline.stages[index],
            stagesToTest = pipeline.stages.slice(0, index + 1),
            regions = stage.regions || [],
            allRegionsFound = true;

        if (pipeline.parallel) {
          stagesToTest = pipelineConfigService.getAllUpstreamDependencies(pipeline, pipeline.stages[index]);
        }

        regions.forEach(function(region) {
          var regionFound = false;
          stagesToTest.forEach(function(toTest) {
            if (toTest.type === 'deploy' && toTest.clusters && toTest.clusters.length) {
              toTest.clusters.forEach(function(cluster) {
                var clusterName = namingService.getClusterName(cluster.application, cluster.stack, cluster.freeFormDetails);
                if (clusterName === stage.cluster && cluster.account === stage.credentials && cluster.availabilityZones && cluster.availabilityZones.hasOwnProperty(region)) {
                  regionFound = true;
                }
              });
            } else if (toTest.type === 'cloneServerGroup' && toTest.targetCluster === stage.cluster && toTest.region === region) {
              regionFound = true;
            }
          });
          if (!regionFound) {
            allRegionsFound = false;
          }
        });
        if (!allRegionsFound) {
          messages.push(validationConfig.message);
        }
      }
    };

    function validatePipeline(pipeline) {
      var stages = pipeline.stages || [],
          triggers = pipeline.triggers || [],
          parameters = pipeline.parameterConfig || [],
          messages = [],
          asyncValidations = [];

      triggers.forEach(function(trigger, index) {
        let config = pipelineConfig.getTriggerConfig(trigger.type);
        if (config && config.validators) {
          config.validators.forEach(function(validator) {
            switch(validator.type) {
              case 'requiredField':
                validators.checkRequiredField(pipeline, trigger, validator, config, messages);
                break;
              default:
                $log.warn('No validator of type "' + validator.type + '" found, ignoring validation on trigger "' + (index + 1) + '" (' + trigger.type + ')');
            }
          });
        }
      });
      stages.forEach(function(stage, index) {
        let config = pipelineConfig.getStageConfig(stage);
        if (config && config.validators) {
          config.validators.forEach(function(validator) {
            if (validator.skipValidation && validator.skipValidation(pipeline, stage)) {
              return;
            }
            switch(validator.type) {
              case 'stageOrTriggerBeforeType':
                asyncValidations.push(validators.stageOrTriggerBeforeType(pipeline, index, validator, messages));
                break;
              case 'stageBeforeType':
                validators.stageBeforeType(pipeline, index, validator, messages);
                break;
              case 'targetImpedance':
                validators.targetImpedance(pipeline, index, validator, messages);
                break;
              case 'requiredField':
                validators.checkRequiredField(pipeline, stage, validator, config, messages);
                break;
              case 'custom':
                validator.validator(pipeline, stage, validator, config, messages);
                break;
              default:
                $log.warn('No validator of type "' + validator.type + '" found, ignoring validation on stage "' + stage.name + '" (' + stage.type + ')');
            }
          });
        }
      });
      parameters.forEach(function (parameter) {
        var validationConfig = {
          type: 'requiredField',
          fieldName: 'name',
          message: 'Name is a required field for parameters.',
        };

        validators.checkRequiredField(pipeline, parameter, validationConfig, {}, messages);
      });
      return $q.all(asyncValidations).then(() => {
        return _.uniq(messages);
      });
    }

    return {
      validatePipeline: validatePipeline,
      clearCache: clearCache,
    };
  });
<commit_msg>(core) warn user if creating strategy with deploy stage (#3111)<commit_after>'use strict';

import _ from 'lodash';
import {NAMING_SERVICE} from 'core/naming/naming.service';

let angular = require('angular');

module.exports = angular.module('spinnaker.core.pipeline.config.validator.service', [
  require('../pipelineConfigProvider.js'),
  require('../services/pipelineConfigService.js'),
  NAMING_SERVICE,
])
  .factory('pipelineConfigValidator', function($log, pipelineConfig, pipelineConfigService, namingService, $q) {

    // Stores application pipeline configs so we don't needlessly fetch them every time we validate the pipeline
    let pipelineCache = new Map();

    let clearCache = () => {
      pipelineCache.clear();
    };

    let addTriggers = (pipelines, pipelineIdToFind, stagesToTest) => {
      let [match] = pipelines.filter(p => p.id === pipelineIdToFind);
      if (match) {
        stagesToTest.push(...match.triggers);
      }
    };

    function addExternalTriggers(trigger, stagesToTest, deferred) {
      pipelineConfigService.getPipelinesForApplication(trigger.application).then(pipelines => {
        pipelineCache.set(trigger.application, pipelines);
        addTriggers(pipelines, trigger.pipeline, stagesToTest);
        deferred.resolve();
      });
    }

    function addPipelineTriggers(pipeline, stagesToTest) {
      let pipelineTriggers = pipeline.triggers.filter(t => t.type === 'pipeline');
      let parentTriggersToCheck = [];
      pipelineTriggers.forEach(trigger => {
        let deferred = $q.defer();
        if (pipelineCache.has(trigger.application)) {
          addTriggers(pipelineCache.get(trigger.application), trigger.pipeline, stagesToTest);
        } else {
          addExternalTriggers(trigger, stagesToTest, deferred);
          parentTriggersToCheck.push(deferred.promise);
        }
      });
      return parentTriggersToCheck;
    }

    var validators = {
      stageOrTriggerBeforeType: function(pipeline, index, validationConfig, messages) {
        var stageTypes = validationConfig.stageTypes || [validationConfig.stageType];
        var stagesToTest = pipeline.stages.slice(0, index + 1);
        if (pipeline.parallel) {
          stagesToTest = pipelineConfigService.getAllUpstreamDependencies(pipeline, pipeline.stages[index]);
        }
        stagesToTest = stagesToTest.concat(pipeline.triggers);

        var parentTriggersToCheck = validationConfig.checkParentTriggers ? addPipelineTriggers(pipeline, stagesToTest) : [];
        return $q.all(parentTriggersToCheck).then(() => {
          if (stagesToTest.every((stage) => !stageTypes.includes(stage.type))) {
            messages.push(validationConfig.message);
          }
        });
      },
      stageBeforeType: function(pipeline, index, validationConfig, messages) {
        if (pipeline.strategy === true && pipeline.stages[index].type === 'deploy') {
          return;
        }

        var stageTypes = validationConfig.stageTypes || [validationConfig.stageType];
        var stagesToTest = pipeline.stages.slice(0, index + 1);
        if (pipeline.parallel) {
          stagesToTest = pipelineConfigService.getAllUpstreamDependencies(pipeline, pipeline.stages[index]);
        }
        if (stagesToTest.every((stage) => !stageTypes.includes(stage.type))) {
          messages.push(validationConfig.message);
        }
      },
      checkRequiredField: function(pipeline, stage, validationConfig, config, messages) {
        if (pipeline.strategy === true && ['cluster', 'regions', 'zones', 'credentials'].includes(validationConfig.fieldName)) {
          return;
        }

        let fieldLabel = validationConfig.fieldLabel || validationConfig.fieldName;
        fieldLabel = fieldLabel.charAt(0).toUpperCase() + fieldLabel.substr(1);
        let validationMessage = validationConfig.message ||
          '' + fieldLabel + ' is a required field for ' + config.label + ' stages.';
        var field = stage,
            parts = validationConfig.fieldName.split('.'),
            fieldNotFound = false;

        parts.forEach(function(part) {
          if (field[part] === undefined) {
            fieldNotFound = true;
            return;
          }
          field = field[part];
        });

        if (fieldNotFound ||
          (!field && field !== 0) ||
          (field && field instanceof Array && field.length === 0)
          ) {
          messages.push(validationMessage);
        }
      },
      targetImpedance: function(pipeline, index, validationConfig, messages) {
        var stage = pipeline.stages[index],
            stagesToTest = pipeline.stages.slice(0, index + 1),
            regions = stage.regions || [],
            allRegionsFound = true;

        if (pipeline.parallel) {
          stagesToTest = pipelineConfigService.getAllUpstreamDependencies(pipeline, pipeline.stages[index]);
        }

        regions.forEach(function(region) {
          var regionFound = false;
          stagesToTest.forEach(function(toTest) {
            if (toTest.type === 'deploy' && toTest.clusters && toTest.clusters.length) {
              toTest.clusters.forEach(function(cluster) {
                var clusterName = namingService.getClusterName(cluster.application, cluster.stack, cluster.freeFormDetails);
                if (clusterName === stage.cluster && cluster.account === stage.credentials && cluster.availabilityZones && cluster.availabilityZones.hasOwnProperty(region)) {
                  regionFound = true;
                }
              });
            } else if (toTest.type === 'cloneServerGroup' && toTest.targetCluster === stage.cluster && toTest.region === region) {
              regionFound = true;
            }
          });
          if (!regionFound) {
            allRegionsFound = false;
          }
        });
        if (!allRegionsFound) {
          messages.push(validationConfig.message);
        }
      }
    };

    function validatePipeline(pipeline) {
      var stages = pipeline.stages || [],
          triggers = pipeline.triggers || [],
          parameters = pipeline.parameterConfig || [],
          messages = [],
          asyncValidations = [];

      triggers.forEach(function(trigger, index) {
        let config = pipelineConfig.getTriggerConfig(trigger.type);
        if (config && config.validators) {
          config.validators.forEach(function(validator) {
            switch(validator.type) {
              case 'requiredField':
                validators.checkRequiredField(pipeline, trigger, validator, config, messages);
                break;
              default:
                $log.warn('No validator of type "' + validator.type + '" found, ignoring validation on trigger "' + (index + 1) + '" (' + trigger.type + ')');
            }
          });
        }
      });
      stages.forEach(function(stage, index) {
        let config = pipelineConfig.getStageConfig(stage);
        if (config && config.validators) {
          config.validators.forEach(function(validator) {
            if (validator.skipValidation && validator.skipValidation(pipeline, stage)) {
              return;
            }
            switch(validator.type) {
              case 'stageOrTriggerBeforeType':
                asyncValidations.push(validators.stageOrTriggerBeforeType(pipeline, index, validator, messages));
                break;
              case 'stageBeforeType':
                validators.stageBeforeType(pipeline, index, validator, messages);
                break;
              case 'targetImpedance':
                validators.targetImpedance(pipeline, index, validator, messages);
                break;
              case 'requiredField':
                validators.checkRequiredField(pipeline, stage, validator, config, messages);
                break;
              case 'custom':
                validator.validator(pipeline, stage, validator, config, messages);
                break;
              default:
                $log.warn('No validator of type "' + validator.type + '" found, ignoring validation on stage "' + stage.name + '" (' + stage.type + ')');
            }
          });
        }
      });
      parameters.forEach(function (parameter) {
        var validationConfig = {
          type: 'requiredField',
          fieldName: 'name',
          message: 'Name is a required field for parameters.',
        };

        validators.checkRequiredField(pipeline, parameter, validationConfig, {}, messages);
      });

      if (pipeline.strategy && !(stages.find(stage => stage.type === 'deploy'))) {
        messages.push('To be able to create new server groups, a custom strategy should contain a Deploy stage.');
      }

      return $q.all(asyncValidations).then(() => {
        return _.uniq(messages);
      });
    }

    return {
      validatePipeline: validatePipeline,
      clearCache: clearCache,
    };
  });
<|endoftext|>


<commit_before>const fetch = require('node-fetch')
const config = require('../config.js')
const cloudscraper = require('cloudscraper') // For cloudflare
const RequestError = require('../structs/errors/RequestError.js')
const FeedParserError = require('../structs/errors/FeedParserError.js')
const DecodedFeedParser = require('../structs/DecodedFeedParser.js')
const ArticleIDResolver = require('../structs/ArticleIDResolver.js')
const testFilters = require('../rss/translator/filters.js')
const REQUEST_ERROR_CODE = 50042
const FEEDPARSER_ERROR_CODE = 40002

class FeedFetcher {
  constructor () {
    throw new Error('Cannot be instantiated')
  }

  /**
   * Fetch a URL
   * @param {string} url - URL to fetch
   * @param {object} requestOptions - Options to directly pass to fetch
   * @param {boolean} retried - If true, recursive retries will not be made
   */
  static async fetchURL (url, requestOptions = {}, retried) {
    if (!url) throw new Error('No url defined')
    const options = {
      timeout: 15000,
      follow: 5,
      ...requestOptions,
      headers: {
        'user-agent': `Mozilla/5.0 ${url.includes('.tumblr.com') ? 'GoogleBot' : ''} (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36`,
      }
    }

    if (requestOptions.headers) {
      options.headers = {
        ...options.headers,
        ...requestOptions.headers
      }
    }
    let endStatus
    let res

    try {
      res = await fetch(url, options)
    } catch (err) {
      throw new RequestError(err.message)
    }

    endStatus = res.status

    // Fetch returns a 304 if the two properties below were passed in by the calling function to check if there are new feeds
    if (res.status === 200 || (res.status === 304 && 'If-Modified-Since' in options.headers && 'If-None-Match' in options.headers)) {
      return {
        stream: res.body,
        response: res
      }
    }
    if (!retried && (res.status === 403 || res.status === 400)) {
      delete options.headers
      const res2 = await this.fetchURL(url, { ...options, headers: { ...options.headers, 'user-agent': '' } }, true)
      endStatus = res2.status
      if (res2.status === 200) {
        return {
          stream: res2.body,
          response: res2
        }
      }
    }

    const serverHeaders = res.headers.get('server')
    if (!serverHeaders || !serverHeaders.includes('cloudflare')) throw new RequestError(REQUEST_ERROR_CODE, `Bad status code (${endStatus})`)

    // Cloudflare is used here
    if (config._vip) throw new RequestError(REQUEST_ERROR_CODE, `Bad Cloudflare status code (${endStatus}) (Unsupported on public bot)`, true)

    return cloudscraper({ method: 'GET', uri: url, resolveWithFullResponse: true }).then(res => {
      if (res.statusCode !== 200) throw new RequestError(REQUEST_ERROR_CODE, `Bad Cloudflare status code (${res.statusCode})`, true)
      const Readable = require('stream').Readable
      const feedStream = new Readable()
      feedStream.push(res.body)
      feedStream.push(null)
      return {
        stream: feedStream
      }
    })
  }
  /**
 * @typedef {object} FeedData
 * @property {object[]} articleList - Array of articles
 * @property {string} idType - The ID type used for the article ._id property
 */

  /**
   * Parse a stream and return the article list, and the article ID type used
   * @param {object} stream
   * @param {string} url - The fetched URL of this stream
   * @returns {FeedData} - The article list and the id type used
   */
  static async parseStream (stream, url) {
    const feedparser = new DecodedFeedParser(null, url)
    const idResolver = new ArticleIDResolver()
    const articleList = []

    return new Promise((resolve, reject) => {
      stream.on('error', reject) // feedparser may not handle all errors such as incorrect headers. (feedparser v2.2.9)

      feedparser.on('error', err => {
        feedparser.removeAllListeners('end')
        if (err.message === 'Not a feed') reject(new FeedParserError(FEEDPARSER_ERROR_CODE, 'That is a not a valid feed. Note that you cannot add just any link. You may check if it is a valid feed by using online RSS feed validators'))
        else reject(new FeedParserError(null, err.message))
      })

      feedparser.on('readable', function () {
        let item
        do {
          item = this.read()
          if (item) {
            idResolver.recordArticle(item)
            articleList.push(item)
          }
        } while (item)
      })

      feedparser.on('end', () => {
        if (articleList.length === 0) return resolve({ articleList })
        const idType = idResolver.getIDType()
        for (const article of articleList) {
          article._id = ArticleIDResolver.getIDTypeValue(article, idType)
        }
        resolve({ articleList, idType })
      })

      stream.pipe(feedparser)
    })
  }

  /**
   * Fetch and parse results, and result the article list and id type
   * @param {string} url - The URL to fetch
   * @param {object} options - The options to pass to fetch
   * @returns {FeedData} - The article list and the id type used
   */
  static async fetchFeed (url, options) {
    const { stream } = await this.fetchURL(url, options)
    const { articleList, idType } = await this.parseStream(stream, url)
    return { articleList, idType }
  }

  /**
   * Get a random article in the feed
   * @param {string} url - The URL to fetch
   * @param {object} filters
   * @returns {object} - Either null, or an article object
   */
  static async fetchRandomArticle (url, filters) {
    const { articleList } = await this.fetchFeed(url)
    if (articleList.length === 0) return null
    if (!filters) {
      return articleList[Math.round(Math.random() * (articleList.length - 1))]
    }
    const filtered = []
    for (const article of articleList) {
      if (testFilters(filters, article).passed) filtered.push(article)
    }
    return filtered.length === 0 ? null : filtered[Math.round(Math.random() * (filtered.length - 1))]
  }
}

module.exports = FeedFetcher
<commit_msg>Fixed rssfilters 5 for feeds with raw filters<commit_after>const fetch = require('node-fetch')
const config = require('../config.js')
const cloudscraper = require('cloudscraper') // For cloudflare
const RequestError = require('../structs/errors/RequestError.js')
const FeedParserError = require('../structs/errors/FeedParserError.js')
const DecodedFeedParser = require('../structs/DecodedFeedParser.js')
const ArticleIDResolver = require('../structs/ArticleIDResolver.js')
const Article = require('../structs/Article.js')
const testFilters = require('../rss/translator/filters.js')
const REQUEST_ERROR_CODE = 50042
const FEEDPARSER_ERROR_CODE = 40002

class FeedFetcher {
  constructor () {
    throw new Error('Cannot be instantiated')
  }

  /**
   * Fetch a URL
   * @param {string} url - URL to fetch
   * @param {object} requestOptions - Options to directly pass to fetch
   * @param {boolean} retried - If true, recursive retries will not be made
   */
  static async fetchURL (url, requestOptions = {}, retried) {
    if (!url) throw new Error('No url defined')
    const options = {
      timeout: 15000,
      follow: 5,
      ...requestOptions,
      headers: {
        'user-agent': `Mozilla/5.0 ${url.includes('.tumblr.com') ? 'GoogleBot' : ''} (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36`,
      }
    }

    if (requestOptions.headers) {
      options.headers = {
        ...options.headers,
        ...requestOptions.headers
      }
    }
    let endStatus
    let res

    try {
      res = await fetch(url, options)
    } catch (err) {
      throw new RequestError(err.message)
    }

    endStatus = res.status

    // Fetch returns a 304 if the two properties below were passed in by the calling function to check if there are new feeds
    if (res.status === 200 || (res.status === 304 && 'If-Modified-Since' in options.headers && 'If-None-Match' in options.headers)) {
      return {
        stream: res.body,
        response: res
      }
    }
    if (!retried && (res.status === 403 || res.status === 400)) {
      delete options.headers
      const res2 = await this.fetchURL(url, { ...options, headers: { ...options.headers, 'user-agent': '' } }, true)
      endStatus = res2.status
      if (res2.status === 200) {
        return {
          stream: res2.body,
          response: res2
        }
      }
    }

    const serverHeaders = res.headers.get('server')
    if (!serverHeaders || !serverHeaders.includes('cloudflare')) throw new RequestError(REQUEST_ERROR_CODE, `Bad status code (${endStatus})`)

    // Cloudflare is used here
    if (config._vip) throw new RequestError(REQUEST_ERROR_CODE, `Bad Cloudflare status code (${endStatus}) (Unsupported on public bot)`, true)

    return cloudscraper({ method: 'GET', uri: url, resolveWithFullResponse: true }).then(res => {
      if (res.statusCode !== 200) throw new RequestError(REQUEST_ERROR_CODE, `Bad Cloudflare status code (${res.statusCode})`, true)
      const Readable = require('stream').Readable
      const feedStream = new Readable()
      feedStream.push(res.body)
      feedStream.push(null)
      return {
        stream: feedStream
      }
    })
  }
  /**
 * @typedef {object} FeedData
 * @property {object[]} articleList - Array of articles
 * @property {string} idType - The ID type used for the article ._id property
 */

  /**
   * Parse a stream and return the article list, and the article ID type used
   * @param {object} stream
   * @param {string} url - The fetched URL of this stream
   * @returns {FeedData} - The article list and the id type used
   */
  static async parseStream (stream, url) {
    const feedparser = new DecodedFeedParser(null, url)
    const idResolver = new ArticleIDResolver()
    const articleList = []

    return new Promise((resolve, reject) => {
      stream.on('error', reject) // feedparser may not handle all errors such as incorrect headers. (feedparser v2.2.9)

      feedparser.on('error', err => {
        feedparser.removeAllListeners('end')
        if (err.message === 'Not a feed') reject(new FeedParserError(FEEDPARSER_ERROR_CODE, 'That is a not a valid feed. Note that you cannot add just any link. You may check if it is a valid feed by using online RSS feed validators'))
        else reject(new FeedParserError(null, err.message))
      })

      feedparser.on('readable', function () {
        let item
        do {
          item = this.read()
          if (item) {
            idResolver.recordArticle(item)
            articleList.push(item)
          }
        } while (item)
      })

      feedparser.on('end', () => {
        if (articleList.length === 0) return resolve({ articleList })
        const idType = idResolver.getIDType()
        for (const article of articleList) {
          article._id = ArticleIDResolver.getIDTypeValue(article, idType)
        }
        resolve({ articleList, idType })
      })

      stream.pipe(feedparser)
    })
  }

  /**
   * Fetch and parse results, and result the article list and id type
   * @param {string} url - The URL to fetch
   * @param {object} options - The options to pass to fetch
   * @returns {FeedData} - The article list and the id type used
   */
  static async fetchFeed (url, options) {
    const { stream } = await this.fetchURL(url, options)
    const { articleList, idType } = await this.parseStream(stream, url)
    return { articleList, idType }
  }

  /**
   * Get a random article in the feed
   * @param {string} url - The URL to fetch
   * @param {object} filters
   * @returns {object} - Either null, or an article object
   */
  static async fetchRandomArticle (url, filters) {
    const { articleList } = await this.fetchFeed(url)
    if (articleList.length === 0) return null
    if (!filters) {
      return articleList[Math.round(Math.random() * (articleList.length - 1))]
    }
    const filtered = []
    for (const article of articleList) {
      if (testFilters(filters, new Article(article, {}, {})).passed) filtered.push(article)
    }
    return filtered.length === 0 ? null : filtered[Math.round(Math.random() * (filtered.length - 1))]
  }
}

module.exports = FeedFetcher
<|endoftext|>


<commit_before>'use strict';

// Utils
const { logError } = require('../../utils/log');

const { managesGroup } = require('../../stores/group');

const pkg = require('../../package.json');

const caption = `\
Sorry, you need to set up your own instance \
to use me in your group or a network of groups.

For managing a single group, it'll be simpler for you \
to use @GroupButler_bot or @mattatabot instead.
`;

const inline_keyboard = [ [ {
	text: '🛠 Setup a New Bot',
	url: pkg.homepage,
} ] ];

const reply_markup = JSON.stringify({ inline_keyboard });

const gifs = [
	'https://media.giphy.com/media/xTk9ZBWrma4PIC9y4E/giphy.gif',
	'https://media.giphy.com/media/l2Sqc3POpzkj5r8SQ/giphy.gif',
	'https://media.giphy.com/media/StaMzjNkq5PqM/giphy.gif',
	'https://media.giphy.com/media/fjYDN5flDJ756/giphy.gif',
	'https://media.giphy.com/media/3XiQswSmbjBiU/giphy.gif',
];

const randomChoice = arr => arr[Math.floor(Math.random() * arr.length)];

const leaveUnmanagedHandler = async (ctx, next) => {
	if (ctx.chat.type === 'private' || await managesGroup(ctx.chat)) {
		return next();
	}

	try {
		await ctx.replyWithVideo(randomChoice(gifs), { caption, reply_markup });
	} catch (err) {
		logError(err);
	}

	return ctx.telegram.leaveChat(ctx.chat.id);
};

module.exports = leaveUnmanagedHandler;
<commit_msg>fixed multiple leave messages, resolved #14<commit_after>'use strict';

// Utils
const { logError } = require('../../utils/log');

const { managesGroup } = require('../../stores/group');

const pkg = require('../../package.json');

const caption = `\
Sorry, you need to set up your own instance \
to use me in your group or a network of groups.

For managing a single group, it'll be simpler for you \
to use @GroupButler_bot or @mattatabot instead.
`;

const inline_keyboard = [ [ {
	text: '🛠 Setup a New Bot',
	url: pkg.homepage,
} ] ];

const reply_markup = JSON.stringify({ inline_keyboard });

const gifs = [
	'https://media.giphy.com/media/xTk9ZBWrma4PIC9y4E/giphy.gif',
	'https://media.giphy.com/media/l2Sqc3POpzkj5r8SQ/giphy.gif',
	'https://media.giphy.com/media/StaMzjNkq5PqM/giphy.gif',
	'https://media.giphy.com/media/fjYDN5flDJ756/giphy.gif',
	'https://media.giphy.com/media/3XiQswSmbjBiU/giphy.gif',
];

const randomChoice = arr => arr[Math.floor(Math.random() * arr.length)];

const leaveUnmanagedHandler = async (ctx, next) => {
	if (ctx.chat.type === 'private' || await managesGroup(ctx.chat)) {
		return next();
	}

	try {
		await ctx.replyWithVideo(randomChoice(gifs), { caption, reply_markup });
	} catch (err) {
		logError(err);
	}
	await ctx.telegram.leaveChat(ctx.chat.id);
	return next();
};

module.exports = leaveUnmanagedHandler;
<|endoftext|>


<commit_before>/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @format
 */

const {fbContent, fbInternalOnly} = require('internaldocs-fb-helpers');
module.exports = {
  mainSidebar: {
    // TODO: update content
    'What is Litho?': ['intro/motivation', 'intro/built-with-litho'],
    'Tutorial': [
      'tutorial/overview',
      // TODO: add kotlin dep
      'tutorial/project-setup',
      'tutorial/first-components',
      'tutorial/adding-state',
      'tutorial/building-lists',
    ],
    'Main Concepts': [
      'mainconcepts/components-basics',
      'mainconcepts/props',
      {
        'Hooks and State': [
          'mainconcepts/hooks-intro',
          'mainconcepts/use-state',
          'mainconcepts/use-effect',
          'mainconcepts/use-ref',
          'mainconcepts/use-error-boundary',
        ],
      },
      'mainconcepts/flexbox-yoga',
      // TODO: Kotlin-ify,
      'mainconcepts/coordinate-state-actions/keys-and-identity',
    ],
     // TODO: Clean up/update for Collection
    'Building lists': [
      'sections/start',
      'sections/recycler-collection-component',
      'sections/best-practices',
      'sections/hscrolls',
      'sections/api-overview',
      'sections/working-ranges',
      'sections/services',
      'sections/view-support',
      'sections/diff-sections',
      'sections/architecture',
    ],
    // TODO: Kotlin-ify this section
    'Animations': [
      'animations/transition-basics',
      'animations/transition-types',
      'animations/transition-all-layout',
      'animations/transition-choreography',
      'animations/transition-definitions',
      'animations/transition-key-types',
      'animations/dynamic-props',
    ],
    'Visibility': [
      // TODO: Kotlin-ify
      'mainconcepts/coordinate-state-actions/visibility-handling',
    ],
    'Accessibility': ['accessibility/accessibility-overview'],
    // TODO: revisit wording to make it clear it's not just the 'kotlin' testing API
    'Testing': [
      'kotlin/testing-getting-started',
      'kotlin/testing-assertions',
      'kotlin/testing-actions',
    ],
    'Widgets': [
      'widgets/builtin-widgets',
      ...fbInternalOnly(['fb/widgets/design-components']),
    ],
    'Adopting Litho': [
        'kotlin/migration-strategies',
        'kotlin/custom-view-compat',
      ],
    // TODO: clean this section up, add intro page
    'Codegen APIs': [
      'codegen/layout-specs',
      'codegen/mount-specs',
      {
        'Passing Data To Components': [
          'codegen/passing-data-to-components/spec-props',
          'codegen/passing-data-to-components/treeprops',
        ],
      },
      'codegen/state-for-specs',
      'codegen/events-for-specs',
      'codegen/trigger-events',
    ],
    // TODO: de-dupe content with main concepts
    ...fbInternalOnly({
      'Migrating to the Kotlin API': [
        'kotlin/kotlin-intro',
        'kotlin/kotlin-api-basics',
        'kotlin/hooks-for-spec-developers',
        'kotlin/kotlin-flexbox-containers',
        'kotlin/event-handling',
        'kotlin/kotlin-api-cheatsheet',
        {
          'Lazy Collections' : [
           'kotlin/lazycollections/lazycollections',
           'kotlin/lazycollections/lazycollections-working-with-updates',
           'kotlin/lazycollections/lazycollections-layout',
           'kotlin/lazycollections/lazycollections-interactions',
           'kotlin/lazycollections/lazycollections-sections-migration',
          ],
        },
      ],
    }),
    // TODO: clean this section up, add intro page
    'Tooling': [
      {
        'Debugging': [
          'debugging/debugging-tips',
          'debugging/debugging-sections',
          ...fbInternalOnly(['debugging/fb/debugging-time-travel']),
        ],
        'Developer Tools': [
          'devtools/android-studio-plugin',
          'devtools/flipper-plugins',
        ],
      },
      'performance/analysing-performance',
    ],
    'Best Practices': [
      'best-practices/immutability',
      'best-practices/props-vs-state',
      'mainconcepts/coordinate-state-actions/hoisting-state',
      'mainconcepts/coordinate-state-actions/communicating-between-components',
      'best-practices/coding-style',
    ],
    ...fbInternalOnly({
      'Contributing to the Documentation': [
        'fb/documentation/contributing-documentation',
        'fb/documentation/formatting-tips',
        'fb/documentation/writing-guide',
      ],
    }),
    ...fbInternalOnly({
      '[Internal]': [
        'fb/internal-litho',
        'fb/video-lessons',
        {
          Architecture: [
            'fb/architecture-sections-in-a-fragment-or-activity',
            'fb/architecture-thread-safety',
            'fb/architecture-litho-tricks',
          ],
        },
        'fb/dependency-injection',
        {
          'Analysing Performance': [
            'fb/analysing-performance-qpl',
            'fb/analysing-performance-spotting-performance-issues',
            'fb/analysing-performance-ttrc',
          ],
        },
        {
          'Error Handling': [
            'fb/error-boundaries',
            'fb/error-handling',
            'fb/error-handling-setting-a-default-error-event-handler',
          ],
        },
        'fb/experimentation',
        {
          'Open Source': [
            'fb/open-source',
            'fb/open-source-using-the-open-source-repo',
            'fb/open-source-releasing-litho',
          ],
        },
        'fb/sample-app',
      ],
    }),

    //
    // Begin unused content
    //

    ...fbInternalOnly({
      '[Old Not Reused Content]': [
        {
          'Introducing Litho': ['intro', 'uses'],
          'Quick Start': [
            'getting-started',
            'tutorial',
            'writing-components',
            'using-components',
          ],
          'Reference': ['common-props', 'cached-values'],
          'Handling Events': ['events-touch-handling'],
          'Sections': ['sections-tutorial', 'communicating-with-the-ui'],
          'Common use cases': [
            'updating-ui',
            'borders',
            'tooltips',
            'saving-state',
          ],
          'Compatibility': ['styles'],
          'Advanced Guides': [
            'mainconcepts/coordinate-state-actions/componenttree',
            'architecture-overview',
            'custom-layout',
            'onattached-ondetached',
          ],
          'Architecture': [
            'codegen',
            'asynchronous-layout',
            'view-flattening',
            'recycling',
          ],
          'Experimental': ['mount-extensions'],
          'Additional Resources': ['faq', 'glossary'],
          'Contributing': [
            'contributing',
            'community-showcase',
            'repo-structure',
          ],
          'Testing': [
            'testing/testing-overview',
            {
              'Unit Tests': [
                'testing/unit-testing',
                ...fbInternalOnly(['testing/fb/unit-testing-at-facebook']),
                'testing/subcomponent-testing',
                'testing/prop-matching',
                'testing/testing-treeprops',
                'testing/injectprop-matching',
                'testing/event-handler-testing',
                'testing/sections-testing',
              ],
            },
            {
              'UI Tests': [
                'testing/espresso-testing',
                ...fbInternalOnly(['testing/fb/buddy-tests-at-facebook']),
              ],
            },
            ...fbInternalOnly([
              {
                'Benchmark Tests': [
                  'testing/fb/litho-benchmark-tests',
                  {
                    'MobileLab Tests': [
                      'testing/fb/mobilelab-benchmark-tests/mobilelab-tests',
                      'testing/fb/mobilelab-benchmark-tests/getting-started',
                      'testing/fb/mobilelab-benchmark-tests/memory-benchmarks',
                      'testing/fb/mobilelab-benchmark-tests/mobilelab-integration',
                      'testing/fb/mobilelab-benchmark-tests/profiling-benchmarks',
                    ],
                  },
                ],
              },
            ]),
            'testing/tests-in-android-studio',
          ],
          'Deep Dive': [
            {
              Reconciliation: [
                'deep-dive/reconciliation',
                'deep-dive/reconciliation/enabling-reconciliation',
                ...fbInternalOnly([
                  'deep-dive/reconciliation/fb/when-to-use-reconciliation',
                ]),
              ],
            },
            'deep-dive/incremental-mount',
            {
              Debugging: ['annotation-processor-debugging'],
            },
          ],
        },
      ],
    }),
  },
};
<commit_msg>Meta internal migration guide<commit_after>/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @format
 */

const {fbContent, fbInternalOnly} = require('internaldocs-fb-helpers');
module.exports = {
  mainSidebar: {
    // TODO: update content
    'What is Litho?': ['intro/motivation', 'intro/built-with-litho'],
    'Tutorial': [
      'tutorial/overview',
      // TODO: add kotlin dep
      'tutorial/project-setup',
      'tutorial/first-components',
      'tutorial/adding-state',
      'tutorial/building-lists',
    ],
    'Main Concepts': [
      'mainconcepts/components-basics',
      'mainconcepts/props',
      {
        'Hooks and State': [
          'mainconcepts/hooks-intro',
          'mainconcepts/use-state',
          'mainconcepts/use-effect',
          'mainconcepts/use-ref',
          'mainconcepts/use-error-boundary',
        ],
      },
      'mainconcepts/flexbox-yoga',
      // TODO: Kotlin-ify,
      'mainconcepts/coordinate-state-actions/keys-and-identity',
    ],
     // TODO: Clean up/update for Collection
    'Building lists': [
      'sections/start',
      'sections/recycler-collection-component',
      'sections/best-practices',
      'sections/hscrolls',
      'sections/api-overview',
      'sections/working-ranges',
      'sections/services',
      'sections/view-support',
      'sections/diff-sections',
      'sections/architecture',
    ],
    // TODO: Kotlin-ify this section
    'Animations': [
      'animations/transition-basics',
      'animations/transition-types',
      'animations/transition-all-layout',
      'animations/transition-choreography',
      'animations/transition-definitions',
      'animations/transition-key-types',
      'animations/dynamic-props',
    ],
    'Visibility': [
      // TODO: Kotlin-ify
      'mainconcepts/coordinate-state-actions/visibility-handling',
    ],
    'Accessibility': ['accessibility/accessibility-overview'],
    // TODO: revisit wording to make it clear it's not just the 'kotlin' testing API
    'Testing': [
      'kotlin/testing-getting-started',
      'kotlin/testing-assertions',
      'kotlin/testing-actions',
    ],
    'Widgets': [
      'widgets/builtin-widgets',
      ...fbInternalOnly(['fb/widgets/design-components']),
    ],
    'Adopting Litho': [
        'kotlin/migration-strategies',
        'kotlin/custom-view-compat',
      ],
    // TODO: clean this section up, add intro page
    'Codegen APIs': [
      'codegen/layout-specs',
      'codegen/mount-specs',
      {
        'Passing Data To Components': [
          'codegen/passing-data-to-components/spec-props',
          'codegen/passing-data-to-components/treeprops',
        ],
      },
      'codegen/state-for-specs',
      'codegen/events-for-specs',
      'codegen/trigger-events',
    ],
    // TODO: de-dupe content with main concepts
    ...fbInternalOnly({
      'Migrating to the Kotlin API': [
        'kotlin/kotlin-intro',
        'kotlin/kotlin-api-basics',
        'kotlin/hooks-for-spec-developers',
        'kotlin/kotlin-flexbox-containers',
        'kotlin/event-handling',
        'kotlin/kotlin-api-cheatsheet',
        {
          'Lazy Collections' : [
           'kotlin/lazycollections/lazycollections',
           'kotlin/lazycollections/lazycollections-working-with-updates',
           'kotlin/lazycollections/lazycollections-layout',
           'kotlin/lazycollections/lazycollections-interactions',
           'kotlin/lazycollections/lazycollections-sections-migration',
           ...fbInternalOnly(['kotlin/lazycollections/fb/lazycollections-fb-internal']),
          ],
        },
      ],
    }),
    // TODO: clean this section up, add intro page
    'Tooling': [
      {
        'Debugging': [
          'debugging/debugging-tips',
          'debugging/debugging-sections',
          ...fbInternalOnly(['debugging/fb/debugging-time-travel']),
        ],
        'Developer Tools': [
          'devtools/android-studio-plugin',
          'devtools/flipper-plugins',
        ],
      },
      'performance/analysing-performance',
    ],
    'Best Practices': [
      'best-practices/immutability',
      'best-practices/props-vs-state',
      'mainconcepts/coordinate-state-actions/hoisting-state',
      'mainconcepts/coordinate-state-actions/communicating-between-components',
      'best-practices/coding-style',
    ],
    ...fbInternalOnly({
      'Contributing to the Documentation': [
        'fb/documentation/contributing-documentation',
        'fb/documentation/formatting-tips',
        'fb/documentation/writing-guide',
      ],
    }),
    ...fbInternalOnly({
      '[Internal]': [
        'fb/internal-litho',
        'fb/video-lessons',
        {
          Architecture: [
            'fb/architecture-sections-in-a-fragment-or-activity',
            'fb/architecture-thread-safety',
            'fb/architecture-litho-tricks',
          ],
        },
        'fb/dependency-injection',
        {
          'Analysing Performance': [
            'fb/analysing-performance-qpl',
            'fb/analysing-performance-spotting-performance-issues',
            'fb/analysing-performance-ttrc',
          ],
        },
        {
          'Error Handling': [
            'fb/error-boundaries',
            'fb/error-handling',
            'fb/error-handling-setting-a-default-error-event-handler',
          ],
        },
        'fb/experimentation',
        {
          'Open Source': [
            'fb/open-source',
            'fb/open-source-using-the-open-source-repo',
            'fb/open-source-releasing-litho',
          ],
        },
        'fb/sample-app',
      ],
    }),

    //
    // Begin unused content
    //

    ...fbInternalOnly({
      '[Old Not Reused Content]': [
        {
          'Introducing Litho': ['intro', 'uses'],
          'Quick Start': [
            'getting-started',
            'tutorial',
            'writing-components',
            'using-components',
          ],
          'Reference': ['common-props', 'cached-values'],
          'Handling Events': ['events-touch-handling'],
          'Sections': ['sections-tutorial', 'communicating-with-the-ui'],
          'Common use cases': [
            'updating-ui',
            'borders',
            'tooltips',
            'saving-state',
          ],
          'Compatibility': ['styles'],
          'Advanced Guides': [
            'mainconcepts/coordinate-state-actions/componenttree',
            'architecture-overview',
            'custom-layout',
            'onattached-ondetached',
          ],
          'Architecture': [
            'codegen',
            'asynchronous-layout',
            'view-flattening',
            'recycling',
          ],
          'Experimental': ['mount-extensions'],
          'Additional Resources': ['faq', 'glossary'],
          'Contributing': [
            'contributing',
            'community-showcase',
            'repo-structure',
          ],
          'Testing': [
            'testing/testing-overview',
            {
              'Unit Tests': [
                'testing/unit-testing',
                ...fbInternalOnly(['testing/fb/unit-testing-at-facebook']),
                'testing/subcomponent-testing',
                'testing/prop-matching',
                'testing/testing-treeprops',
                'testing/injectprop-matching',
                'testing/event-handler-testing',
                'testing/sections-testing',
              ],
            },
            {
              'UI Tests': [
                'testing/espresso-testing',
                ...fbInternalOnly(['testing/fb/buddy-tests-at-facebook']),
              ],
            },
            ...fbInternalOnly([
              {
                'Benchmark Tests': [
                  'testing/fb/litho-benchmark-tests',
                  {
                    'MobileLab Tests': [
                      'testing/fb/mobilelab-benchmark-tests/mobilelab-tests',
                      'testing/fb/mobilelab-benchmark-tests/getting-started',
                      'testing/fb/mobilelab-benchmark-tests/memory-benchmarks',
                      'testing/fb/mobilelab-benchmark-tests/mobilelab-integration',
                      'testing/fb/mobilelab-benchmark-tests/profiling-benchmarks',
                    ],
                  },
                ],
              },
            ]),
            'testing/tests-in-android-studio',
          ],
          'Deep Dive': [
            {
              Reconciliation: [
                'deep-dive/reconciliation',
                'deep-dive/reconciliation/enabling-reconciliation',
                ...fbInternalOnly([
                  'deep-dive/reconciliation/fb/when-to-use-reconciliation',
                ]),
              ],
            },
            'deep-dive/incremental-mount',
            {
              Debugging: ['annotation-processor-debugging'],
            },
          ],
        },
      ],
    }),
  },
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

'use strict';

const path = require('path');

const app = require('commander');
const chalk = require('chalk');

const cosi = require(path.resolve(path.join(__dirname, '..', 'lib', 'cosi')));
const Dashboard = require(path.join(cosi.lib_dir, 'dashboard'));

app.
    version(cosi.app_version).
    usage('[options] <config_file>').
    parse(process.argv);

console.log(chalk.bold(app.name()), `v${app.version()}`);

if (app.args.length === 0) {
    console.error(chalk.red('config_file is required'));
    app.outputHelp();
    process.exit(1);
}

const cfgFile = path.resolve(app.args[0]);
const dash = new Dashboard(cfgFile);

dash.update().
    then((updated) => {
        dash.save(cfgFile, true);
        console.log(chalk.green('Updated'), updated.display_name);
    }).
    catch((err) => {
        console.error(chalk.red('ERROR:'), err);
        process.exit(1);
    });
<commit_msg>title not display_name<commit_after>#!/usr/bin/env node

// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

'use strict';

const path = require('path');

const app = require('commander');
const chalk = require('chalk');

const cosi = require(path.resolve(path.join(__dirname, '..', 'lib', 'cosi')));
const Dashboard = require(path.join(cosi.lib_dir, 'dashboard'));

app.
    version(cosi.app_version).
    usage('[options] <config_file>').
    parse(process.argv);

console.log(chalk.bold(app.name()), `v${app.version()}`);

if (app.args.length === 0) {
    console.error(chalk.red('config_file is required'));
    app.outputHelp();
    process.exit(1);
}

const cfgFile = path.resolve(app.args[0]);
const dash = new Dashboard(cfgFile);

dash.update().
    then((updated) => {
        dash.save(cfgFile, true);
        console.log(chalk.green('Updated'), updated.title);
    }).
    catch((err) => {
        console.error(chalk.red('ERROR:'), err);
        process.exit(1);
    });
<|endoftext|>


<commit_before>describe('#Marker', function() {

    var container;
    var map;
    var tile;
    var center = new Z.Coordinate(118.846825, 32.046534);
    var canvasContainer;
    var layer;

    beforeEach(function() {
        var setups = commonSetupMap(center);
        container = setups.container;
        map = setups.map;
        canvasContainer = map._panels.mapPlatform;
        layer = new maptalks.VectorLayer('v').addTo(map);
    });

    afterEach(function() {
        map.removeLayer('v');
        layer = null;
        removeContainer(container)
    });

    it('setCoordinates', function() {
        var marker = new maptalks.Marker([0, 0]);
        marker.setCoordinates([1, 1]);
        expect(marker.getCoordinates().toArray()).to.be.eql([1, 1]);
    });

    it('getCenter', function() {
        var marker = new maptalks.Marker({x: 0, y: 0});
        expect(marker.getCenter().toArray()).to.be.eql([0, 0]);
    });

    it('getExtent', function() {
        var marker = new maptalks.Marker({x: 0, y: 0});

        expect(marker.getExtent().toJSON()).to.be.eql({xmin : 0, xmax : 0, ymin : 0, ymax : 0});

    });

    it('getSize', function() {
        var marker = new maptalks.Marker(map.getCenter());
        layer.addGeometry(marker);
        var size = marker.getSize();

        expect(size.width).to.be.above(0);
        expect(size.height).to.be.above(0);
    });

    it('show/hide/isVisible', function() {
        var marker = new maptalks.Marker({x: 0, y: 0});
        layer.addGeometry(marker);
        marker.show();
        marker.hide();
        expect(marker.isVisible()).not.to.be.ok();
    });

    it('remove', function() {
        var marker = new maptalks.Marker({x: 0, y: 0});
        layer.addGeometry(marker);
        marker.remove();

        expect(marker.getLayer()).to.be(null);
    });

    describe("symbol", function() {

        beforeEach(function() {
            layer = new Z.VectorLayer('id');
            map.addLayer(layer);
        });

        afterEach(function() {
            map.removeLayer(layer);
        });

        it("can be icon", function() {
            var marker = new Z.Marker(center, {
                symbol: {
                    markerFile: Z.prefix + 'images/control/2.png',
                    markerWidth: 30,
                    markerHeight: 22
                }
            });

            expect(function () {
                layer.addGeometry(marker);
            }).to.not.throwException();
        });

        it("can be text", function() {
            var marker = new Z.Marker(center, {
                symbol: {
                    textName: 'texxxxxt',
                    font: 'monospace'
                }
            });

            expect(function () {
                layer.addGeometry(marker);
            }).to.not.throwException();
        });

        it("can be vector", function() {
            var types = ['circle', 'triangle', 'cross', 'diamond', 'square', 'x', 'bar'];

            expect(function () {
                for(var i = 0; i < types.length; i++) {
                    var marker = new Z.Marker(center, {
                        symbol: {
                            markerType: types[i],
                            markerLineDasharray: [20, 10, 5, 5, 5, 10]
                        }
                    });
                    layer.addGeometry(marker);
                }
            }).to.not.throwException();
        });

        it("can be shield", function() {
            var types = ['label', 'tip'];

            expect(function () {
                for(var i = 0; i < types.length; i++) {
                    var marker = new Z.Marker(center, {
                        symbol: {
                            shieldType: types[i],
                            shieldName: types[i] + 'Shield'
                        }
                    });
                    layer.addGeometry(marker);
                }
            }).to.not.throwException();
        });

    });

    describe('set marker\'s Symbol', function() {

        it('fires symbolchange event', function() {
            var spy = sinon.spy();
            var marker = new Z.Marker(center);
            marker.on('symbolchange', spy);
            marker.setSymbol({
                'markerType' : 'ellipse',
                'markerLineColor': '#ff0000',
                'markerFill': '#ffffff',
                'markerFillOpacity': 0.6,
                'markerHeight' : 8,
                'markerWidth' : 8
            });

            expect(spy.called).to.be.ok();
        });

        it('unsuppored markerType', function() {
            var layer = new maptalks.VectorLayer('vector', {'drawImmediate' : true});
            map.addLayer(layer);
            var marker = new maptalks.Marker(map.getCenter(), {
                symbol:{
                    "markerType" : "unsupported",
                    "markerWidth":20,
                    "markerHeight":30
                }
            });
            expect(function() {
                layer.addGeometry(marker);
            }).to.throwException();
        });

    });

    describe('events', function() {
        it('canvas events', function() {
            var vector = new Z.Marker(center);
            new GeoEventsTester().testCanvasEvents(vector, map, vector.getCenter());
        });
    });

    it('can have various symbols',function(done) {
        var vector = new Z.Marker(center);
        GeoSymbolTester.testGeoSymbols(vector, map, done);
    });

    it("Marker._containsPoint", function() {

        var geometry = new Z.Marker(center, {
            symbol: {
                markerFile : Z.prefix + 'images/control/2.png',
                markerHeight : 30,
                markerWidth : 22,
                dx : 0,
                dy : 0
            }
        });
        layer = new Z.VectorLayer('id');
        map.addLayer(layer);
        layer.addGeometry(geometry);

        var spy = sinon.spy();
        geometry.on('click', spy);
        //TODO 因为marker的width和height为0, 所以无法击中
        happen.click(canvasContainer, {
            clientX: 400 + 8,
            clientY: 300 + 8
        });

        //expect(spy.called).to.be.ok();
    });

});
<commit_msg>update MarkerSpec<commit_after>describe('#Marker', function() {

    var container;
    var map;
    var tile;
    var center = new Z.Coordinate(118.846825, 32.046534);
    var canvasContainer;
    var layer;

    beforeEach(function() {
        var setups = commonSetupMap(center);
        container = setups.container;
        map = setups.map;
        canvasContainer = map._panels.mapPlatform;
        layer = new maptalks.VectorLayer('v').addTo(map);
    });

    afterEach(function() {
        map.removeLayer('v');
        layer = null;
        removeContainer(container)
    });

    it('setCoordinates', function() {
        var marker = new maptalks.Marker([0, 0]);
        marker.setCoordinates([1, 1]);
        expect(marker.getCoordinates().toArray()).to.be.eql([1, 1]);
    });

    it('getCenter', function() {
        var marker = new maptalks.Marker({x: 0, y: 0});
        expect(marker.getCenter().toArray()).to.be.eql([0, 0]);
    });

    it('getExtent', function() {
        var marker = new maptalks.Marker({x: 0, y: 0});

        expect(marker.getExtent().toJSON()).to.be.eql({xmin : 0, xmax : 0, ymin : 0, ymax : 0});

    });

    it('getSize', function() {
        var marker = new maptalks.Marker(map.getCenter());
        layer.addGeometry(marker);
        var size = marker.getSize();

        expect(size.width).to.be.above(0);
        expect(size.height).to.be.above(0);
    });

    it('show/hide/isVisible', function() {
        var marker = new maptalks.Marker({x: 0, y: 0});
        layer.addGeometry(marker);
        marker.show();
        marker.hide();
        expect(marker.isVisible()).not.to.be.ok();
    });

    it('remove', function() {
        var marker = new maptalks.Marker({x: 0, y: 0});
        layer.addGeometry(marker);
        marker.remove();

        expect(marker.getLayer()).to.be(null);
    });

    describe("symbol", function() {

        beforeEach(function() {
            layer = new Z.VectorLayer('id', {'drawImmediate' : true});
            map.addLayer(layer);
        });

        afterEach(function() {
            map.removeLayer(layer);
        });

        it("can be icon", function() {
            var marker = new Z.Marker(center, {
                symbol: {
                    markerFile: Z.prefix + 'images/control/2.png',
                    markerWidth: 30,
                    markerHeight: 22
                }
            });

            expect(function () {
                layer.addGeometry(marker);
            }).to.not.throwException();
        });

        it("can be text", function() {
            var marker = new Z.Marker(center, {
                symbol: {
                    textName: 'texxxxxt',
                    textFaceName: 'monospace'
                }
            });

            expect(function () {
                layer.addGeometry(marker);
            }).to.not.throwException();
        });

        it("can be vector", function() {
            var types = ['ellipse', 'triangle', 'cross', 'diamond', 'square', 'x', 'bar'];

            expect(function () {
                for(var i = 0; i < types.length; i++) {
                    var marker = new Z.Marker(center, {
                        symbol: {
                            markerType: types[i],
                            markerLineDasharray: [20, 10, 5, 5, 5, 10]
                        }
                    });
                    layer.addGeometry(marker);
                }
            }).to.not.throwException();
        });

    });

    describe('set marker\'s Symbol', function() {

        it('fires symbolchange event', function() {
            var spy = sinon.spy();
            var marker = new Z.Marker(center);
            marker.on('symbolchange', spy);
            marker.setSymbol({
                'markerType' : 'ellipse',
                'markerLineColor': '#ff0000',
                'markerFill': '#ffffff',
                'markerFillOpacity': 0.6,
                'markerHeight' : 8,
                'markerWidth' : 8
            });

            expect(spy.called).to.be.ok();
        });

        it('unsuppored markerType', function() {
            var layer = new maptalks.VectorLayer('vector', {'drawImmediate' : true});
            map.addLayer(layer);
            var marker = new maptalks.Marker(map.getCenter(), {
                symbol:{
                    "markerType" : "unsupported",
                    "markerWidth":20,
                    "markerHeight":30
                }
            });
            expect(function() {
                layer.addGeometry(marker);
            }).to.throwException();
        });

    });

    describe('events', function() {
        it('canvas events', function() {
            var vector = new Z.Marker(center);
            new GeoEventsTester().testCanvasEvents(vector, map, vector.getCenter());
        });
    });

    it('can have various symbols',function(done) {
        var vector = new Z.Marker(center);
        GeoSymbolTester.testGeoSymbols(vector, map, done);
    });

    it("Marker._containsPoint", function() {

        var geometry = new Z.Marker(center, {
            symbol: {
                markerFile : Z.prefix + 'images/control/2.png',
                markerHeight : 30,
                markerWidth : 22,
                dx : 0,
                dy : 0
            }
        });
        layer = new Z.VectorLayer('id');
        map.addLayer(layer);
        layer.addGeometry(geometry);

        var spy = sinon.spy();
        geometry.on('click', spy);
        //TODO 因为marker的width和height为0, 所以无法击中
        happen.click(canvasContainer, {
            clientX: 400 + 8,
            clientY: 300 + 8
        });

        //expect(spy.called).to.be.ok();
    });

});
<|endoftext|>


<commit_before>// The function is executed in the context of the inspected page.
var page_getKnockoutInfo = function(shouldSerialize) {
	"use strict";
	var debug=function(m){
		//console.log(m);
	};
	var ko = window.ko;
	if( !ko){
		if(typeof window.require === 'function') {
			try{
				ko = require('ko');
			} catch(e) { /*ingore */ }
			if(!ko){
				try{
					ko = require('knockout');
				} catch(e) { /*ingore */ }
			}
		}
		if(!ko) {
			return {error:"knockout.js is not used in the page (ko is undefined). Maybe you are using iFrames, if so, browse to the url of the frame and try again."};
		}
	}

	var isString = function (obj) {	// _ implementation
		return toString.call(obj) == '[object String]';
	};

	function isFunction(functionToCheck) {
		var getType = {};
		var res= functionToCheck && getType.toString.call(functionToCheck) == '[object Function]';
		return res;
	}

	var i=0;
	// Make a shallow copy with a null prototype, don't show prototype stuff in panel
	var copy = { __proto__: null };
	var copy2 = { __proto__: null };
	debug($0);
	var context = $0 ?ko.contextFor($0) : {};
	debug("context ");
	debug(context);

	try{
		var props = Object.getOwnPropertyNames(context);
		for (i = 0; i < props.length; ++i){
			//you probably want to see the value of the index instead of the ko.observable function
			if(props[i]==="$index"){
				copy["$index()"] = context[props[i]]();
			}
			else if(props[i]==="$root"){
				if(context[props[i]] != window){
					try{
						if(shouldSerialize){
							copy["$root_toJS"] = ko.toJS(context[props[i]]);
						}
						else{
							copy["$root"] = context[props[i]];
						}
					}
					catch(toJsErr){
						copy["$root_toJS"]="Error: ko.toJS("+props[i]+")";
						copy["$root_toJS_exc"]=toJsErr;
					}
				}
				else{
					copy["$root"]="(Global window object)";
				}
			}
			else{
				copy[props[i]] = context[props[i]];
			}
		}
	}
	catch(err){
		//when you don't select a dom node but plain text  (rare)
		debug(err);
		return {info:"Please select a dom node with ko data.",ExtensionError:err};
	}

	try{
		var dataFor= $0 ? ko.dataFor($0): {};
		var data =shouldSerialize? ko.toJS(dataFor) : ko.utils.unwrapObservable(dataFor);

		if(isString(data)){	//don't do getOwnPropertyNames if it's not an object
			copy["vm_string"]=data;
		}
		else{
			try{
				var props2 = Object.getOwnPropertyNames(data);
				for (i = 0; i < props2.length; ++i){
					//create a empty object that contains the whole vm in a expression. contains even the functions.
					copy2[props2[i]] = ko.utils.unwrapObservable(data[props2[i]]);
					//show the basic properties of the vm directly, without the need to collapse anything
					if(shouldSerialize){//if you don't serialize, the isFunction check is useless cause observables are functions
						if(!isFunction(data[props2[i]])){
							//chrome sorts alphabetically, make sure the properties come first
							copy[" "+props2[i]] = data[props2[i]];
						}
					}
					else{
						copy[" "+props2[i]] =  ko.utils.unwrapObservable(data[props2[i]]);
					}
				}
				//set the whole vm in a expression (collapsable). contains even the functions.
				copy["vm_toJS"]=copy2;

			}
			catch(err){
				//I don't know the type but I'll try to display the data
				copy["vm_no_object"]=data;
			}
		}
	}
	catch(error){
		copy["error"]=error;
	}
	return copy;
};
var pluginTitle="Knockout context";
var shouldDoKOtoJS=true;
var localStorageError="Unable to get value from localstorage. Check the privacy settings of chrome";
try{
	var shouldDoKOtoJSValue=localStorage["shouldDoKOtoJS"];
}
catch(e){
	console.log(localStorageError,e);
}
if(shouldDoKOtoJSValue)
	shouldDoKOtoJS=JSON.parse(shouldDoKOtoJSValue);

chrome.devtools.panels.elements.createSidebarPane(pluginTitle,function(sidebar) {
	"use strict";
	function updateElementProperties() {
		//pase a function as a string that will be executed later on by chrome
		sidebar.setExpression("(" + page_getKnockoutInfo.toString() + ")("+shouldDoKOtoJS+")");
	}
	//initial
	updateElementProperties();
	//attach to chrome events so that the sidebarPane refreshes (contains up to date info)
	chrome.devtools.panels.elements.onSelectionChanged.addListener(updateElementProperties);
	sidebar.onShown.addListener(updateElementProperties);

  //listen to a message send by the background page (when the chrome windows's focus changes)
  chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
      updateElementProperties();
  });

});

var localStorageValue=undefined;
try{
	localStorageValue = localStorage["shouldPanelBeShown"];
}
catch(e){
	console.log(localStorageError,e);
}
var shouldPanelBeShown=true;
if(localStorageValue)
	shouldPanelBeShown=JSON.parse(localStorageValue);
if(shouldPanelBeShown){
	var knockoutPanel = chrome.devtools.panels.create(
		"KnockoutJS",
		"logo.png",
		"/pages/panel.html"
	);
}

<commit_msg>fixed possible try catch scope issue<commit_after>// The function is executed in the context of the inspected page.
var page_getKnockoutInfo = function(shouldSerialize) {
	"use strict";
	var debug=function(m){
		//console.log(m);
	};
	var ko = window.ko;
	if( !ko){
		if(typeof window.require === 'function') {
			try{
				ko = require('ko');
			} catch(e) { /*ingore */ }
			if(!ko){
				try{
					ko = require('knockout');
				} catch(e) { /*ingore */ }
			}
		}
		if(!ko) {
			return {error:"knockout.js is not used in the page (ko is undefined). Maybe you are using iFrames, if so, browse to the url of the frame and try again."};
		}
	}

	var isString = function (obj) {	// _ implementation
		return toString.call(obj) == '[object String]';
	};

	function isFunction(functionToCheck) {
		var getType = {};
		var res= functionToCheck && getType.toString.call(functionToCheck) == '[object Function]';
		return res;
	}

	var i=0;
	// Make a shallow copy with a null prototype, don't show prototype stuff in panel
	var copy = { __proto__: null };
	var copy2 = { __proto__: null };
	debug($0);
	var context = $0 ?ko.contextFor($0) : {};
	debug("context ");
	debug(context);

	try{
		var props = Object.getOwnPropertyNames(context);
		for (i = 0; i < props.length; ++i){
			//you probably want to see the value of the index instead of the ko.observable function
			if(props[i]==="$index"){
				copy["$index()"] = context[props[i]]();
			}
			else if(props[i]==="$root"){
				if(context[props[i]] != window){
					try{
						if(shouldSerialize){
							copy["$root_toJS"] = ko.toJS(context[props[i]]);
						}
						else{
							copy["$root"] = context[props[i]];
						}
					}
					catch(toJsErr){
						copy["$root_toJS"]="Error: ko.toJS("+props[i]+")";
						copy["$root_toJS_exc"]=toJsErr;
					}
				}
				else{
					copy["$root"]="(Global window object)";
				}
			}
			else{
				copy[props[i]] = context[props[i]];
			}
		}
	}
	catch(err){
		//when you don't select a dom node but plain text  (rare)
		debug(err);
		return {info:"Please select a dom node with ko data.",ExtensionError:err};
	}

	try{
		var dataFor= $0 ? ko.dataFor($0): {};
		var data =shouldSerialize? ko.toJS(dataFor) : ko.utils.unwrapObservable(dataFor);

		if(isString(data)){	//don't do getOwnPropertyNames if it's not an object
			copy["vm_string"]=data;
		}
		else{
			try{
				var props2 = Object.getOwnPropertyNames(data);
				for (i = 0; i < props2.length; ++i){
					//create a empty object that contains the whole vm in a expression. contains even the functions.
					copy2[props2[i]] = ko.utils.unwrapObservable(data[props2[i]]);
					//show the basic properties of the vm directly, without the need to collapse anything
					if(shouldSerialize){//if you don't serialize, the isFunction check is useless cause observables are functions
						if(!isFunction(data[props2[i]])){
							//chrome sorts alphabetically, make sure the properties come first
							copy[" "+props2[i]] = data[props2[i]];
						}
					}
					else{
						copy[" "+props2[i]] =  ko.utils.unwrapObservable(data[props2[i]]);
					}
				}
				//set the whole vm in a expression (collapsable). contains even the functions.
				copy["vm_toJS"]=copy2;

			}
			catch(err){
				//I don't know the type but I'll try to display the data
				copy["vm_no_object"]=data;
			}
		}
	}
	catch(error){
		copy["error"]=error;
	}
	return copy;
};
var pluginTitle="Knockout context";
var shouldDoKOtoJS=true;
var localStorageError="Unable to get value from localstorage. Check the privacy settings of chrome";
try{
	var shouldDoKOtoJSValue=localStorage["shouldDoKOtoJS"];
	if(shouldDoKOtoJSValue)
		shouldDoKOtoJS=JSON.parse(shouldDoKOtoJSValue);
}
catch(e){
	console.log(localStorageError,e);
}

chrome.devtools.panels.elements.createSidebarPane(pluginTitle,function(sidebar) {
	"use strict";
	function updateElementProperties() {
		//pase a function as a string that will be executed later on by chrome
		sidebar.setExpression("(" + page_getKnockoutInfo.toString() + ")("+shouldDoKOtoJS+")");
	}
	//initial
	updateElementProperties();
	//attach to chrome events so that the sidebarPane refreshes (contains up to date info)
	chrome.devtools.panels.elements.onSelectionChanged.addListener(updateElementProperties);
	sidebar.onShown.addListener(updateElementProperties);

  //listen to a message send by the background page (when the chrome windows's focus changes)
  chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
      updateElementProperties();
  });

});

var localStorageValue=undefined;
try{
	localStorageValue = localStorage["shouldPanelBeShown"];
}
catch(e){
	console.log(localStorageError,e);
}
var shouldPanelBeShown=true;
if(localStorageValue)
	shouldPanelBeShown=JSON.parse(localStorageValue);
if(shouldPanelBeShown){
	var knockoutPanel = chrome.devtools.panels.create(
		"KnockoutJS",
		"logo.png",
		"/pages/panel.html"
	);
}

<|endoftext|>