<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

var App = require('app');
var objectUtils = require('utils/object_utils');

App.ConfigsComparator = Em.Mixin.create({

  isCompareMode: false,

  compareServiceVersion: null,

  selectedVersion: null,

  isVersionDefault: Em.K,
  /**
   * load version configs for comparison
   * @param allConfigs
   * @return {object}
   * @public
   * @method loadCompareVersionConfigs
   */
  loadCompareVersionConfigs: function (allConfigs) {
    var dfd = $.Deferred();
    var self = this;
    var compareServiceVersions = [];

    if (this.get('compareServiceVersion')) {
      if (!this.isVersionDefault(this.get('compareServiceVersion').get('version'))) {
        compareServiceVersions = [this.get('compareServiceVersion').get('version'), this.get('selectedVersion')];
      } else {
        compareServiceVersions = [this.get('compareServiceVersion').get('version')];
      }
      this.getCompareVersionConfigs(compareServiceVersions).done(function (json) {
        allConfigs.setEach('isEditable', false);
        self.initCompareConfig(allConfigs, json);
        self.setProperties({
          compareServiceVersion: null,
          isCompareMode: true
        });
        dfd.resolve(true);
      }).fail(function () {
        self.set('compareServiceVersion', null);
        dfd.resolve(true);
      });
    } else {
      self.set('isCompareMode', false);
      allConfigs.setEach('isComparison', false);
      dfd.resolve(false);
    }
    return dfd.promise();
  },

  /**
   * attach analogical config to each property for comparison
   * @param allConfigs
   * @param json
   * @private
   * @method initCompareConfig
   */
  initCompareConfig: function(allConfigs, json) {
    var serviceVersionMap = {};
    var configNamesMap = allConfigs.toWickMapByProperty('name');
    var serviceName = this.get('content.serviceName');
    var compareVersionNumber = this.get('compareServiceVersion').get('version');
    //indicate whether compared versions are from non-default group
    var compareNonDefaultVersions = (json.items.length > 1);

    serviceVersionMap[compareVersionNumber] = {};
    if (compareNonDefaultVersions) {
      serviceVersionMap[this.get('selectedVersion')] = {};
    }

    json.items.forEach(function (item) {
      item.configurations.forEach(function (configuration) {
        if (serviceName == 'YARN' && configuration.type == 'capacity-scheduler') {
          var configsToSkip = this.get('settingsTabProperties').filterProperty('filename', 'capacity-scheduler.xml').filterProperty('subSection').mapProperty('name');
          // put all properties in a single textarea for capacity-scheduler
          var value = '';
          for (var prop in configuration.properties) {
            if (configsToSkip.contains(prop)) {
              serviceVersionMap[item.service_config_version][prop + '-' + configuration.type] = {
                name: prop,
                value: configuration.properties[prop],
                type: configuration.type,
                tag: configuration.tag,
                version: configuration.version,
                service_config_version: item.service_config_version
              };
            } else {
              value += prop + '=' + configuration.properties[prop] + '\n';
            }
          }
          serviceVersionMap[item.service_config_version][configuration.type + '-' + configuration.type] = {
            name: configuration.type,
            value: value,
            type: configuration.type,
            tag: configuration.tag,
            version: configuration.version,
            service_config_version: item.service_config_version
          };
        } else {
          for (var prop in configuration.properties) {
            serviceVersionMap[item.service_config_version][prop + '-' + configuration.type] = {
              name: prop,
              value: configuration.properties[prop],
              type: configuration.type,
              tag: configuration.tag,
              version: configuration.version,
              service_config_version: item.service_config_version
            };
            if (Em.isNone(configNamesMap[prop])) {
              allConfigs.push(this.getMockConfig(prop, serviceName, App.config.getOriginalFileName(configuration.type)));
            }
          }
        }
        if (configuration.properties_attributes && configuration.properties_attributes.final) {
          for (var final in configuration.properties_attributes.final) {
            serviceVersionMap[item.service_config_version][final + '-' + configuration.type].isFinal = (configuration.properties_attributes.final[final] === 'true');
          }
        }
      }, this);
    }, this);

    if (compareNonDefaultVersions) {
      allConfigs.forEach(function (serviceConfig) {
        this.setCompareConfigs(serviceConfig, serviceVersionMap, compareVersionNumber, this.get('selectedVersion'));
      }, this);
    } else {
      allConfigs.forEach(function (serviceConfig) {
        var serviceCfgVersionMap = serviceVersionMap[this.get('compareServiceVersion').get('version')];
        var compareConfig = serviceCfgVersionMap[serviceConfig.name + '-' + App.config.getConfigTagFromFileName(serviceConfig.filename)];
        this.setCompareDefaultGroupConfig(serviceConfig, compareConfig);
      }, this);
    }
  },

  /**
   * set compare properties to service config of non-default group
   * @param serviceConfig
   * @param serviceVersionMap
   * @param compareVersion
   * @param selectedVersion
   * @private
   * @method setCompareConfigs
   */
  setCompareConfigs: function (serviceConfig, serviceVersionMap, compareVersion, selectedVersion) {
    var compareConfig = serviceVersionMap[compareVersion][Em.get(serviceConfig, 'name') + '-' + App.config.getConfigTagFromFileName(Em.get(serviceConfig, 'filename'))];
    var selectedConfig = serviceVersionMap[selectedVersion][Em.get(serviceConfig, 'name') + '-' + App.config.getConfigTagFromFileName(Em.get(serviceConfig, 'filename'))];

    Em.set(serviceConfig, 'compareConfigs', []);
    Em.set(serviceConfig, 'isComparison', true);

    if (compareConfig && selectedConfig) {
      Em.get(serviceConfig, 'compareConfigs').push(this.getComparisonConfig(serviceConfig, compareConfig));
      Em.get(serviceConfig, 'compareConfigs').push(this.getComparisonConfig(serviceConfig, selectedConfig));
      Em.set(serviceConfig, 'hasCompareDiffs', this.hasCompareDiffs(Em.get(serviceConfig,'compareConfigs')[0], Em.get(serviceConfig,'compareConfigs')[1]));
    } else if (compareConfig && !selectedConfig) {
      Em.get(serviceConfig, 'compareConfigs').push(this.getComparisonConfig(serviceConfig, compareConfig));
      Em.get(serviceConfig, 'compareConfigs').push(this.getMockComparisonConfig(selectedConfig, selectedVersion));
      Em.set(serviceConfig, 'hasCompareDiffs', true);
    } else if (!compareConfig && selectedConfig) {
      Em.get(serviceConfig, 'compareConfigs').push(this.getMockComparisonConfig(selectedConfig, compareVersion));
      Em.get(serviceConfig, 'compareConfigs').push(this.getComparisonConfig(serviceConfig, selectedConfig));
      Em.set(serviceConfig, 'hasCompareDiffs', true);
    }
  },

  /**
   * init attributes and wrap mock compare config into App.ServiceConfigProperty
   * @param serviceConfig
   * @param compareServiceVersion
   * @return {object}
   * @private
   * @method getMockComparisonConfig
   */
  getMockComparisonConfig: function (serviceConfig, compareServiceVersion) {
    var compareObject = $.extend(true, {isComparison: false},  serviceConfig);
    Em.set(compareObject, 'isEditable', false);

    Em.set(compareObject, 'serviceVersion', App.ServiceConfigVersion.find(this.get('content.serviceName') + "_" + compareServiceVersion));
    Em.set(compareObject, 'isMock', true);
    Em.set(compareObject, 'displayType', 'label');
    compareObject = App.ServiceConfigProperty.create(compareObject);
    compareObject.set('value', Em.I18n.t('common.property.undefined'));
    return compareObject;
  },

  /**
   * init attributes and wrap compare config into App.ServiceConfigProperty
   * @param serviceConfig
   * @param compareConfig
   * @return {object}
   * @private
   * @method getComparisonConfig
   */
  getComparisonConfig: function (serviceConfig, compareConfig) {
    var compareObject = $.extend(true, {isComparison: false, isOriginalSCP: false},  serviceConfig);
    Em.set(compareObject, 'isEditable', false);

    if (compareConfig) {
      if (Em.get(serviceConfig, 'isMock')) {
        Em.set(compareObject, 'displayType', 'string');
        Em.set(compareObject, 'isMock', false);
      }
      Em.set(compareObject, 'serviceVersion', App.ServiceConfigVersion.find(this.get('content.serviceName') + "_" + compareConfig.service_config_version));
      compareObject = App.ServiceConfigProperty.create(compareObject);
      compareObject.setProperties({
        isFinal: !!compareConfig.isFinal,
        value: App.config.formatPropertyValue(serviceConfig, compareConfig.value),
        compareConfigs: null,
        isOriginalSCP: false
      });
    }
    return compareObject;
  },

  /**
   * set compare properties to service config of default group
   * @param serviceConfig
   * @param compareConfig
   * @private
   * @method setCompareDefaultGroupConfig
   */
  setCompareDefaultGroupConfig: function (serviceConfig, compareConfig) {
    var compareObject = {};
    var isEmptyProp = App.isEmptyObject(serviceConfig);

    Em.set(serviceConfig, 'compareConfigs', []);
    Em.set(serviceConfig, 'isComparison', true);

    //if config isn't reconfigurable then it can't have changed value to compare
    if (compareConfig) {
      compareObject = this.getComparisonConfig(serviceConfig, compareConfig);
      Em.set(serviceConfig, 'hasCompareDiffs', Em.get(serviceConfig, 'isMock') || this.hasCompareDiffs(serviceConfig, compareObject));
      Em.get(serviceConfig, 'compareConfigs').push(compareObject);
      // user custom property or property that was added during upgrade
    } else if (Em.get(serviceConfig, 'isUserProperty') || (!isEmptyProp && !compareConfig && Em.get(serviceConfig, 'isRequiredByAgent') !== false)) {
      Em.get(serviceConfig, 'compareConfigs').push(this.getMockComparisonConfig(serviceConfig, this.get('compareServiceVersion.version')));
      Em.set(serviceConfig, 'hasCompareDiffs', true);
    }

    return serviceConfig;
  },

  /**
   * check value and final attribute of original and compare config for differences
   * if config is password, it won't be shown in the comparison
   *
   * @param originalConfig
   * @param compareConfig
   * @return {Boolean}
   * @private
   * @method hasCompareDiffs
   */
  hasCompareDiffs: function (originalConfig, compareConfig) {
    var displayType = Em.get(originalConfig, 'displayType');
    var notShownTypes = ['password'];
    if (notShownTypes.contains(displayType)) {
      return false;
    }
    var originalValue = App.config.trimProperty({ value: Em.get(originalConfig, 'value'), displayType: 'string' });
    var compareValue = App.config.trimProperty({ value: Em.get(compareConfig, 'value'), displayType: 'string' });

    if (originalValue.toArray) {
      originalValue = originalValue.toArray();
    }
    if (compareValue.toArray) {
      compareValue = compareValue.toArray();
    }

    if (originalValue instanceof Array) {
      originalValue.sort();
    }
    if (compareValue instanceof Array) {
      compareValue.sort();
    }

    return (!objectUtils.deepEqual(originalValue, compareValue)) || (!!Em.get(originalConfig, 'isFinal') !== !!Em.get(compareConfig, 'isFinal'));
  },

  /**
   * generate mock config object
   * @param name
   * @param serviceName
   * @param filename
   * @return {Object}
   * @private
   * @method getMockConfig
   */
  getMockConfig: function (name, serviceName, filename) {
    var undefinedConfig = App.configsCollection.getConfigByName(name, filename) || {
      description: name,
      displayName: name,
      isOverridable: false,
      isReconfigurable: false,
      isRequired: false,
      isRequiredByAgent: false,
      isSecureConfig: false,
      isUserProperty: true,
      isVisible: true,
      isOriginalSCP: false,
      name: name,
      filename: filename,
      serviceName: serviceName,
      category: App.config.getDefaultCategory(false, filename),
      value: Em.I18n.t('common.property.undefined'),
      isMock: true,
      displayType: 'label'
    };
    return App.ServiceConfigProperty.create(undefinedConfig);
  },

  /**
   * get configs of chosen version from server to compare
   * @param compareServiceVersions
   * @return {$.ajax}
   * @private
   * @method getCompareVersionConfigs
   */
  getCompareVersionConfigs: function (compareServiceVersions) {
    this.set('versionLoaded', false);

    return App.ajax.send({
      name: 'service.serviceConfigVersions.get.multiple',
      sender: this,
      data: {
        serviceName: this.get('content.serviceName'),
        serviceConfigVersions: compareServiceVersions
      }
    });
  }

});
<commit_msg>AMBARI-14151 config comparison should not happen for UI only configs. (ababiichuk)<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

