<commit_before>var m = require('mithril');
var Raven = require('raven-js');
var $osf = require('js/osfHelpers');
var ShareJSDoc = require('js/pages/ShareJSDocFile.js');
require('ace-noconflict');
require('ace-mode-markdown');
require('ace-ext-language_tools');
require('addons/wiki/static/ace-markdown-snippets.js');
var Markdown = require('pagedown-ace-converter');
Markdown.getSanitizingConverter = require('pagedown-ace-sanitizer').getSanitizingConverter;
require('imports?Markdown=pagedown-ace-converter!pagedown-ace-editor');

var util = require('./util.js');

var model = {};

var FileEditor = {
    controller: function(contentUrl, shareWSUrl, editorMeta, observables) {
        var self = {};

        self.url = contentUrl;
        self.loaded = false;
        self.initialText = '';
        self.editorMeta = editorMeta;

        self.observables = observables;

        self.unthrottledStatus = self.observables.status;
        $osf.throttle(self.observables.status, 4000, {leading: false});

        self.bindAce = function(element, isInitialized, context) {
            if (isInitialized) return;
            model.editor = ace.edit(element.id);
            model.editor.setValue(self.initialText, -1);
            new ShareJSDoc(shareWSUrl, self.editorMeta, model.editor, self.observables);
        };

        self.reloadFile = function() {
            self.loaded = false;
            $.ajax({
                type: 'GET',
                url: self.url,
                beforeSend: $osf.setXHRAuthorization,
            }).done(function (parsed, status, response) {
                m.startComputation();
                self.loaded = true;
                self.initialText = response.responseText;
                if (model.editor) {
                    model.editor.setValue(self.initialText);
                }
                m.endComputation();
            }).fail(function (xhr, textStatus, error) {
                $osf.growl('Error','The file content could not be loaded.');
                Raven.captureMessage('Could not GET file contents.', {
                    url: self.url,
                    textStatus: textStatus,
                    error: error
                });
            });
        };

        self.saveFile = $osf.throttle(function() {
            if(self.changed()) {
                var oldstatus = self.observables.status();
                model.editor.setReadOnly(true);
                self.unthrottledStatus('saving');
                m.redraw();
                var request = $.ajax({
                    type: 'PUT',
                    url: self.url,
                    data: model.editor.getValue(),
                    beforeSend: $osf.setXHRAuthorization
                }).done(function () {
                    model.editor.setReadOnly(false);
                    self.unthrottledStatus(oldstatus);
                    $(document).trigger('fileviewpage:reload');
                    self.initialText = model.editor.getValue();
                    m.redraw();
                }).fail(function(error) {
                    model.editor.setReadOnly(false);
                    self.unthrottledStatus(oldstatus);
                    $(document).trigger('fileviewpage:reload');
                    model.editor.setValue(self.initialText);
                    $osf.growl('Error', 'The file could not be updated.');
                    Raven.captureMessage('Could not PUT file content.', {
                        error: error,
                        url: self.url
                    });
                    m.redraw();
                });
            } else {
                alert('There are no changes to save.');
            }
        }, 500);

        self.changed = function() {
            var file1 = self.initialText;
            var file2 = !model.editor ? '' : model.editor.getValue();

            var clean1 = typeof file1 === 'string' ?
                file1.replace(/(\r\n|\n|\r)/gm, '\n') : '';
            var clean2 = typeof file2 === 'string' ?
                file2.replace(/(\r\n|\n|\r)/gm, '\n') : '';

            return clean1 !== clean2;
        };

        self.reloadFile();

        return self;
    },
    view: function(ctrl) {
        if (!ctrl.loaded) return util.Spinner;

        return m('.editor-pane', [
            m('.wiki-connected-users', m('.row', m('.col-sm-12', [
                m('.ul.list-inline', {style: {'margin-top': '10px'}}, [
                    ctrl.observables.activeUsers().map(function(user) {
                        return m('li', m('a', {href: user.url}, [
                            m('img', {
                                title: user.name,
                                src: user.gravatar,
                                'data-container': 'body',
                                'data-placement': 'top',
                                'data-toggle': 'tooltip',
                                style: {border: '1px solid black'}
                            })
                        ]));
                    })

                ])
            ]))),
            m('', {style: {'padding-top': '10px'}}, [
                m('.wmd-input.wiki-editor#editor', {config: ctrl.bindAce})
            ]),
            m('br'),
            m('.osf-panel-footer[style=position:inherit]', [
                m('.row', m('.col-sm-12', [
                    m('.pull-right', [
                        m('button#fileEditorRevert.btn.btn-sm.btn-danger', {onclick: function(){ctrl.reloadFile();}}, 'Revert'),
                        ' ',
                        m('button#fileEditorSave.btn.btn-sm.btn-success', {onclick: function() {ctrl.saveFile();}}, 'Save')
                    ])
                ]))
            ])
        ]);

    }
};

module.exports = FileEditor;
<commit_msg>Force jQuery to not parse returned data<commit_after>var m = require('mithril');
var Raven = require('raven-js');
var $osf = require('js/osfHelpers');
var ShareJSDoc = require('js/pages/ShareJSDocFile.js');
require('ace-noconflict');
require('ace-mode-markdown');
require('ace-ext-language_tools');
require('addons/wiki/static/ace-markdown-snippets.js');
var Markdown = require('pagedown-ace-converter');
Markdown.getSanitizingConverter = require('pagedown-ace-sanitizer').getSanitizingConverter;
require('imports?Markdown=pagedown-ace-converter!pagedown-ace-editor');

var util = require('./util.js');

var model = {};

var FileEditor = {
    controller: function(contentUrl, shareWSUrl, editorMeta, observables) {
        var self = {};

        self.url = contentUrl;
        self.loaded = false;
        self.initialText = '';
        self.editorMeta = editorMeta;

        self.observables = observables;

        self.unthrottledStatus = self.observables.status;
        $osf.throttle(self.observables.status, 4000, {leading: false});

        self.bindAce = function(element, isInitialized, context) {
            if (isInitialized) return;
            model.editor = ace.edit(element.id);
            model.editor.setValue(self.initialText, -1);
            new ShareJSDoc(shareWSUrl, self.editorMeta, model.editor, self.observables);
        };

        self.reloadFile = function() {
            self.loaded = false;
            $.ajax({
                type: 'GET',
                url: self.url,
                dataType: 'text',
                beforeSend: $osf.setXHRAuthorization,
            }).done(function (parsed, status, response) {
                m.startComputation();
                self.loaded = true;
                self.initialText = response.responseText;
                if (model.editor) {
                    model.editor.setValue(self.initialText);
                }
                m.endComputation();
            }).fail(function (xhr, textStatus, error) {
                $osf.growl('Error','The file content could not be loaded.');
                Raven.captureMessage('Could not GET file contents.', {
                    url: self.url,
                    textStatus: textStatus,
                    error: error
                });
            });
        };

        self.saveFile = $osf.throttle(function() {
            if(self.changed()) {
                var oldstatus = self.observables.status();
                model.editor.setReadOnly(true);
                self.unthrottledStatus('saving');
                m.redraw();
                var request = $.ajax({
                    type: 'PUT',
                    url: self.url,
                    data: model.editor.getValue(),
                    beforeSend: $osf.setXHRAuthorization
                }).done(function () {
                    model.editor.setReadOnly(false);
                    self.unthrottledStatus(oldstatus);
                    $(document).trigger('fileviewpage:reload');
                    self.initialText = model.editor.getValue();
                    m.redraw();
                }).fail(function(error) {
                    model.editor.setReadOnly(false);
                    self.unthrottledStatus(oldstatus);
                    $(document).trigger('fileviewpage:reload');
                    model.editor.setValue(self.initialText);
                    $osf.growl('Error', 'The file could not be updated.');
                    Raven.captureMessage('Could not PUT file content.', {
                        error: error,
                        url: self.url
                    });
                    m.redraw();
                });
            } else {
                alert('There are no changes to save.');
            }
        }, 500);

        self.changed = function() {
            var file1 = self.initialText;
            var file2 = !model.editor ? '' : model.editor.getValue();

            var clean1 = typeof file1 === 'string' ?
                file1.replace(/(\r\n|\n|\r)/gm, '\n') : '';
            var clean2 = typeof file2 === 'string' ?
                file2.replace(/(\r\n|\n|\r)/gm, '\n') : '';

            return clean1 !== clean2;
        };

        self.reloadFile();

        return self;
    },
    view: function(ctrl) {
        if (!ctrl.loaded) return util.Spinner;

        return m('.editor-pane', [
            m('.wiki-connected-users', m('.row', m('.col-sm-12', [
                m('.ul.list-inline', {style: {'margin-top': '10px'}}, [
                    ctrl.observables.activeUsers().map(function(user) {
                        return m('li', m('a', {href: user.url}, [
                            m('img', {
                                title: user.name,
                                src: user.gravatar,
                                'data-container': 'body',
                                'data-placement': 'top',
                                'data-toggle': 'tooltip',
                                style: {border: '1px solid black'}
                            })
                        ]));
                    })

                ])
            ]))),
            m('', {style: {'padding-top': '10px'}}, [
                m('.wmd-input.wiki-editor#editor', {config: ctrl.bindAce})
            ]),
            m('br'),
            m('.osf-panel-footer[style=position:inherit]', [
                m('.row', m('.col-sm-12', [
                    m('.pull-right', [
                        m('button#fileEditorRevert.btn.btn-sm.btn-danger', {onclick: function(){ctrl.reloadFile();}}, 'Revert'),
                        ' ',
                        m('button#fileEditorSave.btn.btn-sm.btn-success', {onclick: function() {ctrl.saveFile();}}, 'Save')
                    ])
                ]))
            ])
        ]);

    }
};

module.exports = FileEditor;
<|endoftext|>


<commit_before>/*
 * Copyright 2017 StreamSets Inc.
 *
 * 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.
 */