var App = require('app');
var objectUtils = require('utils/object_utils');

App.ConfigsComparator = Em.Mixin.create({

  isCompareMode: false,

  compareServiceVersion: null,

  selectedVersion: null,

  isVersionDefault: Em.K,
  /**
   * load version configs for comparison
   * @param allConfigs
   * @return {object}
   * @public
   * @method loadCompareVersionConfigs
   */
  loadCompareVersionConfigs: function (allConfigs) {
    var dfd = $.Deferred();
    var self = this;
    var compareServiceVersions = [];

    if (this.get('compareServiceVersion')) {
      if (!this.isVersionDefault(this.get('compareServiceVersion').get('version'))) {
        compareServiceVersions = [this.get('compareServiceVersion').get('version'), this.get('selectedVersion')];
      } else {
        compareServiceVersions = [this.get('compareServiceVersion').get('version')];
      }
      this.getCompareVersionConfigs(compareServiceVersions).done(function (json) {
        allConfigs.setEach('isEditable', false);
        self.initCompareConfig(allConfigs, json);
        self.setProperties({
          compareServiceVersion: null,
          isCompareMode: true
        });
        dfd.resolve(true);
      }).fail(function () {
        self.set('compareServiceVersion', null);
        dfd.resolve(true);
      });
    } else {
      self.set('isCompareMode', false);
      allConfigs.setEach('isComparison', false);
      dfd.resolve(false);
    }
    return dfd.promise();
  },

  /**
   * attach analogical config to each property for comparison
   * @param allConfigs
   * @param json
   * @private
   * @method initCompareConfig
   */
  initCompareConfig: function(allConfigs, json) {
    var serviceVersionMap = {};
    var configNamesMap = allConfigs.toWickMapByProperty('name');
    var serviceName = this.get('content.serviceName');
    var compareVersionNumber = this.get('compareServiceVersion').get('version');
    //indicate whether compared versions are from non-default group
    var compareNonDefaultVersions = (json.items.length > 1);

    serviceVersionMap[compareVersionNumber] = {};
    if (compareNonDefaultVersions) {
      serviceVersionMap[this.get('selectedVersion')] = {};
    }

    json.items.forEach(function (item) {
      item.configurations.forEach(function (configuration) {
        if (serviceName == 'YARN' && configuration.type == 'capacity-scheduler') {
          var configsToSkip = this.get('settingsTabProperties').filterProperty('filename', 'capacity-scheduler.xml').filterProperty('subSection').mapProperty('name');
          // put all properties in a single textarea for capacity-scheduler
          var value = '';
          for (var prop in configuration.properties) {
            if (configsToSkip.contains(prop)) {
              serviceVersionMap[item.service_config_version][prop + '-' + configuration.type] = {
                name: prop,
                value: configuration.properties[prop],
                type: configuration.type,
                tag: configuration.tag,
                version: configuration.version,
                service_config_version: item.service_config_version
              };
            } else {
              value += prop + '=' + configuration.properties[prop] + '\n';
            }
          }
          serviceVersionMap[item.service_config_version][configuration.type + '-' + configuration.type] = {
            name: configuration.type,
            value: value,
            type: configuration.type,
            tag: configuration.tag,
            version: configuration.version,
            service_config_version: item.service_config_version
          };
        } else {
          for (var prop in configuration.properties) {
            serviceVersionMap[item.service_config_version][prop + '-' + configuration.type] = {
              name: prop,
              value: configuration.properties[prop],
              type: configuration.type,
              tag: configuration.tag,
              version: configuration.version,
              service_config_version: item.service_config_version
            };
            if (Em.isNone(configNamesMap[prop])) {
              allConfigs.push(this.getMockConfig(prop, serviceName, App.config.getOriginalFileName(configuration.type)));
            }
          }
        }
        if (configuration.properties_attributes && configuration.properties_attributes.final) {
          for (var final in configuration.properties_attributes.final) {
            serviceVersionMap[item.service_config_version][final + '-' + configuration.type].isFinal = (configuration.properties_attributes.final[final] === 'true');
          }
        }
      }, this);
    }, this);

    if (compareNonDefaultVersions) {
      allConfigs.forEach(function (serviceConfig) {
        if (Em.get(serviceConfig, 'isRequiredByAgent') !== false) {
          this.setCompareConfigs(serviceConfig, serviceVersionMap, compareVersionNumber, this.get('selectedVersion'));
        }
      }, this);
    } else {
      allConfigs.forEach(function (serviceConfig) {
        if (Em.get(serviceConfig, 'isRequiredByAgent') !== false) {
          var serviceCfgVersionMap = serviceVersionMap[this.get('compareServiceVersion').get('version')];
          var compareConfig = serviceCfgVersionMap[serviceConfig.name + '-' + App.config.getConfigTagFromFileName(serviceConfig.filename)];
          this.setCompareDefaultGroupConfig(serviceConfig, compareConfig);
        }
      }, this);
    }
  },

  /**
   * set compare properties to service config of non-default group
   * @param serviceConfig
   * @param serviceVersionMap
   * @param compareVersion
   * @param selectedVersion
   * @private
   * @method setCompareConfigs
   */
  setCompareConfigs: function (serviceConfig, serviceVersionMap, compareVersion, selectedVersion) {
    var compareConfig = serviceVersionMap[compareVersion][Em.get(serviceConfig, 'name') + '-' + App.config.getConfigTagFromFileName(Em.get(serviceConfig, 'filename'))];
    var selectedConfig = serviceVersionMap[selectedVersion][Em.get(serviceConfig, 'name') + '-' + App.config.getConfigTagFromFileName(Em.get(serviceConfig, 'filename'))];

    Em.set(serviceConfig, 'compareConfigs', []);
    Em.set(serviceConfig, 'isComparison', true);

    if (compareConfig && selectedConfig) {
      Em.get(serviceConfig, 'compareConfigs').push(this.getComparisonConfig(serviceConfig, compareConfig));
      Em.get(serviceConfig, 'compareConfigs').push(this.getComparisonConfig(serviceConfig, selectedConfig));
      Em.set(serviceConfig, 'hasCompareDiffs', this.hasCompareDiffs(Em.get(serviceConfig,'compareConfigs')[0], Em.get(serviceConfig,'compareConfigs')[1]));
    } else if (compareConfig && !selectedConfig) {
      Em.get(serviceConfig, 'compareConfigs').push(this.getComparisonConfig(serviceConfig, compareConfig));
      Em.get(serviceConfig, 'compareConfigs').push(this.getMockComparisonConfig(selectedConfig, selectedVersion));
      Em.set(serviceConfig, 'hasCompareDiffs', true);
    } else if (!compareConfig && selectedConfig) {
      Em.get(serviceConfig, 'compareConfigs').push(this.getMockComparisonConfig(selectedConfig, compareVersion));
      Em.get(serviceConfig, 'compareConfigs').push(this.getComparisonConfig(serviceConfig, selectedConfig));
      Em.set(serviceConfig, 'hasCompareDiffs', true);
    }
  },

  /**
   * init attributes and wrap mock compare config into App.ServiceConfigProperty
   * @param serviceConfig
   * @param compareServiceVersion
   * @return {object}
   * @private
   * @method getMockComparisonConfig
   */
  getMockComparisonConfig: function (serviceConfig, compareServiceVersion) {
    var compareObject = $.extend(true, {isComparison: false},  serviceConfig);
    Em.set(compareObject, 'isEditable', false);

    Em.set(compareObject, 'serviceVersion', App.ServiceConfigVersion.find(this.get('content.serviceName') + "_" + compareServiceVersion));
    Em.set(compareObject, 'isMock', true);
    Em.set(compareObject, 'displayType', 'label');
    compareObject = App.ServiceConfigProperty.create(compareObject);
    compareObject.set('value', Em.I18n.t('common.property.undefined'));
    return compareObject;
  },

  /**
   * init attributes and wrap compare config into App.ServiceConfigProperty
   * @param serviceConfig
   * @param compareConfig
   * @return {object}
   * @private
   * @method getComparisonConfig
   */
  getComparisonConfig: function (serviceConfig, compareConfig) {
    var compareObject = $.extend(true, {isComparison: false, isOriginalSCP: false},  serviceConfig);
    Em.set(compareObject, 'isEditable', false);

    if (compareConfig) {
      if (Em.get(serviceConfig, 'isMock')) {
        Em.set(compareObject, 'displayType', 'string');
        Em.set(compareObject, 'isMock', false);
      }
      Em.set(compareObject, 'serviceVersion', App.ServiceConfigVersion.find(this.get('content.serviceName') + "_" + compareConfig.service_config_version));
      compareObject = App.ServiceConfigProperty.create(compareObject);
      compareObject.setProperties({
        isFinal: !!compareConfig.isFinal,
        value: App.config.formatPropertyValue(serviceConfig, compareConfig.value),
        compareConfigs: null,
        isOriginalSCP: false
      });
    }
    return compareObject;
  },

  /**
   * set compare properties to service config of default group
   * @param serviceConfig
   * @param compareConfig
   * @private
   * @method setCompareDefaultGroupConfig
   */
  setCompareDefaultGroupConfig: function (serviceConfig, compareConfig) {
    var compareObject = {};
    var isEmptyProp = App.isEmptyObject(serviceConfig);

    Em.set(serviceConfig, 'compareConfigs', []);
    Em.set(serviceConfig, 'isComparison', true);

    //if config isn't reconfigurable then it can't have changed value to compare
    if (compareConfig) {
      compareObject = this.getComparisonConfig(serviceConfig, compareConfig);
      Em.set(serviceConfig, 'hasCompareDiffs', Em.get(serviceConfig, 'isMock') || this.hasCompareDiffs(serviceConfig, compareObject));
      Em.get(serviceConfig, 'compareConfigs').push(compareObject);
      // user custom property or property that was added during upgrade
    } else if (Em.get(serviceConfig, 'isUserProperty') || (!isEmptyProp && !compareConfig && Em.get(serviceConfig, 'isRequiredByAgent') !== false)) {
      Em.get(serviceConfig, 'compareConfigs').push(this.getMockComparisonConfig(serviceConfig, this.get('compareServiceVersion.version')));
      Em.set(serviceConfig, 'hasCompareDiffs', true);
    }

    return serviceConfig;
  },

  /**
   * check value and final attribute of original and compare config for differences
   * if config is password, it won't be shown in the comparison
   *
   * @param originalConfig
   * @param compareConfig
   * @return {Boolean}
   * @private
   * @method hasCompareDiffs
   */
  hasCompareDiffs: function (originalConfig, compareConfig) {
    var displayType = Em.get(originalConfig, 'displayType');
    var notShownTypes = ['password'];
    if (notShownTypes.contains(displayType)) {
      return false;
    }
    var originalValue = App.config.trimProperty({ value: Em.get(originalConfig, 'value'), displayType: 'string' });
    var compareValue = App.config.trimProperty({ value: Em.get(compareConfig, 'value'), displayType: 'string' });

    if (originalValue.toArray) {
      originalValue = originalValue.toArray();
    }
    if (compareValue.toArray) {
      compareValue = compareValue.toArray();
    }

    if (originalValue instanceof Array) {
      originalValue.sort();
    }
    if (compareValue instanceof Array) {
      compareValue.sort();
    }

    return (!objectUtils.deepEqual(originalValue, compareValue)) || (!!Em.get(originalConfig, 'isFinal') !== !!Em.get(compareConfig, 'isFinal'));
  },

  /**
   * generate mock config object
   * @param name
   * @param serviceName
   * @param filename
   * @return {Object}
   * @private
   * @method getMockConfig
   */
  getMockConfig: function (name, serviceName, filename) {
    var undefinedConfig = App.configsCollection.getConfigByName(name, filename) || {
      description: name,
      displayName: name,
      isOverridable: false,
      isReconfigurable: false,
      isRequired: false,
      isRequiredByAgent: false,
      isSecureConfig: false,
      isUserProperty: true,
      isVisible: true,
      isOriginalSCP: false,
      name: name,
      filename: filename,
      serviceName: serviceName,
      category: App.config.getDefaultCategory(false, filename),
      value: Em.I18n.t('common.property.undefined'),
      isMock: true,
      displayType: 'label'
    };
    return App.ServiceConfigProperty.create(undefinedConfig);
  },

  /**
   * get configs of chosen version from server to compare
   * @param compareServiceVersions
   * @return {$.ajax}
   * @private
   * @method getCompareVersionConfigs
   */
  getCompareVersionConfigs: function (compareServiceVersions) {
    this.set('versionLoaded', false);

    return App.ajax.send({
      name: 'service.serviceConfigVersions.get.multiple',
      sender: this,
      data: {
        serviceName: this.get('content.serviceName'),
        serviceConfigVersions: compareServiceVersions
      }
    });
  }

});
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { connect } from 'react-redux'
import Loader from 'react-loader';
import Navbar from "../../components/Navbar.react"
import EntityItem from "../../components/EntityItem.react"
import EdgeConnection from "../../components/EdgeConnection.react"

import { Alert, Tooltip, OverlayTrigger  } from "react-bootstrap"

import { getNode, postNodeTags } from "../actions/index"
const responsiveClasses = [
        'col-xs-12','col-sm-10',
        'col-sm-offset-1',
        'col-md-10',
        'col-md-offset-1'].join(' ')

class NodeMain extends Component {

  constructor() {
     super();
     this.handleAlertDismiss = this.handleAlertDismiss.bind(this)
     this.addTag = this.addTag.bind(this)
     this.clearMessage = this.clearMessage.bind(this)
     const messageFlag = window.location.search.search((/message=true/))
     this.state = {
       messageFlag: messageFlag!=-1?true:false
     }
  }

  componentWillMount() {
    const { dispatch } = this.props;
    dispatch(getNode(window.location.pathname.replace('/node/',''))) //@todo include this in the page
  }

  render() {
    const { nodeResult } = this.props
    const { messageFlag } = this.state

    if (!nodeResult || Object.keys(nodeResult).length==0) {
      return (

        <Navbar/>
        <Loader top={'30%'} />

)
    }

    const { edges, faviconCDN, title, canonicalLink, description } = nodeResult

    const connectHref = "/connect?url="+canonicalLink

    const prettyLink = canonicalLink.replace(/^(http:\/\/|https:\/\/)/,'');

    let documentImage = ()
    if (faviconCDN){
      documentImage = ()
    }

    const nodeEdges = edges.map(function(edge, i){
      const { users, entity } = edge;
      const user = users[0].user;
      const createdAt = users[0].createdAt;
      const edgeComponent =(
      <EdgeConnection
        username={user.username}
        profileImageUrl={user.twitter.profile_image_url}
        createdAt={Number(createdAt)}
        length={users.length}
        />
      )

      return

        <EntityItem
          imageCDN={entity.imageCDN.url?entity.imageCDN.url:''}
          faviconCDN={entity.faviconCDN?entity.faviconCDN:''}
          canonicalLink={entity.canonicalLink}
          title={entity.title}
          description={entity.description}
          id={entity._id}
          edge={edgeComponent}
        />

    });

    const tooltip = (
      <Tooltip id="emptyNodeTooltip" className="wikiweb-tooltip">When you connect two URLs together, you are helping to grow the WikiWeb since other people can find those connections later.</Tooltip>
    );

    const emptyMessage = nodeEdges.length==0?

Shucks! There are no connections to this site yet.

        You chould be the first to
        create one.
          <OverlayTrigger placement="top" overlay={tooltip}>

          </OverlayTrigger>

    :null;

    let descriptionClipped = ''
    if (description.length > 200){
      descriptionClipped = description.slice(0,200)+"..."
    } else {
      descriptionClipped = description
    }

    return (

      <Navbar />

{title}

                {documentImage}

                {prettyLink}

{descriptionClipped}

                   <button
                     type="button"
                     className="btn btn-default resultNodeAddConnectionBox">
                     Add a new connection
                   </button>

          {messageFlag?
            <Alert bsStyle="success" onDismiss={this.handleAlertDismiss} style={{height: '115px'}}>

You added a new Connection!

Every connection on the WikiWeb makes it that much more useful for the next person.

                <input onFocus={this.clearMessage} id="tag-input" type="text"></input>
                <button onClick={this.addTag} style={{marginLeft: '3px'}}>Add Tag</button>

            </Alert>
           :null}

            {nodeEdges}

            {emptyMessage}

    );
  }