/**
 * Controller for Import Modal Dialog.
 */

angular
  .module('dataCollectorApp.home')
  .controller('ImportModalInstanceController', function ($scope, $modalInstance, api, pipelineInfo, $translate, tracking) {
    var errorMsg = 'Not a valid Pipeline Configuration file.';

    angular.extend($scope, {
      common: {
        errors: []
      },
      showLoading: true,
      uploadFile: {},
      createNewPipeline: (!pipelineInfo),
      pipelineInfo: pipelineInfo,
      newConfig : {
        title: '',
        description: ''
      },

      /**
       * Import button callback function.
       */
      import: function () {
        tracking.mixpanel.track('Import Pipeline Started', {});
        var reader = new FileReader();

        if ($scope.createNewPipeline && !$scope.newConfig.title) {
          $translate('home.library.nameRequiredValidation').then(function(translation) {
            $scope.common.errors = [translation];
          });

          return;
        }

        reader.onload = function (loadEvent) {
          try {
            var parsedObj = JSON.parse(loadEvent.target.result);
            var jsonConfigObj;
            var jsonRulesObj;

            if (parsedObj.pipelineConfig) {
              //It is an config and rules envelope
              jsonConfigObj = parsedObj.pipelineConfig;
              jsonRulesObj = parsedObj.pipelineRules;
            } else {
              jsonConfigObj = parsedObj;
            }

            if (!jsonConfigObj.version) {
              jsonConfigObj.version = 1;
            }

            if (jsonConfigObj.uuid) {
              if (pipelineInfo && !$scope.createNewPipeline) { // If pipeline config already exists
                jsonConfigObj.pipelineId = pipelineInfo.pipelineId;
                jsonConfigObj.uuid = pipelineInfo.uuid;
                jsonConfigObj.metadata = pipelineInfo.metadata;
                jsonConfigObj.title = pipelineInfo.title;

                api.pipelineAgent.savePipelineConfig(pipelineInfo.pipelineId, jsonConfigObj).
                then(function(res) {
                  if (jsonRulesObj && jsonRulesObj.uuid) {
                    api.pipelineAgent.getPipelineRules(pipelineInfo.pipelineId).
                    then(function(res) {
                      var rulesObj = res.data;
                      rulesObj.schemaVersion = jsonRulesObj.schemaVersion;
                      rulesObj.version = jsonRulesObj.version;
                      rulesObj.metricsRuleDefinitions = jsonRulesObj.metricsRuleDefinitions;
                      rulesObj.dataRuleDefinitions = jsonRulesObj.dataRuleDefinitions;
                      rulesObj.driftRuleDefinitions = jsonRulesObj.driftRuleDefinitions;
                      rulesObj.emailIds = jsonRulesObj.emailIds;
                      rulesObj.configuration = jsonRulesObj.configuration;

                      api.pipelineAgent.savePipelineRules(pipelineInfo.pipelineId, rulesObj).
                      then(function() {
                        tracking.mixpanel.track('Import Pipeline Update Completed', {});
                        tracking.mixpanel.people.set({'Core Journey Stage - Pipeline Imported': true});
                        $modalInstance.close();
                      });

                    });

                  } else {
                    $modalInstance.close();
                  }
                },function(res) {
                  $scope.common.errors = [res.data];
                  tracking.mixpanel.track('Import Pipeline Failed', {'Failure Reason': JSON.stringify(res.data)});
                });
              } else { // If no pipeline exist or create pipeline option selected
                var newPipelineObject,
                  label,
                  name,
                  description;

                if ($scope.createNewPipeline) {
                  label = $scope.newConfig.title;
                  description = $scope.newConfig.description;
                } else {
                  label = jsonConfigObj.info.label || jsonConfigObj.info.pipelineId;
                  description = jsonConfigObj.info.description;
                }

                api.pipelineAgent.createNewPipelineConfig(label, description)
                  .then(function(res) {
                    newPipelineObject = res.data;
                    name = newPipelineObject.info.pipelineId;
                    newPipelineObject.configuration = jsonConfigObj.configuration;
                    newPipelineObject.errorStage = jsonConfigObj.errorStage;
                    newPipelineObject.statsAggregatorStage = jsonConfigObj.statsAggregatorStage;
                    newPipelineObject.uiInfo = jsonConfigObj.uiInfo;
                    newPipelineObject.stages = jsonConfigObj.stages;
                    newPipelineObject.version = jsonConfigObj.version;
                    newPipelineObject.schemaVersion = jsonConfigObj.schemaVersion;
                    newPipelineObject.metadata = jsonConfigObj.metadata;
                    newPipelineObject.startEventStages = jsonConfigObj.startEventStages;
                    newPipelineObject.stopEventStages = jsonConfigObj.stopEventStages;
                    newPipelineObject.testOriginStage = jsonConfigObj.testOriginStage;
                    newPipelineObject.fragments = jsonConfigObj.fragments;
                    return api.pipelineAgent.savePipelineConfig(name, newPipelineObject);
                  })
                  .then(function(res) {
                    if (jsonRulesObj && jsonRulesObj.uuid) {
                      api.pipelineAgent.getPipelineRules(name).
                      then(function(res) {
                        var rulesObj = res.data;
                        rulesObj.schemaVersion = jsonRulesObj.schemaVersion;
                        rulesObj.version = jsonRulesObj.version;
                        rulesObj.metricsRuleDefinitions = jsonRulesObj.metricsRuleDefinitions;
                        rulesObj.dataRuleDefinitions = jsonRulesObj.dataRuleDefinitions;
                        rulesObj.driftRuleDefinitions = jsonRulesObj.driftRuleDefinitions;
                        rulesObj.emailIds = jsonRulesObj.emailIds;
                        rulesObj.configuration = jsonRulesObj.configuration;

                        api.pipelineAgent.savePipelineRules(name, rulesObj).
                        then(function() {
                          $modalInstance.close(newPipelineObject);
                          tracking.mixpanel.track('Import Pipeline Completed', {'Pipeline ID': newPipelineObject.pipelineId});
                          tracking.mixpanel.people.set({'Core Journey Stage - Pipeline Imported': true});
                          tracking.FS.event('Import Pipeline Completed', {'Pipeline ID': newPipelineObject.pipelineId});
                        });

                      });

                    } else {
                      $modalInstance.close(newPipelineObject);
                      tracking.mixpanel.track('Import Pipeline Completed', {'Pipeline ID': newPipelineObject.pipelineId});
                      tracking.mixpanel.people.set({'Core Journey Stage - Pipeline Imported': true});
                      tracking.FS.event('Import Pipeline Completed', {'Pipeline ID': newPipelineObject.pipelineId});
                    }
                  },function(res) {
                    $scope.common.errors = [res.data];
                    tracking.mixpanel.track('Import Pipeline Failed', {'Failure Reason': JSON.stringify(res.data)});
                    //Failed to import pipeline. If new pipeline is created during import revert it back.
                    if (res.data && res.data.RemoteException &&
                      res.data.RemoteException.errorCode === 'CONTAINER_0201') {
                      // For CONTAINER_0201 - Pipeline 'Sample123' already exists - don't delete existing one
                      return;
                    }

                    api.pipelineAgent.deletePipelineConfig(name);
                  });
              }

            } else {
              $scope.$apply(function() {
                $scope.common.errors = [errorMsg];
              });
              tracking.mixpanel.track('Import Pipeline Failed', {'Failure Reason': 'Missing Pipeline uuid'});

            }
          } catch(e) {
            $scope.$apply(function() {
              $scope.common.errors = [errorMsg];
            });
            tracking.mixpanel.track('Import Pipeline Failed', {'Failure Reason': e.toString()});
          }
        };
        reader.readAsText($scope.uploadFile);
      },

      /**
       * Cancel button callback.
       */
      cancel: function () {
        $modalInstance.dismiss('cancel');
      }
    });

    $scope.$watch('uploadFile', function (newValue) {
      if (newValue && newValue.name && !$scope.newConfig.title) {
        $scope.newConfig.title = newValue.name.replace('.json', '');
      }
    });

  });
<commit_msg>SDC-14802: Downgrade: UI is discarding sdcVersion on pipeline import<commit_after>/*
 * Copyright 2017 StreamSets Inc.
 *
 * 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.
 */
/**
 * Controller for Import Modal Dialog.
 */