  addTag() {
    let tagInput = document.getElementById('tag-input').value
    let tagOutput = document.createElement('span')
    tagOutput.innerHTML = tagInput
    let targetNode = (document.getElementsByClassName('highlight-first')[0])
    targetNode = targetNode.getElementsByClassName('edgeTags')[0]
    targetNode.appendChild(tagOutput)

    // dispatch route
    this.props.dispatch(postNodeTags(1234,[document.getElementById('tag-input').value]))

    // clear input
    document.getElementById('tag-input').value = ''

    // give confirmation message to user
    let tagStatus=document.getElementsByClassName('tagging-status')[0]
    tagStatus.innerText = "tag added!"
  }

  clearMessage() {
    let tagStatus=document.getElementsByClassName('tagging-status')[0]
    tagStatus.innerText = ""
  }

  handleAlertDismiss(){
    this.setState({
      messageFlag:false
    })
  }

}

function mapStateToProps(state) {
  const { nodeResult, edgeResult } = state;
  return {
    nodeResult,
    edgeResult
  }
}

export default connect(mapStateToProps)(NodeMain)
<commit_msg>minor commit<commit_after>import React, { Component } from 'react'
import { connect } from 'react-redux'
import Loader from 'react-loader';
import Navbar from "../../components/Navbar.react"
import EntityItem from "../../components/EntityItem.react"
import EdgeConnection from "../../components/EdgeConnection.react"

import { Alert, Tooltip, OverlayTrigger  } from "react-bootstrap"

import { getNode, postNodeTags } from "../actions/index"
const responsiveClasses = [
        'col-xs-12','col-sm-10',
        'col-sm-offset-1',
        'col-md-10',
        'col-md-offset-1'].join(' ')

class NodeMain extends Component {

  constructor() {
     super();
     this.handleAlertDismiss = this.handleAlertDismiss.bind(this)
     this.addTag = this.addTag.bind(this)
     this.clearMessage = this.clearMessage.bind(this)
     const messageFlag = window.location.search.search((/message=true/))
     this.state = {
       messageFlag: messageFlag!=-1?true:false
     }
  }

  componentWillMount() {
    const { dispatch } = this.props;
    dispatch(getNode(window.location.pathname.replace('/node/',''))) //@todo include this in the page
  }

  render() {
    const { nodeResult } = this.props
    const { messageFlag } = this.state

    if (!nodeResult || Object.keys(nodeResult).length==0) {
      return (

        <Navbar/>
        <Loader top={'30%'} />

)
    }

    const { edges, faviconCDN, title, canonicalLink, description } = nodeResult

    const connectHref = "/connect?url="+canonicalLink

    const prettyLink = canonicalLink.replace(/^(http:\/\/|https:\/\/)/,'');

    let documentImage = ()
    if (faviconCDN){
      documentImage = ()
    }

    const nodeEdges = edges.map(function(edge, i){
      const { users, entity } = edge;
      const user = users[0].user;
      const createdAt = users[0].createdAt;
      const edgeComponent =(
      <EdgeConnection
        username={user.username}
        profileImageUrl={user.twitter.profile_image_url}
        createdAt={Number(createdAt)}
        length={users.length}
        />
      )

      return

        <EntityItem
          imageCDN={entity.imageCDN.url?entity.imageCDN.url:''}
          faviconCDN={entity.faviconCDN?entity.faviconCDN:''}
          canonicalLink={entity.canonicalLink}
          title={entity.title}
          description={entity.description}
          id={entity._id}
          edge={edgeComponent}
        />

    });

    const tooltip = (
      <Tooltip id="emptyNodeTooltip" className="wikiweb-tooltip">When you connect two URLs together, you are helping to grow the WikiWeb since other people can find those connections later.</Tooltip>
    );

    const emptyMessage = nodeEdges.length==0?

Shucks! There are no connections to this site yet.

        You chould be the first to
        create one.
          <OverlayTrigger placement="top" overlay={tooltip}>

          </OverlayTrigger>

    :null;

    let descriptionClipped = ''
    if (description.length > 200){
      descriptionClipped = description.slice(0,200)+"..."
    } else {
      descriptionClipped = description
    }

    return (

      <Navbar />

{title}

                {documentImage}

                {prettyLink}

{descriptionClipped}

                   <button
                     type="button"
                     className="btn btn-default resultNodeAddConnectionBox">
                     Add a new connection
                   </button>

          {messageFlag?
            <Alert bsStyle="success" onDismiss={this.handleAlertDismiss} style={{height: '130px'}}>

You added a new Connection!

Every connection on the WikiWeb makes it that much more useful for the next person.

                <input onFocus={this.clearMessage} id="tag-input" type="text"></input>
                <button onClick={this.addTag} style={{marginLeft: '3px'}}>Add Tag</button>

            </Alert>
           :null}

            {nodeEdges}

            {emptyMessage}

    );
  }

  addTag() {
    let tagInput = document.getElementById('tag-input').value
    let tagOutput = document.createElement('span')
    tagOutput.innerHTML = tagInput
    let targetNode = (document.getElementsByClassName('highlight-first')[0])
    targetNode = targetNode.getElementsByClassName('edgeTags')[0]
    targetNode.appendChild(tagOutput)

    // dispatch route
    this.props.dispatch(postNodeTags(1234,[document.getElementById('tag-input').value]))

    // clear input
    document.getElementById('tag-input').value = ''

    // give confirmation message to user
    let tagStatus=document.getElementsByClassName('tagging-status')[0]
    tagStatus.innerText = "tag added!"
  }

  clearMessage() {
    let tagStatus=document.getElementsByClassName('tagging-status')[0]
    tagStatus.innerText = ""
  }

  handleAlertDismiss(){
    this.setState({
      messageFlag:false
    })
  }

}

function mapStateToProps(state) {
  const { nodeResult, edgeResult } = state;
  return {
    nodeResult,
    edgeResult
  }
}

export default connect(mapStateToProps)(NodeMain)
<|endoftext|>


<commit_before>Admin.StatsMapTableView = Ember.View.extend({
  tagName: 'table',

  classNames: ['table', 'table-striped', 'table-bordered', 'table-condensed'],

  didInsertElement: function() {
    this.$().DataTable({
      searching: false,
      order: [[1, 'desc']],
      data: this.get('model.regions'),
      columns: [
        {
          data: 'name',
          title: 'Location',
          defaultContent: '-',
          render: _.bind(function(name, type, data) {
            if(type === 'display' && name && name !== '-') {
              var link, params;
              if(this.get('model.region_field') === 'request_ip_city') {
                params = _.clone(this.get('controller.query.params'));
                params.search = 'request_ip_city:"' + data.id + '"';
                link = '#/stats/logs/' + $.param(params);
              } else {
                params = _.clone(this.get('controller.query.params'));
                params.region = data.id;
                link = '#/stats/map/' + $.param(params);
              }

              return '' + _.escape(name) + '';
            }

            return name;
          }, this),
        },
        {
          data: 'hits',
          title: 'Hits',
          defaultContent: '-',
          render: function(number, type) {
            if(type === 'display' && number && number !== '-') {
              return numeral(number).format('0,0');
            }

            return number;
          },
        },
      ]
    });
  },

  refreshData: function() {
    var table = this.$().DataTable();
    table.clear();
    table.add(this.get('model.regions'));
  }.observes('model.regions'),
});
<commit_msg>Fix refreshing of map datatable.<commit_after>Admin.StatsMapTableView = Ember.View.extend({
  tagName: 'table',

  classNames: ['table', 'table-striped', 'table-bordered', 'table-condensed'],

  didInsertElement: function() {
    this.$().DataTable({
      searching: false,
      order: [[1, 'desc']],
      data: this.get('model.regions'),
      columns: [
        {
          data: 'name',
          title: 'Location',
          defaultContent: '-',
          render: _.bind(function(name, type, data) {
            if(type === 'display' && name && name !== '-') {
              var link, params;
              if(this.get('model.region_field') === 'request_ip_city') {
                params = _.clone(this.get('controller.query.params'));
                params.search = 'request_ip_city:"' + data.id + '"';
                link = '#/stats/logs/' + $.param(params);
              } else {
                params = _.clone(this.get('controller.query.params'));
                params.region = data.id;
                link = '#/stats/map/' + $.param(params);
              }

              return '' + _.escape(name) + '';
            }

            return name;
          }, this),
        },
        {
          data: 'hits',
          title: 'Hits',
          defaultContent: '-',
          render: function(number, type) {
            if(type === 'display' && number && number !== '-') {
              return numeral(number).format('0,0');
            }

            return number;
          },
        },
      ]
    });
  },

  refreshData: function() {
    var table = this.$().DataTable();
    table.clear();
    table.rows.add(this.get('model.regions')).draw();
  }.observes('model.regions'),
});
<|endoftext|>