angular
  .module('dataCollectorApp.home')
  .controller('ImportModalInstanceController', function ($scope, $modalInstance, api, pipelineInfo, $translate, tracking) {
    var errorMsg = 'Not a valid Pipeline Configuration file.';

    angular.extend($scope, {
      common: {
        errors: []
      },
      showLoading: true,
      uploadFile: {},
      createNewPipeline: (!pipelineInfo),
      pipelineInfo: pipelineInfo,
      newConfig : {
        title: '',
        description: ''
      },

      /**
       * Import button callback function.
       */
      import: function () {
        tracking.mixpanel.track('Import Pipeline Started', {});
        var reader = new FileReader();

        if ($scope.createNewPipeline && !$scope.newConfig.title) {
          $translate('home.library.nameRequiredValidation').then(function(translation) {
            $scope.common.errors = [translation];
          });

          return;
        }

        reader.onload = function (loadEvent) {
          try {
            var parsedObj = JSON.parse(loadEvent.target.result);
            var jsonConfigObj;
            var jsonRulesObj;

            if (parsedObj.pipelineConfig) {
              //It is an config and rules envelope
              jsonConfigObj = parsedObj.pipelineConfig;
              jsonRulesObj = parsedObj.pipelineRules;
            } else {
              jsonConfigObj = parsedObj;
            }

            if (!jsonConfigObj.version) {
              jsonConfigObj.version = 1;
            }

            if (jsonConfigObj.uuid) {
              if (pipelineInfo && !$scope.createNewPipeline) { // If pipeline config already exists
                jsonConfigObj.pipelineId = pipelineInfo.pipelineId;
                jsonConfigObj.uuid = pipelineInfo.uuid;
                jsonConfigObj.metadata = pipelineInfo.metadata;
                jsonConfigObj.title = pipelineInfo.title;

                api.pipelineAgent.savePipelineConfig(pipelineInfo.pipelineId, jsonConfigObj).
                then(function(res) {
                  if (jsonRulesObj && jsonRulesObj.uuid) {
                    api.pipelineAgent.getPipelineRules(pipelineInfo.pipelineId).
                    then(function(res) {
                      var rulesObj = res.data;
                      rulesObj.schemaVersion = jsonRulesObj.schemaVersion;
                      rulesObj.version = jsonRulesObj.version;
                      rulesObj.metricsRuleDefinitions = jsonRulesObj.metricsRuleDefinitions;
                      rulesObj.dataRuleDefinitions = jsonRulesObj.dataRuleDefinitions;
                      rulesObj.driftRuleDefinitions = jsonRulesObj.driftRuleDefinitions;
                      rulesObj.emailIds = jsonRulesObj.emailIds;
                      rulesObj.configuration = jsonRulesObj.configuration;

                      api.pipelineAgent.savePipelineRules(pipelineInfo.pipelineId, rulesObj).
                      then(function() {
                        tracking.mixpanel.track('Import Pipeline Update Completed', {});
                        tracking.mixpanel.people.set({'Core Journey Stage - Pipeline Imported': true});
                        $modalInstance.close();
                      });

                    });

                  } else {
                    $modalInstance.close();
                  }
                },function(res) {
                  $scope.common.errors = [res.data];
                  tracking.mixpanel.track('Import Pipeline Failed', {'Failure Reason': JSON.stringify(res.data)});
                });
              } else { // If no pipeline exist or create pipeline option selected
                var newPipelineObject,
                  label,
                  name,
                  description;

                if ($scope.createNewPipeline) {
                  label = $scope.newConfig.title;
                  description = $scope.newConfig.description;
                } else {
                  label = jsonConfigObj.info.label || jsonConfigObj.info.pipelineId;
                  description = jsonConfigObj.info.description;
                }

                api.pipelineAgent.createNewPipelineConfig(label, description)
                  .then(function(res) {
                    newPipelineObject = res.data;
                    name = newPipelineObject.info.pipelineId;
                    newPipelineObject.configuration = jsonConfigObj.configuration;
                    newPipelineObject.errorStage = jsonConfigObj.errorStage;
                    newPipelineObject.statsAggregatorStage = jsonConfigObj.statsAggregatorStage;
                    newPipelineObject.uiInfo = jsonConfigObj.uiInfo;
                    newPipelineObject.stages = jsonConfigObj.stages;
                    newPipelineObject.version = jsonConfigObj.version;
                    newPipelineObject.schemaVersion = jsonConfigObj.schemaVersion;
                    newPipelineObject.metadata = jsonConfigObj.metadata;
                    newPipelineObject.startEventStages = jsonConfigObj.startEventStages;
                    newPipelineObject.stopEventStages = jsonConfigObj.stopEventStages;
                    newPipelineObject.testOriginStage = jsonConfigObj.testOriginStage;
                    newPipelineObject.fragments = jsonConfigObj.fragments;
                    newPipelineObject.info.sdcVersion = jsonConfigObj.info.sdcVersion;
                    return api.pipelineAgent.savePipelineConfig(name, newPipelineObject);
                  })
                  .then(function(res) {
                    if (jsonRulesObj && jsonRulesObj.uuid) {
                      api.pipelineAgent.getPipelineRules(name).
                      then(function(res) {
                        var rulesObj = res.data;
                        rulesObj.schemaVersion = jsonRulesObj.schemaVersion;
                        rulesObj.version = jsonRulesObj.version;
                        rulesObj.metricsRuleDefinitions = jsonRulesObj.metricsRuleDefinitions;
                        rulesObj.dataRuleDefinitions = jsonRulesObj.dataRuleDefinitions;
                        rulesObj.driftRuleDefinitions = jsonRulesObj.driftRuleDefinitions;
                        rulesObj.emailIds = jsonRulesObj.emailIds;
                        rulesObj.configuration = jsonRulesObj.configuration;

                        api.pipelineAgent.savePipelineRules(name, rulesObj).
                        then(function() {
                          $modalInstance.close(newPipelineObject);
                          tracking.mixpanel.track('Import Pipeline Completed', {'Pipeline ID': newPipelineObject.pipelineId});
                          tracking.mixpanel.people.set({'Core Journey Stage - Pipeline Imported': true});
                          tracking.FS.event('Import Pipeline Completed', {'Pipeline ID': newPipelineObject.pipelineId});
                        });

                      });

                    } else {
                      $modalInstance.close(newPipelineObject);
                      tracking.mixpanel.track('Import Pipeline Completed', {'Pipeline ID': newPipelineObject.pipelineId});
                      tracking.mixpanel.people.set({'Core Journey Stage - Pipeline Imported': true});
                      tracking.FS.event('Import Pipeline Completed', {'Pipeline ID': newPipelineObject.pipelineId});
                    }
                  },function(res) {
                    $scope.common.errors = [res.data];
                    tracking.mixpanel.track('Import Pipeline Failed', {'Failure Reason': JSON.stringify(res.data)});
                    //Failed to import pipeline. If new pipeline is created during import revert it back.
                    if (res.data && res.data.RemoteException &&
                      res.data.RemoteException.errorCode === 'CONTAINER_0201') {
                      // For CONTAINER_0201 - Pipeline 'Sample123' already exists - don't delete existing one
                      return;
                    }

                    api.pipelineAgent.deletePipelineConfig(name);
                  });
              }

            } else {
              $scope.$apply(function() {
                $scope.common.errors = [errorMsg];
              });
              tracking.mixpanel.track('Import Pipeline Failed', {'Failure Reason': 'Missing Pipeline uuid'});

            }
          } catch(e) {
            $scope.$apply(function() {
              $scope.common.errors = [errorMsg];
            });
            tracking.mixpanel.track('Import Pipeline Failed', {'Failure Reason': e.toString()});
          }
        };
        reader.readAsText($scope.uploadFile);
      },

      /**
       * Cancel button callback.
       */
      cancel: function () {
        $modalInstance.dismiss('cancel');
      }
    });

    $scope.$watch('uploadFile', function (newValue) {
      if (newValue && newValue.name && !$scope.newConfig.title) {
        $scope.newConfig.title = newValue.name.replace('.json', '');
      }
    });

  });
<|endoftext|>


<commit_before>var $ = require('jquery');
var m = require('mithril');
var $osf = require('js/osfHelpers');
var FileViewPage = require('js/filepage');
var Raven = require('raven-js');

require('jquery-tagsinput');

m.mount(document.getElementsByClassName('file-view-panels')[0], FileViewPage(window.contextVars));

var tagUrl = '/api/v1/project/' + window.contextVars.node.id + '/osfstorage' + window.contextVars.file.path + '/tags/';

$(function() {
    // Tag input
    $('#fileTags').tagsInput({
        width: '100%',
        interactive: window.contextVars.currentUser.canEdit,
        maxChars: 128,
        defaultText: 'Add a tag to enhance discoverability',
        onAddTag: function (tag) {
            var url = tagUrl;
            var request = $osf.postJSON(url, {'tag': tag });
            request.fail(function (xhr, textStatus, error) {
                $osf.growl('Error', 'Could not add tag.');
                Raven.captureMessage('Failed to add tag', {
                    extra: { tag: tag, url: url, textStatus: textStatus, error: error }
                });
            });
        },
        onRemoveTag: function (tag) {
            // Don't try to delete a blank tag (would result in a server error)
            if (!tag) {
                return false;
            }
            var request = $osf.ajaxJSON('DELETE', tagUrl, {'data': {'tag': tag}});
            request.fail(function (xhr, textStatus, error) {
                // Suppress "tag not found" errors, as the end result is what the user wanted (tag is gone)- eg could be because two people were working at same time
                if (xhr.status !== 409) {
                    $osf.growl('Error', 'Could not remove tag.');
                    Raven.captureMessage('Failed to remove tag', {
                        extra: {tag: tag, url: tagUrl, textStatus: textStatus, error: error}
                    });
                }
            });
        }
    });

    // allows inital default message to fit on empty tag
    if(!$('.tag').length){
        $('#fileTags_tag').css('width', '250px');
    }

    $('#fileTags_tag').attr('maxlength', '128');
    if (!window.contextVars.currentUser.canEdit || window.contextVars.node.isRegistration) {
        $('a[title="Removing tag"]').remove();
        $('span.tag span').each(function(idx, elm) {
            $(elm).text($(elm).text().replace(/\s*$/, ''));
        });
    }

    var titleEditable = function () {
        var readOnlyProviders = ['bitbucket', 'figshare', 'dataverse', 'gitlab', 'onedrive'];
        var ctx = window.contextVars;
        if (readOnlyProviders.indexOf(ctx.file.provider) >= 0 || ctx.file.checkoutUser || !ctx.currentUser.canEdit || ctx.node.isRegistration)
            return false;
        else
            return true;
    };

    if(titleEditable()) {
        $('#fileTitleEditable').editable({
            type: 'text',
            mode: 'inline',
            send: 'always',
            url: window.contextVars.file.urls.delete,
            ajaxOptions: {
                type: 'post',
                contentType: 'application/json',
                dataType: 'json',
                beforeSend: $osf.setXHRAuthorization,
                crossOrigin: true,
            },
            validate: function(value) {
                if($.trim(value) === ''){
                    return 'The file title cannot be empty.';
                } else if(value.length > 100){
                    return 'The file title cannot be more than 100 characters.';
                }
            },
            params: function(params) {
                var payload = {
                    action: 'rename',
                    rename: params.value,
                };
                return JSON.stringify(payload);
            },
            success: function(response) {
                $osf.growl('Success', 'Your file was successfully renamed. To view the new filename in the file tree below, refresh the page.', 'success');
            },
            error: function (response) {
                var msg = response.responseJSON.message;
                if (msg) {
                    // This is done to override inherited css style and prevent error message lines from overlapping with each other
                    $('.editable-error-block').css('line-height', '35px');
                    return msg;
                }
            }
        });
    }
});
<commit_msg>Update tagUrl<commit_after>var $ = require('jquery');
var m = require('mithril');
var $osf = require('js/osfHelpers');
var FileViewPage = require('js/filepage');
var Raven = require('raven-js');