<commit_before>function applyClickCallbackOnProtocolCards() {
  $('.protocol-card').off('click').on('click', function(e) {
    // Check whether this card is already active and deactivate it
    if ($(this).hasClass('active')) {
      resetPreviewPanel();
      $(this).removeClass('active');
    }
    else {
      var curr_protocol_card = $(this);
      $('.protocol-card').removeClass('active');
      curr_protocol_card.addClass('active');

      $.ajax({
        url: $(curr_protocol_card).data('show-url'),
        type: 'GET',
        dataType: 'json',
        data: {
          protocol_source: $(curr_protocol_card).data('protocol-source'),
          protocol_id: $(curr_protocol_card).data('show-protocol-id')
        },
        beforeSend: animateSpinner($('.protocol-preview-panel'), true),
        success: function(data) {
          $('.empty-preview-panel').hide();
          $('.full-preview-panel').show();
          $('.btn-holder').html($(curr_protocol_card).find('.external-import-btn').clone());
          $('.preview-iframe').contents().find('body').html(data.html);

          initLoadProtocolModalPreview();
          animateSpinner($('.protocol-preview-panel'), false);
        },
        error: function(_error) {
          // TODO: we should probably show some alert bubble
          resetPreviewPanel();
          animateSpinner($('.protocol-preview-panel'), false);
        }
      });
    }
    e.preventDefault();
    return false;
  });
}

// Resets preview to the default state
function resetPreviewPanel() {
  $('.empty-preview-panel').show();
  $('.full-preview-panel').hide();
}

// Reset whole view to the default state
function setDefaultViewState() {
  resetPreviewPanel();
  $('.empty-text').show();
  $('.list-wrapper').hide();
}

// Apply AJAX callbacks onto the search box
function applySearchCallback() {
  var timeout;
  // Submit form on every input in the search box
  $('input[name="key"]').off('input').on('input', function() {
    if (timeout)
      clearTimeout(timeout)

    timeout = setTimeout(function() {
      $('form.protocols-search-bar').submit();
    }, 500);
  });

  // Submit form when clicking on sort buttons
  $('input[name="sort_by"]').off('change').on('change', function () {
    $('form.protocols-search-bar').submit();
  });

  // Bind ajax calls on the form
  $('form.protocols-search-bar').off('ajax:success').off('ajax:error')
    .bind('ajax:success', function(evt, data, status, xhr) {
      if (data.html) {
        resetPreviewPanel();
        $('.empty-text').hide();
        $('.list-wrapper').show();

        $('.list-wrapper').html(data.html);
        applyClickCallbackOnProtocolCards();
        initLoadProtocolModalPreview();
      } else {
        setDefaultViewState();
      }
    })
    .bind("ajax:error", function(evt, xhr, status, error) {
      setDefaultViewState();

      console.log(xhr.responseText);
    });
}

function resetFormErrors(modal) {
  // Reset all errors
  modal.find('form > .form-group.has-error').removeClass('has-error');
  modal.find('form > .form-group>span.help-block').html('');
  modal.find('.general-error > span').html('');
}