require('jquery-tagsinput');

m.mount(document.getElementsByClassName('file-view-panels')[0], FileViewPage(window.contextVars));

var tagUrl = '/api/v1/' + window.contextVars.node.id + '/osfstorage' + window.contextVars.file.path + '/tags/';

$(function() {
    // Tag input
    $('#fileTags').tagsInput({
        width: '100%',
        interactive: window.contextVars.currentUser.canEdit,
        maxChars: 128,
        defaultText: 'Add a tag to enhance discoverability',
        onAddTag: function (tag) {
            var url = tagUrl;
            var request = $osf.postJSON(url, {'tag': tag });
            request.fail(function (xhr, textStatus, error) {
                $osf.growl('Error', 'Could not add tag.');
                Raven.captureMessage('Failed to add tag', {
                    extra: { tag: tag, url: url, textStatus: textStatus, error: error }
                });
            });
        },
        onRemoveTag: function (tag) {
            // Don't try to delete a blank tag (would result in a server error)
            if (!tag) {
                return false;
            }
            var request = $osf.ajaxJSON('DELETE', tagUrl, {'data': {'tag': tag}});
            request.fail(function (xhr, textStatus, error) {
                // Suppress "tag not found" errors, as the end result is what the user wanted (tag is gone)- eg could be because two people were working at same time
                if (xhr.status !== 409) {
                    $osf.growl('Error', 'Could not remove tag.');
                    Raven.captureMessage('Failed to remove tag', {
                        extra: {tag: tag, url: tagUrl, textStatus: textStatus, error: error}
                    });
                }
            });
        }
    });

    // allows inital default message to fit on empty tag
    if(!$('.tag').length){
        $('#fileTags_tag').css('width', '250px');
    }

    $('#fileTags_tag').attr('maxlength', '128');
    if (!window.contextVars.currentUser.canEdit || window.contextVars.node.isRegistration) {
        $('a[title="Removing tag"]').remove();
        $('span.tag span').each(function(idx, elm) {
            $(elm).text($(elm).text().replace(/\s*$/, ''));
        });
    }

    var titleEditable = function () {
        var readOnlyProviders = ['bitbucket', 'figshare', 'dataverse', 'gitlab', 'onedrive'];
        var ctx = window.contextVars;
        if (readOnlyProviders.indexOf(ctx.file.provider) >= 0 || ctx.file.checkoutUser || !ctx.currentUser.canEdit || ctx.node.isRegistration)
            return false;
        else
            return true;
    };

    if(titleEditable()) {
        $('#fileTitleEditable').editable({
            type: 'text',
            mode: 'inline',
            send: 'always',
            url: window.contextVars.file.urls.delete,
            ajaxOptions: {
                type: 'post',
                contentType: 'application/json',
                dataType: 'json',
                beforeSend: $osf.setXHRAuthorization,
                crossOrigin: true,
            },
            validate: function(value) {
                if($.trim(value) === ''){
                    return 'The file title cannot be empty.';
                } else if(value.length > 100){
                    return 'The file title cannot be more than 100 characters.';
                }
            },
            params: function(params) {
                var payload = {
                    action: 'rename',
                    rename: params.value,
                };
                return JSON.stringify(payload);
            },
            success: function(response) {
                $osf.growl('Success', 'Your file was successfully renamed. To view the new filename in the file tree below, refresh the page.', 'success');
            },
            error: function (response) {
                var msg = response.responseJSON.message;
                if (msg) {
                    // This is done to override inherited css style and prevent error message lines from overlapping with each other
                    $('.editable-error-block').css('line-height', '35px');
                    return msg;
                }
            }
        });
    }
});
<|endoftext|>


<commit_before>import React from 'react';
import path from 'path';
import wcjsRenderer from '../utils/wcjs-renderer';
import _ from 'lodash';
import {
    RaisedButton
}
from 'material-ui';

import PlayerActions from '../actions';
import PlayerStore from '../store';

try {
    var wcjs = require(path.join(process.cwd(), 'resources/bin/', 'WebChimera.js.node'));
} catch (e) {
    console.error('WCJS Load Error:', e);
}

export
default React.createClass({
    getInitialState() {
        return {
            uri: PlayerStore.getState().uri,
            initialResize: false,

            playing: PlayerStore.getState().playing,
            paused: PlayerStore.getState().paused,
            fullscreen: PlayerStore.getState().fullscreen
        }
    },
    componentWillMount() {
        PlayerStore.listen(this.update);
        window.addEventListener('resize', this.handleResize);
    },
    componentDidMount() {
        if (!PlayerStore.getState().wcjs) {
            PlayerActions.wcjsInit(wcjsRenderer.init(this.refs['wcjs-render'], [
                "--no-media-library",
                "--no-sub-autodetect-file",
                "--no-spu",
                "--no-stats",
                "--no-osd",
                "--network-caching", "3500",
                "--file-caching", "3000",
                "--no-skip-frames",
                "--no-video-title-show",
                "--disable-screensaver",
                "--no-autoscale",
                "--ipv4-timeout=86400000"
            ], {
                fallbackRenderer: false,
                preserveDrawingBuffer: true
            }, wcjs));
        } else {
            wcjsRenderer.reinit(this.refs['wcjs-render'], PlayerStore.getState().wcjs, {
                fallbackRenderer: false,
                preserveDrawingBuffer: true
            });
        }
        this.initPlayer();
    },
    componentWillUnmount() {
        wcjsRenderer.clearCanvas();
        PlayerStore.unlisten(this.update);
        window.removeEventListener('resize', this.handleResize);
    },
    update() {
        if (this.isMounted()) {
            this.setState({
                uri: PlayerStore.getState().uri,
                playing: PlayerStore.getState().playing,
                fullscreen: PlayerStore.getState().fullscreen
            });
        }
    },
    initPlayer() {
        this.player = PlayerStore.getState().wcjs;

        var initializeSize = _.once(() => {
            if (!this.state.initialResize) {
                this.setState({
                    initialResize: true
                });
                this.handleResize();
            }
        });

        this.player.onPositionChanged = _.throttle((pos) => {
            PlayerActions.position(pos);
            initializeSize();
        }, 500);

        this.player.onOpening = PlayerActions.buffering.bind(0);

        this.player.onTimeChanged = PlayerActions.time;

        this.player.onBuffering = _.throttle(PlayerActions.buffering, 500);

        this.player.onLengthChanged = PlayerActions.length;

        this.player.onSeekableChanged = PlayerActions.seekable;

        this.player.onPlaying = PlayerActions.playing;

        this.player.onPaused = PlayerActions.pause;

        this.player.onStopped = PlayerActions.stopped;

        this.player.onEndReached = this.player.stop;

        this.player.onEncounteredError = (error) => {
            console.error(error);
            this.player.stop();
        }

        this.player.playlist.add(this.state.uri);
        this.player.play();
        //this.player.subtitles.track = 0;
    },
    handleResize() {
        var canvas = this.refs['wcjs-render'];
        var container = document.body;
        var sourceAspect = canvas.width / canvas.height;
        var destAspect = container.clientWidth / container.clientHeight;
        var cond = destAspect > sourceAspect;

        if (cond) {
            canvas.style.height = "100%";
            canvas.style.width = ((container.clientHeight * sourceAspect) / container.clientWidth) * 100 + "%";
        } else {
            canvas.style.height = ((container.clientWidth / sourceAspect) / container.clientHeight) * 100 + "%";
            canvas.style.width = "100%";
        };
    },
    handleTogglePlay() {
        if (!this.state.buffering)
            this.player.togglePause(this.state.playing ? false : true);
    },
    render() {
        var renderStyles = {
            container: {
                textAlign: 'center'
            },
            canvas: {
                display: 'inline-block',
                height: '100vh',
                opacity: this.state.initialResize ? 1 : 0,
                position: 'absolute',
                top: '50%',
                left: '50%',
                transform: 'translate(-50%,-50%)'
            }
        };
        return (

                <RaisedButton onClick={this.handleTogglePlay} onDoubleClick={PlayerActions.toggleFullscreen.bind(this, !this.state.fullscreen)} iconClassName="material-icons" className="over-canvas" label="Canvas Overlay" />
                <canvas style={renderStyles.canvas} onClick={this.handleTogglePlay} onDoubleClick={PlayerActions.toggleFullscreen.bind(this, !this.state.fullscreen)} ref="wcjs-render" />

        );
    }
});<commit_msg>prevent player init w/o uri<commit_after>import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import path from 'path';
import wcjsRenderer from '../utils/wcjs-renderer';
import _ from 'lodash';
import {
    RaisedButton
}
from 'material-ui';

import PlayerActions from '../actions';
import PlayerStore from '../store';

try {
    var wcjs = require(path.join(process.cwd(), 'resources/bin/', 'WebChimera.js.node'));
} catch (e) {
    console.error('WCJS Load Error:', e);
}