function showFormErrors(modal, errors) {
  resetFormErrors(modal);

  // AR valdiation errors
  Object.keys(errors.protocol).forEach(function(key) {
    var input = modal.find('#protocol_' + key);
    var msg;
    msg = key.charAt(0).toUpperCase() + key.slice(1) + ': ' + errors.protocol[key].join(', ');
    if ((input.length > 0) && (errors.protocol[key].length > 0)) {
      input.parent().next('span.help-block').html(msg);
      input.parent().parent().addClass('has-error');
    } else if (errors.protocol[key].length > 0) {
      modal.find('.general-error > span').append(msg + '
');
    }
  });
}

function renderTable(table) {
  $(table).handsontable('render');
  // Yet another dirty hack to solve HandsOnTable problems
  if (parseInt($(table).css('height'), 10) < parseInt($(table).css('max-height'), 10) - 30) {
    $(table).find('.ht_master .wtHolder').css({ height: '100%', width: '100%' });
  }
}

// Expand all steps
function expandAllSteps() {
  $('.step .panel-collapse').collapse('show');
  $(document).find("[data-role='step-hot-table']").each(function() {
    renderTable($(this));
  });
  $(document).find('span.collapse-step-icon').each(function() {
    $(this).addClass('fa-caret-square-up');
    $(this).removeClass('fa-caret-square-down');
  });
}

function handleFormSubmit(modal) {
  var form = modal.find('form');
  form.on('submit', function(e) {
    var url = form.attr('action');
    e.preventDefault(); // avoid to execute the actual submit of the form.
    e.stopPropagation();
    animateSpinner(modal, true);
    $.ajax({
      type: 'POST',
      url: url,
      data: form.serialize(), // serializes the form's elements.
      success: function(data) {
        animateSpinner(modal, false);
        window.location.replace(data.redirect_url);
      },
      error: function(data) {
        showFormErrors(modal, data.responseJSON.errors);
      },
      complete: function() {
        animateSpinner(modal, false);
      }
    });
  });
}

function initLoadProtocolModalPreview() {
  $('.external-import-btn').off('click').on('click', function(e) {
    var link = $(this).parents('.protocol-card');
    animateSpinner(null, true);
    $.ajax({
      url: link.data('url'),
      type: 'GET',
      data: {
        protocol_source: link.data('protocol-source'),
        protocol_client_id: link.data('id')
      },
      dataType: 'json',
      success: function(data) {
        var modal = $('#protocol-preview-modal');
        var modalTitle = modal.find('.modal-title');
        var modalBody = modal.find('.modal-body');
        var modalFooter = modal.find('.modal-footer');

        modalTitle.html(data.title);
        modalBody.html(data.html);
        modalFooter.html(data.footer);
        initHandsOnTable(modalBody);
        modal.modal('show');
        expandAllSteps();
        initHandsOnTable(modalBody);

        if (data.validation_errors) {
          showFormErrors(modal, data.validation_errors);
        }

        initFormSubmits();
        handleFormSubmit(modal);
      },
      error: function(error) {
        console.log(error.responseJSON.errors);
        alert('Server error');
      },
      complete: function() {
        animateSpinner(null, false);
      }
    });
    e.preventDefault();
    return false;
  });
}

function initFormSubmits() {
  var modal = $('#protocol-preview-modal');
  modal.find('button[data-action=import_protocol]').off('click').on('click', function() {
    var form = modal.find('form');
    var hiddenField = form.find('#protocol_protocol_type');
    hiddenField.attr('value', $(this).data('import_type'));
    form.submit();
  });
}

applySearchCallback();
<commit_msg>Fix non-existent protocol-card in preview banner<commit_after>function applyClickCallbackOnProtocolCards() {
  $('.protocol-card').off('click').on('click', function(e) {
    // Check whether this card is already active and deactivate it
    if ($(this).hasClass('active')) {
      resetPreviewPanel();
      $(this).removeClass('active');
    }
    else {
      var curr_protocol_card = $(this);
      $('.protocol-card').removeClass('active');
      curr_protocol_card.addClass('active');

      $.ajax({
        url: $(curr_protocol_card).data('show-url'),
        type: 'GET',
        dataType: 'json',
        data: {
          protocol_source: $(curr_protocol_card).data('protocol-source'),
          protocol_id: $(curr_protocol_card).data('show-protocol-id')
        },
        beforeSend: animateSpinner($('.protocol-preview-panel'), true),
        success: function(data) {
          $('.empty-preview-panel').hide();
          $('.full-preview-panel').show();
          $('.btn-holder').html($(curr_protocol_card).find('.external-import-btn').clone());
          $('.preview-iframe').contents().find('body').html(data.html);

          initLoadProtocolModalPreview();
          animateSpinner($('.protocol-preview-panel'), false);
        },
        error: function(_error) {
          // TODO: we should probably show some alert bubble
          resetPreviewPanel();
          animateSpinner($('.protocol-preview-panel'), false);
        }
      });
    }
    e.preventDefault();
    return false;
  });
}

// Resets preview to the default state
function resetPreviewPanel() {
  $('.empty-preview-panel').show();
  $('.full-preview-panel').hide();
}

// Reset whole view to the default state
function setDefaultViewState() {
  resetPreviewPanel();
  $('.empty-text').show();
  $('.list-wrapper').hide();
}

// Apply AJAX callbacks onto the search box
function applySearchCallback() {
  var timeout;
  // Submit form on every input in the search box
  $('input[name="key"]').off('input').on('input', function() {
    if (timeout)
      clearTimeout(timeout)

    timeout = setTimeout(function() {
      $('form.protocols-search-bar').submit();
    }, 500);
  });

  // Submit form when clicking on sort buttons
  $('input[name="sort_by"]').off('change').on('change', function () {
    $('form.protocols-search-bar').submit();
  });

  // Bind ajax calls on the form
  $('form.protocols-search-bar').off('ajax:success').off('ajax:error')
    .bind('ajax:success', function(evt, data, status, xhr) {
      if (data.html) {
        resetPreviewPanel();
        $('.empty-text').hide();
        $('.list-wrapper').show();

        $('.list-wrapper').html(data.html);
        applyClickCallbackOnProtocolCards();
        initLoadProtocolModalPreview();
      } else {
        setDefaultViewState();
      }
    })
    .bind("ajax:error", function(evt, xhr, status, error) {
      setDefaultViewState();

      console.log(xhr.responseText);
    });
}

function resetFormErrors(modal) {
  // Reset all errors
  modal.find('form > .form-group.has-error').removeClass('has-error');
  modal.find('form > .form-group>span.help-block').html('');
  modal.find('.general-error > span').html('');
}

function showFormErrors(modal, errors) {
  resetFormErrors(modal);

  // AR valdiation errors
  Object.keys(errors.protocol).forEach(function(key) {
    var input = modal.find('#protocol_' + key);
    var msg;
    msg = key.charAt(0).toUpperCase() + key.slice(1) + ': ' + errors.protocol[key].join(', ');
    if ((input.length > 0) && (errors.protocol[key].length > 0)) {
      input.parent().next('span.help-block').html(msg);
      input.parent().parent().addClass('has-error');
    } else if (errors.protocol[key].length > 0) {
      modal.find('.general-error > span').append(msg + '
');
    }
  });
}

function renderTable(table) {
  $(table).handsontable('render');
  // Yet another dirty hack to solve HandsOnTable problems
  if (parseInt($(table).css('height'), 10) < parseInt($(table).css('max-height'), 10) - 30) {
    $(table).find('.ht_master .wtHolder').css({ height: '100%', width: '100%' });
  }
}

// Expand all steps
function expandAllSteps() {
  $('.step .panel-collapse').collapse('show');
  $(document).find("[data-role='step-hot-table']").each(function() {
    renderTable($(this));
  });
  $(document).find('span.collapse-step-icon').each(function() {
    $(this).addClass('fa-caret-square-up');
    $(this).removeClass('fa-caret-square-down');
  });
}

function handleFormSubmit(modal) {
  var form = modal.find('form');
  form.on('submit', function(e) {
    var url = form.attr('action');
    e.preventDefault(); // avoid to execute the actual submit of the form.
    e.stopPropagation();
    animateSpinner(modal, true);
    $.ajax({
      type: 'POST',
      url: url,
      data: form.serialize(), // serializes the form's elements.
      success: function(data) {
        animateSpinner(modal, false);
        window.location.replace(data.redirect_url);
      },
      error: function(data) {
        showFormErrors(modal, data.responseJSON.errors);
      },
      complete: function() {
        animateSpinner(modal, false);
      }
    });
  });
}

function initLoadProtocolModalPreview() {
  $('.external-import-btn').off('click').on('click', function(e) {
    var link = $(this).parents('.protocol-card');

    // When clicking on the banner button, we have no protocol-card parent
    if (link.length == 0)
      link = $('.protocol-card.active');

    animateSpinner(null, true);
    $.ajax({
      url: link.data('url'),
      type: 'GET',
      data: {
        protocol_source: link.data('protocol-source'),
        protocol_client_id: link.data('id')
      },
      dataType: 'json',
      success: function(data) {
        var modal = $('#protocol-preview-modal');
        var modalTitle = modal.find('.modal-title');
        var modalBody = modal.find('.modal-body');
        var modalFooter = modal.find('.modal-footer');

        modalTitle.html(data.title);
        modalBody.html(data.html);
        modalFooter.html(data.footer);
        initHandsOnTable(modalBody);
        modal.modal('show');
        expandAllSteps();
        initHandsOnTable(modalBody);

        if (data.validation_errors) {
          showFormErrors(modal, data.validation_errors);
        }

        initFormSubmits();
        handleFormSubmit(modal);
      },
      error: function(error) {
        console.log(error.responseJSON.errors);
        alert('Server error');
      },
      complete: function() {
        animateSpinner(null, false);
      }
    });
    e.preventDefault();
    return false;
  });
}

function initFormSubmits() {
  var modal = $('#protocol-preview-modal');
  modal.find('button[data-action=import_protocol]').off('click').on('click', function() {
    var form = modal.find('form');
    var hiddenField = form.find('#protocol_protocol_type');
    hiddenField.attr('value', $(this).data('import_type'));
    form.submit();
  });
}

applySearchCallback();
<|endoftext|>


<commit_before>'use strict';
angular.module('arethusa.core').directive('keyCapture', [
  'keyCapture',
  function (keyCapture) {
    return {
      restrict: 'A',
      link: function (scope, element, attrs) {
        element.on('keydown', function (event) {
          keyCapture.keydown(event);
        });
        element.on('keyup', function (event) {
          keyCapture.keyup(event);
        });
      }
    };
  }
]);
<commit_msg>Add example documentation (not yet working)<commit_after>'use strict';

/**
 * @ngdoc directive
 * @name keyCapture
 * @restrict A
 *
 * @description
 * Captures keydown and keyup events on the element and
 * delegates the event handling to the keyCapture service.
 *
 * @example
   <example module="arethusa.core">
     <file name="service.js">
        angular.module('arethusa.core').service('keyCapture', function() {
          this.keydown = function(event) {
            console.log(event);
          };
        });
     </file>
     <file name="index.html">
      <div keyCapture>
        Some awesome stuff
      </div>
     </file>
   </example>
 */
angular.module('arethusa.core').directive('keyCapture', [
  'keyCapture',
  function (keyCapture) {
    return {
      restrict: 'A',
      link: function (scope, element, attrs) {
        element.on('keydown', function (event) {
          keyCapture.keydown(event);
        });
        element.on('keyup', function (event) {
          keyCapture.keyup(event);
        });
      }
    };
  }
]);
<|endoftext|>


<commit_before>(function (angular, _) {
    'use strict';

    var module = angular.module('zooAPI');

    var upsert = function (arr, key, newVal) {
        var match = _.find(arr, key);
        if (match) {
            var index = _.indexOf(arr, match);
            arr.splice(index, 1, newVal);
        } else {
            arr.push(newVal);
        }
    };

    module.constant('zooAPIConfig', {
        display_name: 'oldweather',
        app_id: '0cee9a29027e78cc7f9df99a3d6b0d00aaf3bbfad014a4bb73bf29f30b46575f'
    });

    module.factory('zooAPI', function ($window) {
        return $window.zooAPI;
    });

    module.filter('removeCircularDeps', function () {
        return function (val) {
            var process = function (object) {
                return _.omit(object, function (value, key) { return key.charAt(0) === '_'; });
            };

            if (_.isArray(val)) {
                _.each(val, function (item, index) {
                    val[index] = process(val[index]);
                });
            } else {
                val = process(val);
            }

            return val;
        };
    });

    module.factory('zooAPIProject', function ($filter, $q, localStorageService, zooAPIConfig, zooAPI) {
        var get = function () {
            var deferred = $q.defer();

            var cache = localStorageService.get('project');
            if (cache) {
                deferred.resolve(cache);
            }

            zooAPI.type('projects').get({display_name: zooAPIConfig.display_name})
                .then(function (response) {
                    var data = $filter('removeCircularDeps')(response[0]);
                    localStorageService.set('project', data);
                    deferred.resolve(localStorageService.get('project'));
                });

            return deferred.promise;
        };

        return {
            get: get
        };
    });

    module.factory('zooAPIWorkflows', function ($q, $filter, localStorageService, zooAPIConfig, zooAPI) {
        var get = function (filter) {
            var deferred = $q.defer();

            var cache = localStorageService.get('workflows');
            if (cache) {
                if (filter) {
                    var cacheByID = _.find(cache, filter);
                    if (angular.isDefined(cacheByID)) {
                        deferred.notify([cacheByID]);
                    }
                } else {
                    deferred.notify(cache);
                }
            } else {
                cache = [];
            }

            zooAPI.type('workflows').get(filter)
                .then(function (response) {
                    upsert(cache, {id: response.id}, response);
                    cache = $filter('removeCircularDeps')(cache);
                    localStorageService.set('workflows', cache);
                    deferred.resolve(response);
                });

            return deferred.promise;
        };

        return {
            get: get
        };
    });

    module.factory('zooAPISubjectSets', function ($q, $filter, localStorageService, zooAPI, zooAPIProject) {
        var get = function (filter) {
            var deferred = $q.defer();

            var cache = localStorageService.get('subject_sets');
            if (cache) {
                if (filter) {
                    var cacheByID = _.find(cache, filter);
                    if (angular.isDefined(cacheByID)) {
                        deferred.notify([cacheByID]);
                    }
                } else {
                    deferred.notify(cache);
                }
            } else {
                cache = [];
            }

            zooAPIProject.get()
                .then(function (response) {
                    var options = {project_id: response.id};
                    // An array that will contain subject sets returned from out API call.
                    var subjectSets = [];

                    if (angular.isDefined(filter)) {
                        options = angular.extend(options, filter);
                    }

                    var loadPages = function (opts) {
                        zooAPI.type('subject_sets').get(opts)
                            .then(processResponse, deferred.reject);
                    };

                    var processResponse = function (sets) {
                        var meta = sets[0]._meta.subject_sets;

                        angular.forEach(sets, function (s) {
                            upsert(subjectSets, {'id': s.id}, s);
                        });

                        if (meta.next_page) {
                            deferred.notify(subjectSets);
                            loadPages(angular.extend({}, options, {page: meta.next_page}));
                        } else {
                            angular.forEach(subjectSets, function (s) {
                                upsert(cache, {'id': s.id}, s);
                            });

                            // If an item in the cache, is not in the list returned by the server
                            // and we're not filtering (assuming we're loading all data here!)
                            if (!filter) {
                                angular.forEach(cache, function (c) {
                                    if (angular.isUndefined(_.find(subjectSets, {id: c.id}))) {
                                        _.remove(cache, c);
                                    }
                                });
                            }

                            cache = $filter('removeCircularDeps')(cache);
                            localStorageService.set('subject_sets', cache);

                            deferred.resolve(subjectSets);
                        }
                    };

                    loadPages(options);
                });

            return deferred.promise;
        };

        return {
            get: get
        };
    });

}(window.angular, window._));
<commit_msg>Switch to production project<commit_after>(function (angular, _) {
    'use strict';

    var module = angular.module('zooAPI');

    var upsert = function (arr, key, newVal) {
        var match = _.find(arr, key);
        if (match) {
            var index = _.indexOf(arr, match);
            arr.splice(index, 1, newVal);
        } else {
            arr.push(newVal);
        }
    };

    module.constant('zooAPIConfig', {
        display_name: 'oldweather',
        app_id: '2b10a14e8f11eefb130a275f01898c8406600834bff1063bb1b7938795acc8a3'
    });

    module.factory('zooAPI', function ($window, zooAPIConfig) {
        $window.zooAPI.root = 'https://panoptes.zooniverse.org/api';
        $window.zooAPI.appID = zooAPIConfig.app_id;
        return $window.zooAPI;
    });

    module.filter('removeCircularDeps', function () {
        return function (val) {
            var process = function (object) {
                return _.omit(object, function (value, key) { return key.charAt(0) === '_'; });
            };

            if (_.isArray(val)) {
                _.each(val, function (item, index) {
                    val[index] = process(val[index]);
                });
            } else {
                val = process(val);
            }

            return val;
        };
    });

    module.factory('zooAPIProject', function ($filter, $q, localStorageService, zooAPIConfig, zooAPI) {
        var get = function () {
            var deferred = $q.defer();

            var cache = localStorageService.get('project');
            if (cache) {
                deferred.resolve(cache);
            }

            zooAPI.type('projects').get({display_name: zooAPIConfig.display_name})
                .then(function (response) {
                    var data = $filter('removeCircularDeps')(response[0]);
                    localStorageService.set('project', data);
                    deferred.resolve(localStorageService.get('project'));
                });

            return deferred.promise;
        };

        return {
            get: get
        };
    });

    module.factory('zooAPIWorkflows', function ($q, $filter, localStorageService, zooAPIConfig, zooAPI) {
        var get = function (filter) {
            var deferred = $q.defer();

            var cache = localStorageService.get('workflows');
            if (cache) {
                if (filter) {
                    var cacheByID = _.find(cache, filter);
                    if (angular.isDefined(cacheByID)) {
                        deferred.notify([cacheByID]);
                    }
                } else {
                    deferred.notify(cache);
                }
            } else {
                cache = [];
            }

            zooAPI.type('workflows').get(filter)
                .then(function (response) {
                    upsert(cache, {id: response.id}, response);
                    cache = $filter('removeCircularDeps')(cache);
                    localStorageService.set('workflows', cache);
                    deferred.resolve(response);
                });

            return deferred.promise;
        };

        return {
            get: get
        };
    });

    module.factory('zooAPISubjectSets', function ($q, $filter, localStorageService, zooAPI, zooAPIProject) {
        var get = function (filter) {
            var deferred = $q.defer();

            var cache = localStorageService.get('subject_sets');
            if (cache) {
                if (filter) {
                    var cacheByID = _.find(cache, filter);
                    if (angular.isDefined(cacheByID)) {
                        deferred.notify([cacheByID]);
                    }
                } else {
                    deferred.notify(cache);
                }
            } else {
                cache = [];
            }

            zooAPIProject.get()
                .then(function (response) {
                    var options = {project_id: response.id};
                    // An array that will contain subject sets returned from out API call.
                    var subjectSets = [];

                    if (angular.isDefined(filter)) {
                        options = angular.extend(options, filter);
                    }

                    var loadPages = function (opts) {
                        zooAPI.type('subject_sets').get(opts)
                            .then(processResponse, deferred.reject);
                    };

                    var processResponse = function (sets) {
                        var meta = sets[0]._meta.subject_sets;

                        angular.forEach(sets, function (s) {
                            upsert(subjectSets, {'id': s.id}, s);
                        });

                        if (meta.next_page) {
                            deferred.notify(subjectSets);
                            loadPages(angular.extend({}, options, {page: meta.next_page}));
                        } else {
                            angular.forEach(subjectSets, function (s) {
                                upsert(cache, {'id': s.id}, s);
                            });

                            // If an item in the cache, is not in the list returned by the server
                            // and we're not filtering (assuming we're loading all data here!)
                            if (!filter) {
                                angular.forEach(cache, function (c) {
                                    if (angular.isUndefined(_.find(subjectSets, {id: c.id}))) {
                                        _.remove(cache, c);
                                    }
                                });
                            }

                            cache = $filter('removeCircularDeps')(cache);
                            localStorageService.set('subject_sets', cache);

                            deferred.resolve(subjectSets);
                        }
                    };

                    loadPages(options);
                });

            return deferred.promise;
        };

        return {
            get: get
        };
    });

}(window.angular, window._));
<|endoftext|>