export
default React.createClass({

    mixins: [PureRenderMixin],

    getInitialState() {
        return {
            uri: PlayerStore.getState().uri,
            initialResize: false,

            playing: PlayerStore.getState().playing,
            paused: PlayerStore.getState().paused,
            fullscreen: PlayerStore.getState().fullscreen
        }
    },
    componentWillMount() {
        PlayerStore.listen(this.update);
        window.addEventListener('resize', this.handleResize);
    },
    componentDidMount() {
        if (!PlayerStore.getState().wcjs) {
            PlayerActions.wcjsInit(wcjsRenderer.init(this.refs['wcjs-render'], [
                "--no-media-library",
                "--no-sub-autodetect-file",
                "--no-spu",
                "--no-stats",
                "--no-osd",
                "--network-caching", "3500",
                "--file-caching", "3000",
                "--no-skip-frames",
                "--no-video-title-show",
                "--disable-screensaver",
                "--no-autoscale",
                "--ipv4-timeout=86400000"
            ], {
                fallbackRenderer: false,
                preserveDrawingBuffer: true
            }, wcjs));
        } else {
            wcjsRenderer.reinit(this.refs['wcjs-render'], PlayerStore.getState().wcjs, {
                fallbackRenderer: false,
                preserveDrawingBuffer: true
            });
        }
        this.initPlayer();
    },
    componentWillUnmount() {
        wcjsRenderer.clearCanvas();
        PlayerStore.unlisten(this.update);
        window.removeEventListener('resize', this.handleResize);
    },
    update() {
        if (this.isMounted()) {
            this.setState({
                uri: PlayerStore.getState().uri,
                playing: PlayerStore.getState().playing,
                fullscreen: PlayerStore.getState().fullscreen
            });
        }
    },
    initPlayer() {
        this.player = PlayerStore.getState().wcjs;

        var initializeSize = _.once(() => {
            if (!this.state.initialResize) {
                this.setState({
                    initialResize: true
                });
                this.handleResize();
            }
        });

        this.player.onPositionChanged = _.throttle((pos) => {
            PlayerActions.position(pos);
            initializeSize();
        }, 500);

        this.player.onOpening = PlayerActions.buffering.bind(0);

        this.player.onTimeChanged = PlayerActions.time;

        this.player.onBuffering = _.throttle(PlayerActions.buffering, 500);

        this.player.onLengthChanged = PlayerActions.length;

        this.player.onSeekableChanged = PlayerActions.seekable;

        this.player.onPlaying = PlayerActions.playing;

        this.player.onPaused = PlayerActions.pause;

        this.player.onStopped = PlayerActions.stopped;

        this.player.onEndReached = this.player.stop;

        this.player.onEncounteredError = (error) => {
            console.error(error);
            this.player.stop();
        }
        if (this.state.uri) {
            this.player.playlist.add(this.state.uri);
            this.player.play();
        }
        //this.player.subtitles.track = 0;
    },
    handleResize() {
        var canvas = this.refs['wcjs-render'];
        var container = document.body;
        var sourceAspect = canvas.width / canvas.height;
        var destAspect = container.clientWidth / container.clientHeight;
        var cond = destAspect > sourceAspect;

        if (cond) {
            canvas.style.height = "100%";
            canvas.style.width = ((container.clientHeight * sourceAspect) / container.clientWidth) * 100 + "%";
        } else {
            canvas.style.height = ((container.clientWidth / sourceAspect) / container.clientHeight) * 100 + "%";
            canvas.style.width = "100%";
        };
    },
    handleTogglePlay() {
        if (!this.state.buffering)
            this.player.togglePause(this.state.playing ? false : true);
    },
    render() {
        var renderStyles = {
            container: {
                textAlign: 'center'
            },
            canvas: {
                display: 'inline-block',
                height: '100vh',
                opacity: this.state.initialResize ? 1 : 0,
                position: 'absolute',
                top: '50%',
                left: '50%',
                transform: 'translate(-50%,-50%)'
            }
        };
        return (

                <RaisedButton onClick={this.handleTogglePlay} onDoubleClick={PlayerActions.toggleFullscreen.bind(this, !this.state.fullscreen)} iconClassName="material-icons" className="over-canvas" label="Canvas Overlay" />
                <canvas style={renderStyles.canvas} onClick={this.handleTogglePlay} onDoubleClick={PlayerActions.toggleFullscreen.bind(this, !this.state.fullscreen)} ref="wcjs-render" />

        );
    }
});<|endoftext|>


<commit_before>const {h, Component} = require('preact')

const Drawer = require('./Drawer')

const blockedProperties = ['B', 'W', 'C', 'SZ']

class PropertyItem extends Component {
    shouldComponentUpdate({property, index, value, disabled}) {
        return property !== this.props.property
            || index !== this.props.index
            || value !== this.props.value
            || disabled !== this.props.disabled
    }

    render({property, index, value, disabled}) {
        return h('tr', {},
            h('th',
                {
                    style: {
                        width: +(index != null) * 3 + property.length
                    }
                },

                index == null ? property : [property, h('em', {}, `[${index}]`)]
            ),
            h('td', {},
                h('input', {
                    value,
                    disabled
                })
            ),
            h('td', {class: 'action'},
                h('a', {class: 'remove', title: 'Remove', href: '#'},
                    h('img', {src: './node_modules/octicons/build/svg/x.svg'})
                )
            )
        )
    }
}

class AdvancedPropertiesDrawer extends Component {
    constructor(props) {
        super(props)

        this.handleCloseButtonClick = evt => {
            evt.preventDefault()
            sabaki.closeDrawer()
        }
    }

    shouldComponentUpdate({show}) {
        return show || show !== this.props.show
    }

    render({treePosition, show}) {
        let [tree, index] = treePosition
        let node = tree.nodes[index]
        let properties = Object.keys(node).filter(x => x.toUpperCase() === x).sort()

        return h(Drawer,
            {
                type: 'advancedproperties',
                show
            },

            h('form', {},
                h('div', {class: 'properties-list'},
                    h('table', {},
                        properties.map(property =>
                            node[property].map((value, i) =>
                                h(PropertyItem, {
                                    property,
                                    value,
                                    index: node[property].length === 1 ? null : i,
                                    disabled: blockedProperties.includes(property)
                                })
                            )
                        )
                    )
                ),

                h('p', {},
                    h('button', {onClick: this.handleCloseButtonClick}, 'Close')
                )
            )
        )
    }
}

module.exports = AdvancedPropertiesDrawer
<commit_msg>Implement property changing<commit_after>const {h, Component} = require('preact')
const helper = require('../../modules/helper')

const Drawer = require('./Drawer')

const blockedProperties = ['B', 'W', 'C', 'SZ']

class PropertyItem extends Component {
    constructor(props) {
        super(props)

        this.handleRemoveButtonClick = evt => {
            evt.preventDefault()

            let {property, index, onRemove = helper.noop} = this.props
            onRemove({property, index})
        }

        this.handleChange = evt => {
            let {property, index, onChange = helper.noop} = this.props
            onChange({property, index, value: evt.currentTarget.value})
        }
    }

    shouldComponentUpdate({property, index, value, disabled}) {
        return property !== this.props.property
            || index !== this.props.index
            || value !== this.props.value
            || disabled !== this.props.disabled
    }

    render({property, index, value, disabled}) {
        return h('tr', {},
            h('th',
                {
                    style: {
                        width: +(index != null) * 3 + property.length
                    }
                },

                index == null ? property : [property, h('em', {}, `[${index}]`)]
            ),

            h('td', {},
                h('input', {
                    value,
                    disabled,
                    onInput: this.handleChange
                })
            ),

            h('td', {class: 'action'},
                !disabled && h('a',
                    {
                        class: 'remove',
                        title: 'Remove',
                        href: '#',
                        onClick: this.handleRemoveButtonClick
                    },

                    h('img', {src: './node_modules/octicons/build/svg/x.svg'})
                )
            )
        )
    }
}

class AdvancedPropertiesDrawer extends Component {
    constructor(props) {
        super(props)

        this.handleCloseButtonClick = evt => {
            evt.preventDefault()
            sabaki.closeDrawer()
        }

        this.handlePropertyChange = ({property, index, value}) => {
            let [tree, i] = this.props.treePosition
            let node = tree.nodes[i]

            if (index == null || !(property in node)) {
                node[property] = [value]
            } else {
                node[property][index] = value
            }

            sabaki.setCurrentTreePosition(tree, i)
        }

        this.handlePropertyRemove = ({property, index}) => {
            let [tree, i] = this.props.treePosition
            let node = tree.nodes[i]

            if (index == null) {
                delete node[property]
            } else if (property in node) {
                node[property].splice(index, 1)
            }

            sabaki.setCurrentTreePosition(tree, i)
        }
    }

    shouldComponentUpdate({show}) {
        return show || show !== this.props.show
    }

    render({treePosition, show}) {
        let [tree, index] = treePosition
        let node = tree.nodes[index]
        let properties = Object.keys(node).filter(x => x.toUpperCase() === x).sort()

        return h(Drawer,
            {
                type: 'advancedproperties',
                show
            },

            h('form', {},
                h('div', {class: 'properties-list'},
                    h('table', {},
                        properties.map(property =>
                            node[property].map((value, i) =>
                                h(PropertyItem, {
                                    property,
                                    value,
                                    index: node[property].length === 1 ? null : i,
                                    disabled: blockedProperties.includes(property),

                                    onChange: this.handlePropertyChange,
                                    onRemove: this.handlePropertyRemove
                                })
                            )
                        )
                    )
                ),

                h('p', {},
                    h('button', {onClick: this.handleCloseButtonClick}, 'Close')
                )
            )
        )
    }
}

module.exports = AdvancedPropertiesDrawer
<|endoftext|>


<commit_before>basemodel = angular.module('basemodel', []);

basemodel.factory('BaseModel',
                   ['$filter', '$rootScope', '$q', '$http',
                   function($filter, $rootScope, $q, $http) {

    /*
     * A service that retrieves, manages and saves "data", a collection of
     * individual objects that can be identified using a unique id
     */

    var _data = null;
    var added_ids = 0;

    return {
        method: '--error--',

        handle_data: function(data) { return data; },

        retrieve: function() {
            var deferred = $q.defer();
            var m = this;
            if(_data) {
                deferred.resolve(_data);
                return deferred.promise;
            }

            $http.get($rootScope.urlbase + "+" + this.method + "/").success(
                function(data, status, headers, config) {
                    _data = m.handle_data(data);
                    deferred.resolve(data);
                });
            return deferred.promise;
        },

        save: function() {
            var deferred = $q.defer();
            var m = this;
            $http.post($rootScope.urlbase + "+" + this.method + "/", {
                    data:$filter('json')(_data)
            }).success(
                function(data, status, headers, config) {
                    _data = m.handle_data(data);
                    deferred.resolve(data);
                }
            );
            return deferred.promise;
        },

        async: function() {
            return this.retrieve();
        },

        data: function() { return _data; },

        find: function(id) {
                var i;
                for(i = 0; i < _data.existing.length; i++) {
                    if(_data.existing[i].id == id) {
                        return _data.existing[i];
                    }
                }
            },
        add: function(data) {
            _data.existing.unshift(data);
            data.state = "added";
            data.id = "added_" + added_ids++;
            return data.id;
        },
        remove: function(id) {
            var existing = this.find(id);
            existing.state = "deleted";
        },
        update: function(id, data) {
            var existing = this.find(id);
            angular.extend(existing, data);
            if(existing.state != "added") {
                existing.state = "modified";
            }
        }
    };
}]);
<commit_msg>make method construction overridable<commit_after>basemodel = angular.module('basemodel', []);

basemodel.factory('BaseModel',
                   ['$filter', '$rootScope', '$q', '$http',
                   function($filter, $rootScope, $q, $http) {

    /*
     * A service that retrieves, manages and saves "data", a collection of
     * individual objects that can be identified using a unique id
     */

    var _data = null;
    var added_ids = 0;

    return {
        method: '--error--',

        handle_data: function(data) { return data; },
        construct_method: function(method) {
            return $rootScope.urlbase + "+" + method + "/";
        },

        retrieve: function() {
            var deferred = $q.defer();
            var m = this;
            if(_data) {
                deferred.resolve(_data);
                return deferred.promise;
            }

            $http.get(this.construct_method(this.method)).success(
                function(data, status, headers, config) {
                    _data = m.handle_data(data);
                    deferred.resolve(data);
                });
            return deferred.promise;
        },

        save: function() {
            var deferred = $q.defer();
            var m = this;
            $http.post($rootScope.urlbase + "+" + this.method + "/", {
                    data:$filter('json')(_data)
            }).success(
                function(data, status, headers, config) {
                    _data = m.handle_data(data);
                    deferred.resolve(data);
                }
            );
            return deferred.promise;
        },

        async: function() {
            return this.retrieve();
        },

        data: function() { return _data; },

        find: function(id) {
                var i;
                for(i = 0; i < _data.existing.length; i++) {
                    if(_data.existing[i].id == id) {
                        return _data.existing[i];
                    }
                }
            },
        add: function(data) {
            _data.existing.unshift(data);
            data.state = "added";
            data.id = "added_" + added_ids++;
            return data.id;
        },
        remove: function(id) {
            var existing = this.find(id);
            existing.state = "deleted";
        },
        update: function(id, data) {
            var existing = this.find(id);
            angular.extend(existing, data);
            if(existing.state != "added") {
                existing.state = "modified";
            }
        }
    };
}]);
<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React from 'react';
import { injectIntl, FormattedMessage } from 'react-intl';
import TextField from 'material-ui/TextField';
import Dialog from 'material-ui/Dialog';
import AppButton from '../../common/AppButton';
import QueryPickerLoggedInHeader from './QueryPickerLoggedInHeader';
import QueryPickerDemoHeader from './QueryPickerDemoHeader';
import { getShortDate } from '../../../lib/dateUtil';
import { QUERY_LABEL_CHARACTER_LIMIT } from '../../../lib/explorerUtil';
import messages from '../../../resources/messages';

const localMessages = {
  emptyMedia: { id: 'explorer.querypicker.emptyMedia', defaultMessage: 'all media (generally not a good idea)' },
  sourcesSummary: { id: 'explorer.querypicker.sources', defaultMessage: '{sourceCount, plural, \n =0 {``} \n =1 {# source} \n other {# sources }\n}' },
  collectionsSummary: { id: 'explorer.querypicker.coll', defaultMessage: '{collectionCount, plural, \n =0 {``} \n =1 {# collection} \n other {# collections }\n}' },
  searchHint: { id: 'explorer.querypicker.searchHint', defaultMessage: 'keywords' },
  queryDialog: { id: 'explorer.querypicker.queryDialog', defaultMessage: 'The query label shows up on the legend of the various charts and graphs below. We autogenerate it for you based on your query, but you can also set your own short name to make the charts easier to read.' },
  title: { id: 'explorer.querypicker.title', defaultMessage: 'Rename Query' },
};

const focusUsernameInputField = (input) => {
  if (input) {
    setTimeout(() => { input.focus(); }, 100);
  }
};

class QueryPickerItem extends React.Component {
  state = {
    labelChangeDialogOpen: false,
    labelInDialog: '',  // the actual label they type into the change-label popup dialog
  };

  handleBlurAndSelection = () => {
    const { onQuerySelected, query } = this.props;
    // don't allow selection in demo sample mode
    if (query.searchId === undefined) {
      onQuerySelected();
    }
  };

  updateLabelInDialog = (val) => {
    this.setState({ labelInDialog: val });
  };

  handleLabelEditRequest = () => {
    const { query } = this.props;
    this.setState({ showIconMenu: false, labelChangeDialogOpen: true, labelInDialog: query.label });
  };

  handleLabelClose = () => {
    this.setState({ labelChangeDialogOpen: false });
  };

  handleLabelChangeAndClose = () => {
    const { updateQueryProperty } = this.props;
    this.setState({ labelChangeDialogOpen: false });
    let updatedLabel = this.state.labelInDialog;
    if (updatedLabel.indexOf('...') > 0) {
      updatedLabel = updatedLabel.slice(0, updatedLabel.indexOf('...') - 1);
    }
    updateQueryProperty('label', updatedLabel);
  };

  handleMenuItemKeyDown = (evt) => {
    const { onSearch } = this.props;
    switch (evt.key) {
      case 'Enter':
        onSearch();
        break;
      default: break;
    }
  };

  handleColorChange = (newColor) => {
    const { updateQueryProperty } = this.props;
    updateQueryProperty('color', newColor);
  }

  render() {
    const { isLoggedIn, query, isSelected, isDeletable, displayLabel, isLabelEditable, updateDemoQueryLabel, onDelete } = this.props;
    const { formatMessage } = this.props.intl;
    let subT = null;
    let headerInfo = null;
    /* query fields are only editable in place for Demo mode. the user can delete a query
      in Logged-In mode, the user can click the icon button, and edit the label of the query or delete the query
    */
    const actions = [
      <AppButton
        className="query-item-header-dialog-button"
        label={formatMessage(messages.cancel)}
        primary
        onClick={this.handleLabelClose}
      />,
      <AppButton
        label={formatMessage(messages.rename)}
        primary
        keyboardFocused
        onClick={() => this.handleLabelChangeAndClose(query)}
      />,
    ];
    if (query) {
      if (isLoggedIn) {
        headerInfo = (
          <QueryPickerLoggedInHeader
            query={query}
            onLabelEditRequest={this.handleLabelEditRequest}
            isDeletable={isDeletable}
            displayLabel={displayLabel}
            onDelete={onDelete}
            onColorChange={this.handleColorChange}
            handleMenuItemKeyDown={this.handleMenuItemKeyDown}
          />
        );
      } else { // can delete only if this is a custom query (vs sample query) for demo users and this is not the only QueryPickerItem
        headerInfo = (
          <QueryPickerDemoHeader
            query={query}
            isDeletable={isDeletable}
            onDelete={onDelete}
            onColorChange={this.handleColorChange}
            updateDemoQueryLabel={updateDemoQueryLabel}
            isLabelEditable={isLabelEditable}
            handleMenuItemKeyDown={this.handleMenuItemKeyDown}
            focusUsernameInputField={focusUsernameInputField}
          />
        );
      }
      const collectionCount = query.collections ? query.collections.length : 0;
      const sourceCount = query.sources ? query.sources.length : 0;
      // const srcDesc = query.media;
      const totalMediaCount = collectionCount + sourceCount;
      const queryLabel = query.label;
      let oneSourceLabel = query.sources && query.sources[0] && query.sources[0].name ? query.sources[0].name : '';
      const oneCollLabelOrNumber = query.collections[0] && query.collections[0].label ? query.collections[0].label : '';
      const oneCollLabel = collectionCount === 1 ? oneCollLabelOrNumber : '';
      oneSourceLabel = sourceCount === 1 ? oneSourceLabel : '';

      const oneCollStatus = oneCollLabel;
      subT = <FormattedMessage {...localMessages.emptyMedia} values={{ totalMediaCount }} />;

      if (sourceCount === 0 && collectionCount === 1) {
        subT = (

            {displayLabel ? query.label : ''}
            {oneCollStatus.slice(0, QUERY_LABEL_CHARACTER_LIMIT).concat('...')}

            {query.startDate ? getShortDate(query.startDate) : ''} to {query.endDate ? getShortDate(query.endDate) : ''}

        );
      } else if (collectionCount === 0 && sourceCount === 1) {
        subT = (

            {displayLabel ? query.label : ''}
            {oneSourceLabel.slice(0, QUERY_LABEL_CHARACTER_LIMIT).concat('...')}

            {query.startDate ? getShortDate(query.startDate) : ''} to {query.endDate ? getShortDate(query.endDate) : ''}

        );
      } else if (totalMediaCount > 0) {
        subT = (

            {displayLabel ? query.label : ''}
            {collectionCount > 0 ?
<FormattedMessage {...localMessages.collectionsSummary} values={{ collectionCount, label: queryLabel }} />

 : ''}
            {sourceCount > 0 ?
<FormattedMessage {...localMessages.sourcesSummary} values={{ sourceCount, label: queryLabel }} />

 : ''}
            {query.startDate ? getShortDate(query.startDate) : ''} to {query.endDate ? getShortDate(query.endDate) : ''}

        );
      }
    }
    const extraClassNames = (isSelected) ? 'selected' : '';
    let fullQuery = query.label;
    if (fullQuery.indexOf('...') > 0) {
      fullQuery = fullQuery.slice(0, fullQuery.indexOf('...') - 1);
    }
    return (
      <div
        className={`query-picker-item ${extraClassNames}`}
        onTouchTap={() => this.handleBlurAndSelection()}
      >
        {headerInfo}
        <Dialog
          title={formatMessage(localMessages.title)}
          actions={actions}
          modal={false}
          open={this.state.labelChangeDialogOpen}
          onRequestClose={this.handleLabelClose}
        >

<FormattedMessage {...localMessages.queryDialog} />

          <TextField
            className="query-picker-editable-name"
            id="labelInDialog"
            name="labelInDialog"
            defaultValue={fullQuery}
            maxLength={QUERY_LABEL_CHARACTER_LIMIT}
            onChange={(e, val) => {
              this.updateLabelInDialog(val);
            }}
            ref={focusUsernameInputField}
            hintText={query.label || formatMessage(localMessages.searchHint)}
          />
        </Dialog>
        {subT}

    );
  }
}

QueryPickerItem.propTypes = {
  // from parent
  query: PropTypes.object,
  isSelected: PropTypes.bool.isRequired,
  isLabelEditable: PropTypes.bool.isRequired,
  isDeletable: PropTypes.func.isRequired,
  displayLabel: PropTypes.bool.isRequired,
  onQuerySelected: PropTypes.func,
  updateQueryProperty: PropTypes.func.isRequired,
  updateDemoQueryLabel: PropTypes.func.isRequired,
  onSearch: PropTypes.func.isRequired,
  onDelete: PropTypes.func.isRequired,
  loadEditLabelDialog: PropTypes.func,
  isLoggedIn: PropTypes.bool.isRequired,
  // from composition
  intl: PropTypes.object.isRequired,
};

export default
  injectIntl(
    QueryPickerItem
  );
<commit_msg>Capitalization (#876)<commit_after>import PropTypes from 'prop-types';
import React from 'react';
import { injectIntl, FormattedMessage } from 'react-intl';
import TextField from 'material-ui/TextField';
import Dialog from 'material-ui/Dialog';
import AppButton from '../../common/AppButton';
import QueryPickerLoggedInHeader from './QueryPickerLoggedInHeader';
import QueryPickerDemoHeader from './QueryPickerDemoHeader';
import { getShortDate } from '../../../lib/dateUtil';
import { QUERY_LABEL_CHARACTER_LIMIT } from '../../../lib/explorerUtil';
import messages from '../../../resources/messages';

const localMessages = {
  emptyMedia: { id: 'explorer.querypicker.emptyMedia', defaultMessage: 'All media (generally not a good idea)' },
  sourcesSummary: { id: 'explorer.querypicker.sources', defaultMessage: '{sourceCount, plural, \n =0 {``} \n =1 {# source} \n other {# sources }\n}' },
  collectionsSummary: { id: 'explorer.querypicker.coll', defaultMessage: '{collectionCount, plural, \n =0 {``} \n =1 {# collection} \n other {# collections }\n}' },
  searchHint: { id: 'explorer.querypicker.searchHint', defaultMessage: 'keywords' },
  queryDialog: { id: 'explorer.querypicker.queryDialog', defaultMessage: 'The query label shows up on the legend of the various charts and graphs below. We autogenerate it for you based on your query, but you can also set your own short name to make the charts easier to read.' },
  title: { id: 'explorer.querypicker.title', defaultMessage: 'Rename Query' },
};

const focusUsernameInputField = (input) => {
  if (input) {
    setTimeout(() => { input.focus(); }, 100);
  }
};

class QueryPickerItem extends React.Component {
  state = {
    labelChangeDialogOpen: false,
    labelInDialog: '',  // the actual label they type into the change-label popup dialog
  };

  handleBlurAndSelection = () => {
    const { onQuerySelected, query } = this.props;
    // don't allow selection in demo sample mode
    if (query.searchId === undefined) {
      onQuerySelected();
    }
  };

  updateLabelInDialog = (val) => {
    this.setState({ labelInDialog: val });
  };

  handleLabelEditRequest = () => {
    const { query } = this.props;
    this.setState({ showIconMenu: false, labelChangeDialogOpen: true, labelInDialog: query.label });
  };

  handleLabelClose = () => {
    this.setState({ labelChangeDialogOpen: false });
  };

  handleLabelChangeAndClose = () => {
    const { updateQueryProperty } = this.props;
    this.setState({ labelChangeDialogOpen: false });
    let updatedLabel = this.state.labelInDialog;
    if (updatedLabel.indexOf('...') > 0) {
      updatedLabel = updatedLabel.slice(0, updatedLabel.indexOf('...') - 1);
    }
    updateQueryProperty('label', updatedLabel);
  };

  handleMenuItemKeyDown = (evt) => {
    const { onSearch } = this.props;
    switch (evt.key) {
      case 'Enter':
        onSearch();
        break;
      default: break;
    }
  };

  handleColorChange = (newColor) => {
    const { updateQueryProperty } = this.props;
    updateQueryProperty('color', newColor);
  }

  render() {
    const { isLoggedIn, query, isSelected, isDeletable, displayLabel, isLabelEditable, updateDemoQueryLabel, onDelete } = this.props;
    const { formatMessage } = this.props.intl;
    let subT = null;
    let headerInfo = null;
    /* query fields are only editable in place for Demo mode. the user can delete a query
      in Logged-In mode, the user can click the icon button, and edit the label of the query or delete the query
    */
    const actions = [
      <AppButton
        className="query-item-header-dialog-button"
        label={formatMessage(messages.cancel)}
        primary
        onClick={this.handleLabelClose}
      />,
      <AppButton
        label={formatMessage(messages.rename)}
        primary
        keyboardFocused
        onClick={() => this.handleLabelChangeAndClose(query)}
      />,
    ];
    if (query) {
      if (isLoggedIn) {
        headerInfo = (
          <QueryPickerLoggedInHeader
            query={query}
            onLabelEditRequest={this.handleLabelEditRequest}
            isDeletable={isDeletable}
            displayLabel={displayLabel}
            onDelete={onDelete}
            onColorChange={this.handleColorChange}
            handleMenuItemKeyDown={this.handleMenuItemKeyDown}
          />
        );
      } else { // can delete only if this is a custom query (vs sample query) for demo users and this is not the only QueryPickerItem
        headerInfo = (
          <QueryPickerDemoHeader
            query={query}
            isDeletable={isDeletable}
            onDelete={onDelete}
            onColorChange={this.handleColorChange}
            updateDemoQueryLabel={updateDemoQueryLabel}
            isLabelEditable={isLabelEditable}
            handleMenuItemKeyDown={this.handleMenuItemKeyDown}
            focusUsernameInputField={focusUsernameInputField}
          />
        );
      }
      const collectionCount = query.collections ? query.collections.length : 0;
      const sourceCount = query.sources ? query.sources.length : 0;
      // const srcDesc = query.media;
      const totalMediaCount = collectionCount + sourceCount;
      const queryLabel = query.label;
      let oneSourceLabel = query.sources && query.sources[0] && query.sources[0].name ? query.sources[0].name : '';
      const oneCollLabelOrNumber = query.collections[0] && query.collections[0].label ? query.collections[0].label : '';
      const oneCollLabel = collectionCount === 1 ? oneCollLabelOrNumber : '';
      oneSourceLabel = sourceCount === 1 ? oneSourceLabel : '';

      const oneCollStatus = oneCollLabel;
      subT = <FormattedMessage {...localMessages.emptyMedia} values={{ totalMediaCount }} />;

      if (sourceCount === 0 && collectionCount === 1) {
        subT = (

            {displayLabel ? query.label : ''}
            {oneCollStatus.slice(0, QUERY_LABEL_CHARACTER_LIMIT).concat('...')}

            {query.startDate ? getShortDate(query.startDate) : ''} to {query.endDate ? getShortDate(query.endDate) : ''}

        );
      } else if (collectionCount === 0 && sourceCount === 1) {
        subT = (

            {displayLabel ? query.label : ''}
            {oneSourceLabel.slice(0, QUERY_LABEL_CHARACTER_LIMIT).concat('...')}

            {query.startDate ? getShortDate(query.startDate) : ''} to {query.endDate ? getShortDate(query.endDate) : ''}

        );
      } else if (totalMediaCount > 0) {
        subT = (

            {displayLabel ? query.label : ''}
            {collectionCount > 0 ?
<FormattedMessage {...localMessages.collectionsSummary} values={{ collectionCount, label: queryLabel }} />

 : ''}
            {sourceCount > 0 ?
<FormattedMessage {...localMessages.sourcesSummary} values={{ sourceCount, label: queryLabel }} />

 : ''}
            {query.startDate ? getShortDate(query.startDate) : ''} to {query.endDate ? getShortDate(query.endDate) : ''}

        );
      }
    }
    const extraClassNames = (isSelected) ? 'selected' : '';
    let fullQuery = query.label;
    if (fullQuery.indexOf('...') > 0) {
      fullQuery = fullQuery.slice(0, fullQuery.indexOf('...') - 1);
    }
    return (
      <div
        className={`query-picker-item ${extraClassNames}`}
        onTouchTap={() => this.handleBlurAndSelection()}
      >
        {headerInfo}
        <Dialog
          title={formatMessage(localMessages.title)}
          actions={actions}
          modal={false}
          open={this.state.labelChangeDialogOpen}
          onRequestClose={this.handleLabelClose}
        >

<FormattedMessage {...localMessages.queryDialog} />

          <TextField
            className="query-picker-editable-name"
            id="labelInDialog"
            name="labelInDialog"
            defaultValue={fullQuery}
            maxLength={QUERY_LABEL_CHARACTER_LIMIT}
            onChange={(e, val) => {
              this.updateLabelInDialog(val);
            }}
            ref={focusUsernameInputField}
            hintText={query.label || formatMessage(localMessages.searchHint)}
          />
        </Dialog>
        {subT}

    );
  }
}

QueryPickerItem.propTypes = {
  // from parent
  query: PropTypes.object,
  isSelected: PropTypes.bool.isRequired,
  isLabelEditable: PropTypes.bool.isRequired,
  isDeletable: PropTypes.func.isRequired,
  displayLabel: PropTypes.bool.isRequired,
  onQuerySelected: PropTypes.func,
  updateQueryProperty: PropTypes.func.isRequired,
  updateDemoQueryLabel: PropTypes.func.isRequired,
  onSearch: PropTypes.func.isRequired,
  onDelete: PropTypes.func.isRequired,
  loadEditLabelDialog: PropTypes.func,
  isLoggedIn: PropTypes.bool.isRequired,
  // from composition
  intl: PropTypes.object.isRequired,
};

export default
  injectIntl(
    QueryPickerItem
  );
<|endoftext|>


<commit_before>import React, { Component, Fragment } from 'react'
import api from 'api'
import { isURL } from 'validator'
import styled from 'styled-components'
import Auth from 'components/Auth'
import {
  Box,
  Flex,
  Label,
  Heading,
  Text,
  Button,
  Field,
  theme,
} from '@hackclub/design-system'

const CarouselSubmissionFormOuter = styled(Flex).attrs({
  p: [3, 3, 4],
  bg: theme.colors.white,
  align: 'center',
})`
  border-radius: 5px;
  flex-grow: 1;
  flex-shrink: 1;
  flex-direction: column;
  ${theme.mediaQueries.md} {
    border-radius: 10px;
  }
`

const LiveField = styled(Field).attrs({
  label: 'Live URL',
  name: 'Live URL',
  placeholder: '(where’s the final product?)',
})`
  min-width: 320px;
`

const CodeField = styled(Field).attrs({
  label: 'Code URL',
  name: 'Code URL',
  placeholder: '(where’s the code?)',
  mb: [1, 2, 3],
})`
  min-width: 320px;
`

class CarouselSubmissionForm extends Component {
  state = { verifying: false, requestingSubmission: false }

  onClickSubmitButton() {
    const { workshopSlug, submissionData } = this.props
    const { requestingSubmission } = this.state
    const { liveUrl, codeUrl } = submissionData

    // To prevent double-click resubmissions
    if (requestingSubmission) return
    this.setState({ requestingSubmission: true })

    api
      .post(`v1/workshops/${workshopSlug}/projects`, {
        method: 'POST',
        body: JSON.stringify({
          live_url: liveUrl,
          code_url: codeUrl,
          // Screenshot happens on backend for now
          // screenshot_id: screenshotId
        }),
        headers: { 'Content-Type': 'application/json' },
      })
      // For now, just refresh the page. Needs a real Submssion Complete page eventually.
      .then(resp => location.reload())
  }

  onChangeLiveURL(event) {
    const liveUrl = event.target.value
    const { submissionData } = this.props
    this.props.setSubmissionData({ ...submissionData, liveUrl })
  }

  onChangeCodeURL(event) {
    const codeUrl = event.target.value
    const { submissionData } = this.props
    this.props.setSubmissionData({ ...submissionData, codeUrl })
  }

  onClickVeryifyButton() {
    this.setState({ verifying: true })
  }

  render() {
    const {
      workshopSlug,
      submissionData,
      authed,
      authData,
      onSignOut,
    } = this.props

    const { verifying, requestingSubmission } = this.state

    const { liveUrl, codeUrl } = submissionData

    const onClickSubmitButton = this.onClickSubmitButton.bind(this)
    const onClickVeryifyButton = this.onClickVeryifyButton.bind(this)

    const onChangeLiveURL = this.onChangeLiveURL.bind(this)
    const onChangeCodeURL = this.onChangeCodeURL.bind(this)

    const validURLs = isURL(liveUrl) && isURL(codeUrl)

    const disableSubmission = !validURLs || requestingSubmission

    return (
      <CarouselSubmissionFormOuter>
        {verifying ? null : (
          <Fragment>
            <LiveField value={liveUrl} onChange={onChangeLiveURL} />
            <CodeField value={codeUrl} onChange={onChangeCodeURL} />
          </Fragment>
        )}
        {authed || !verifying ? null : (
          <Heading.h4 mb={2}>Before you submit…</Heading.h4>
        )}
        {!(authed || verifying) ? null : (
          <Fragment>
            <Auth
              preAuthed={authed}
              preAuthData={authData}
              signOutCallback={onSignOut}
              loginCallback={onClickSubmitButton}
              headline={"Show that you're human"}
              cardProps={{
                maxWidth: 20,
                p: 3,
                mb: 0,
                bg: 'primary',
              }}
            />
          </Fragment>
        )}
        {!(authed || !verifying) ? null : (
          <Button
            px={3}
            py={2}
            disabled={disableSubmission}
            onClick={
              disableSubmission
                ? null
                : authed
                  ? onClickSubmitButton
                  : onClickVeryifyButton
            }
          >
            {authed ? 'Submit My Thing' : 'Verify & Submit'}
          </Button>
        )}
      </CarouselSubmissionFormOuter>
    )
  }
}

export default CarouselSubmissionForm
<commit_msg>set input types to 'url'<commit_after>import React, { Component, Fragment } from 'react'
import api from 'api'
import { isURL } from 'validator'
import styled from 'styled-components'
import Auth from 'components/Auth'
import {
  Box,
  Flex,
  Label,
  Heading,
  Text,
  Button,
  Field,
  theme,
} from '@hackclub/design-system'

const CarouselSubmissionFormOuter = styled(Flex).attrs({
  p: [3, 3, 4],
  bg: theme.colors.white,
  align: 'center',
})`
  border-radius: 5px;
  flex-grow: 1;
  flex-shrink: 1;
  flex-direction: column;
  ${theme.mediaQueries.md} {
    border-radius: 10px;
  }
`

const LiveField = styled(Field).attrs({
  label: 'Live URL',
  name: 'Live URL',
  placeholder: '(where’s the final product?)',
  type: 'url',
})`
  min-width: 320px;
`

const CodeField = styled(Field).attrs({
  label: 'Code URL',
  name: 'Code URL',
  placeholder: '(where’s the code?)',
  type: 'url',
  mb: [1, 2, 3],
})`
  min-width: 320px;
`

class CarouselSubmissionForm extends Component {
  state = { verifying: false, requestingSubmission: false }

  onClickSubmitButton() {
    const { workshopSlug, submissionData } = this.props
    const { requestingSubmission } = this.state
    const { liveUrl, codeUrl } = submissionData

    // To prevent double-click resubmissions
    if (requestingSubmission) return
    this.setState({ requestingSubmission: true })

    api
      .post(`v1/workshops/${workshopSlug}/projects`, {
        method: 'POST',
        body: JSON.stringify({
          live_url: liveUrl,
          code_url: codeUrl,
          // Screenshot happens on backend for now
          // screenshot_id: screenshotId
        }),
        headers: { 'Content-Type': 'application/json' },
      })
      // For now, just refresh the page. Needs a real Submssion Complete page eventually.
      .then(resp => location.reload())
  }

  onChangeLiveURL(event) {
    const liveUrl = event.target.value
    const { submissionData } = this.props
    this.props.setSubmissionData({ ...submissionData, liveUrl })
  }

  onChangeCodeURL(event) {
    const codeUrl = event.target.value
    const { submissionData } = this.props
    this.props.setSubmissionData({ ...submissionData, codeUrl })
  }

  onClickVeryifyButton() {
    this.setState({ verifying: true })
  }

  render() {
    const {
      workshopSlug,
      submissionData,
      authed,
      authData,
      onSignOut,
    } = this.props

    const { verifying, requestingSubmission } = this.state

    const { liveUrl, codeUrl } = submissionData

    const onClickSubmitButton = this.onClickSubmitButton.bind(this)
    const onClickVeryifyButton = this.onClickVeryifyButton.bind(this)

    const onChangeLiveURL = this.onChangeLiveURL.bind(this)
    const onChangeCodeURL = this.onChangeCodeURL.bind(this)

    const validURLs = isURL(liveUrl) && isURL(codeUrl)

    const disableSubmission = !validURLs || requestingSubmission

    return (
      <CarouselSubmissionFormOuter>
        {verifying ? null : (
          <Fragment>
            <LiveField value={liveUrl} onChange={onChangeLiveURL} />
            <CodeField value={codeUrl} onChange={onChangeCodeURL} />
          </Fragment>
        )}
        {authed || !verifying ? null : (
          <Heading.h4 mb={2}>Before you submit…</Heading.h4>
        )}
        {!(authed || verifying) ? null : (
          <Fragment>
            <Auth
              preAuthed={authed}
              preAuthData={authData}
              signOutCallback={onSignOut}
              loginCallback={onClickSubmitButton}
              headline={"Show that you're human"}
              cardProps={{
                maxWidth: 20,
                p: 3,
                mb: 0,
                bg: 'primary',
              }}
            />
          </Fragment>
        )}
        {!(authed || !verifying) ? null : (
          <Button
            px={3}
            py={2}
            disabled={disableSubmission}
            onClick={
              disableSubmission
                ? null
                : authed
                  ? onClickSubmitButton
                  : onClickVeryifyButton
            }
          >
            {authed ? 'Submit My Thing' : 'Verify & Submit'}
          </Button>
        )}
      </CarouselSubmissionFormOuter>
    )
  }
}

export default CarouselSubmissionForm
<|endoftext|>