<commit_before>(function ($) {
    "use strict";

    $.fn.dataTable.ext.errMode = 'throw';

    $.widget("mapbender.mbDigitizer", $.mapbender.mbDataManager, {

        options: {
            classes: {},
            create: null,
            debug: false,
            fileURI: "uploads/featureTypes",
            schemes: {},
            target: null
        },
        mbMap: null,
        printClient: null,
        active: false,
        controlFactory: null,

        _create: function () {
            this._super();
            var widget = this;
            var target = this.options.target;
            this.toolsetRenderer = this._createToolsetRenderer();
            this.styleEditor = this._createStyleEditor();
            Mapbender.elementRegistry.waitReady(target).then(function(mbMap) {
                widget.mbMap = mbMap;
                widget.setup();
                // Let data manager base method trigger "ready" event and start loading data
                widget._start();
            }, function() {
                Mapbender.checkTarget("mbDigitizer", target);
            });
        },
        _createTableRenderer: function() {
            return new Mapbender.Digitizer.TableRenderer(this);
        },
        _createToolsetRenderer: function() {
            return new Mapbender.Digitizer.Toolset(this);
        },
        _createStyleEditor: function() {
            return new Mapbender.Digitizer.FeatureStyleEditor(this);
        },
        _afterCreate: function() {
            // Invoked only by data manager _create
            // do nothing; deliberately do NOT call parent method
        },
        _schemaFactory: function(schemaConfig) {
            var schemaConfig_ = this._super(schemaConfig);
            if (!schemaConfig_.featureType || !schemaConfig_.featureType.connection || !schemaConfig_.featureType.table || !schemaConfig_.featureType.geomType) {
                throw new Error("Feature Type not correctly specified in Configuration of scheme")
            }
            if (schemaConfig_.popup && schemaConfig_.popup.buttons) {
                _.each(schemaConfig_.popup.buttons, function (button) {
                    console.error("Using Javascript code in the configuration is deprecated:", button);
                });
            }
            return new Mapbender.Digitizer.Scheme(schemaConfig_, this);
        },
        setup: function() {
            var self = this;
            Mapbender.elementRegistry.waitCreated('.mb-element-printclient').then(function (printClient) {
                self.printClient = printClient;
                $.extend(self.printClient, Mapbender.Digitizer.printPlugin);
            });
            var olMap = this.mbMap.getModel().olMap;
            this.contextMenu = this._createContextMenu(olMap);
            this.controlFactory = new Mapbender.Digitizer.DigitizingControlFactory(olMap);
            if (this.options.displayOnInactive) {
                this.activate();
            }
        },
        // reveal / hide = automatic sidepane integration API
        reveal: function() {
            this.activate();
        },
        hide: function() {
            this.deactivate();
        },
        _createContextMenu: function(olMap) {
            return new Mapbender.Digitizer.MapContextMenu(olMap, this);
        },
        _initializeEvents: function() {
            this._super();
            var self = this;
            this.element.on('click', '.-fn-toggle-tool[data-toolname]', function() {
                var $button = $(this);
                var toolName = $button.attr('data-toolname');
                var schema = $button.data('schema');
                var oldState = $button.hasClass('active');
                if (!oldState) {
                    var $activeOthers = $button.siblings('.-fn-toggle-tool.active').not($button);
                    $activeOthers.each(function() {
                        var $other = $(this);
                        self._toggleDrawingTool(schema, $other.attr('data-toolname'), false);
                    });
                    $activeOthers.removeClass('active');
                }
                $button.toggleClass('active', !oldState);
                self._toggleDrawingTool(schema, toolName, !oldState);
            });
        },
        activate: function() {
            if (!this.active) {
                this._activateSchema(this._getCurrentSchema());
                this.active = true;
            }
        },
        deactivate: function() {
            this._deactivateSchema(this._getCurrentSchema());
            this.active = false;
        },
        _activateSchema: function(schema) {
            this._super(schema);
            // HACK: externally patch renderer onto schema post-construction
            if (!schema.renderer) {
                schema.renderer = new Mapbender.Digitizer.FeatureRenderer(this.mbMap.getModel().olMap, schema);
            }
            // HACK: externally patch editor onto schema post-construction
            if (schema.allowDigitize && !schema.geometryEditor) {
                var layer = schema.renderer.getLayer();
                schema.geometryEditor = new Mapbender.Digitizer.FeatureEditor(layer, this.controlFactory);
            }

            this.contextMenu.setSchema(schema);
            this._toggleSchemaInteractions(schema, true);
            schema.renderer.layer.setVisible(true);
        },
        _toggleDrawingTool: function(schema, toolName, state) {
            schema.geometryEditor.toggleTool(toolName, schema, state);
            // @todo: disable selectControl if drawing active and vice-versa
        },
        _deactivateSchema: function(schema) {
            this._super(schema);
            this._toggleSchemaInteractions(schema, false);
            if (!(this.options.displayOnInactive || schema.displayPermanent)) {
                schema.renderer.layer.setVisible(false);
            }
        },
        _toggleSchemaInteractions: function(schema, state) {
            if (schema.geometryEditor) {
                schema.geometryEditor.setActive(state);
            }
            schema.renderer.highlightControl.setActive(state);
            schema.renderer.selectControl.setActive(state);
            if (state) {
                this.contextMenu.enable();
            } else {
                this.contextMenu.disable();
            }
        },
        _getDataStoreFromSchema: function(schema) {
            // Digitizer schema config aliases "dataStore" (upstream) as "featureType"
            return schema.featureType;
        },
        _updateToolset: function($container, schema) {
            this._super($container, schema);
            $('.btn, button', $container)
                .removeClass('button')  // no Mapbender legacy button styling
                // some .btn- color variant is required to visualize active / not active
                .addClass('btn btn-sm btn-default')
            ;
        },
        _renderToolset: function(schema) {
            var dmToolset = $(this._super(schema)).get();   // force to array of nodes
            var nodes = [];
            nodes.push(this.toolsetRenderer.renderCurrentExtentSwitch(schema));
            nodes.push(this.toolsetRenderer.renderButtons(schema, dmToolset));
            return nodes;
        },
        _openEditDialog: function(schema, dataItem) {
            var dialog = this._super(schema, dataItem);
            if (schema.geometryEditor) {
                schema.geometryEditor.pause();
            }
            return dialog;
        },
        _getEditDialogButtons: function(schema, feature) {
            var self = this;
            var buttons = [];
            if (schema.copy && schema.copy.enable) {
                buttons.push({
                    text: Mapbender.trans('mb.digitizer.feature.clone.title'),
                    click: function() {
                        self.cloneFeature(schema, feature);
                    }
                });
            }
            // @todo: don't offer this button if style cannot be saved (no style field)
            if (schema.allowCustomStyle) {
                buttons.push({
                    text: Mapbender.trans('mb.digitizer.feature.style.change'),
                    click: function() {
                        self.openStyleEditor(schema, feature);
                    }
                });
            }
            if (schema.printable && this.printClient) {
                var printClient = this.printClient;
                buttons.push({
                    text: Mapbender.trans('mb.digitizer.feature.print'),
                    click: function() {
                        printClient.printDigitizerFeature(feature, schema);
                    }
                });
            }
            buttons.push.apply(buttons, this._super(schema, feature));
            return buttons;
        },
        _getItemData: function(schema, feature) {
            // NOTE: 'data' property may not exist if feature has just been newly created by an editing tool
            if (!feature.get('data')) {
                feature.set('data', {});
            }
            return this._super(schema, feature.get('data'));
        },
        _afterRemove: function(schema, feature, id) {
            var olMap = this.mbMap.getModel().olMap;
            schema.renderer.getLayer().getSource().removeFeature(feature);
            this._super(schema, feature, id);
            // Multi-Digitizer sync support
            $(olMap).trigger({type: "Digitizer.FeatureUpdatedOnServer", feature: feature});
        },
        _prepareDataItem: function(schema, itemData) {
            var renderer = schema.renderer;
            var feature = (new ol.format.WKT()).readFeatureFromText(itemData.geometry);
            feature.set('data', itemData.properties || {});
            renderer.initializeFeature(schema, feature);
            return feature;
        },
        _afterSave: function(schema, feature, originalId, responseData) {
            // unravel dm-incompatible response format
            // @todo: should we or should we not replace feature geometry?
            // var geometry = (new ol.format.WKT()).readGeometryFromText(responseData.dataItem.geometry);
            // feature.setGEometry(geometry);

            this._super(schema, feature, originalId, {
                dataItem: responseData.dataItem.properties
            });
            feature.set('dirty', false);
            feature.set("modificationState", undefined);
            if (schema.geometryEditor) {
                schema.geometryEditor.resume();
            }
            var olMap = this.mbMap.getModel().olMap;
            $(olMap).trigger({type: "Digitizer.FeatureUpdatedOnServer", feature: feature});   // why?
        },
        _getData: function(schema) {
            var renderer = schema.renderer;
            return this._super(schema).then(function(features) {
                renderer.getLayer().getSource().clear();
                renderer.getLayer().getSource().addFeatures(features);
                return features;
            });
        },
        _cancelForm: function(schema, feature) {
            this._super(schema, feature);
            // NOTE: this also detects cloned features (via new copy functionality) as new
            var isNew = !this._getUniqueItemId(schema, feature);
            if (isNew) {
                schema.renderer.getLayer().getSource().removeFeature(feature);
            }
            // @todo: document new schema config value
            if (!isNew && schema.revertChangedGeometryOnCancel) {
                var oldGeometry = feature.get('oldGeometry');
                if (oldGeometry) {
                    feature.setGeometry(oldGeometry.clone());
                }
                feature.set('dirty', false);
            }
            if (schema.geometryEditor) {
                schema.geometryEditor.resume();
            }
        },
        _replaceItemData: function(schema, feature, newValues) {
            // NOTE: 'data' is a regular mutable data Object (see _prpareDataItem)
            this._super(schema, feature.get('data'), newValues);
        },
        _getSaveRequestData: function(schema, dataItem, newValues) {
            return {
                properties: Object.assign({}, this._getItemData(schema, dataItem), newValues || {}),
                geometry: new ol.format.WKT().writeGeometryText(dataItem.getGeometry()),
                srid: this.getProjectionCode()
            };
        },
        cloneFeature: function(schema, feature) {
            var layer = schema.renderer.getLayer();
            var newFeature = feature.clone();
            var copyDefaults = schema.copy.data || {};
            var newAttributes = {};
            if (schema.copy.overwriteValuesWithDefault) {
                Object.assign(newAttributes, feature.get('data'), copyDefaults);
            } else {
                Object.assign(newAttributes, copyDefaults, feature.get('data'));
            }
            // clear id
            newAttributes[schema.featureType.uniqueId] = null;
            // @todo: Detect the first text-type attribute and prefix it with "Copy of"
            //        Digitizer has no direct knowledge of data columns and types
            //        All we have are table column and form item configuration
            newFeature.set('data', newAttributes);

            // TODO this works, but is potentially buggy: numbers need to be relative to current zoom
            if (schema.copy.moveCopy) {
                newFeature.getGeometry().translate(schema.copy.moveCopy.x, schema.copy.moveCopy.y);
            }
            schema.renderer.getLayer().getSource().addFeature(newFeature);
            newFeature.set('dirty', true);
            newFeature.set("modificationState", "isNew");

            layer.getSource().dispatchEvent({type: 'controlFactory.FeatureAdded', feature: newFeature});
        },
        // Support method for custom Scheme class
        getProjectionCode: function() {
            return this.mbMap.getModel().getCurrentProjectionCode();
        },
        openStyleEditor: function(schema, feature) {
            var styleConfig = feature.get('basicStyle') || schema.renderer.basicStyles.default;
            this.styleEditor.openEditor(schema, feature, styleConfig);
        },
        zoomToFeature: function(schema, feature) {
            Mapbender.Model.zoomToFeature(feature);
        },
        __formatting_dummy: null
    });

})(jQuery);
<commit_msg>[NFC] adjust _openEditDialog variable naming dataItem => feature<commit_after>(function ($) {
    "use strict";

    $.fn.dataTable.ext.errMode = 'throw';

    $.widget("mapbender.mbDigitizer", $.mapbender.mbDataManager, {

        options: {
            classes: {},
            create: null,
            debug: false,
            fileURI: "uploads/featureTypes",
            schemes: {},
            target: null
        },
        mbMap: null,
        printClient: null,
        active: false,
        controlFactory: null,

        _create: function () {
            this._super();
            var widget = this;
            var target = this.options.target;
            this.toolsetRenderer = this._createToolsetRenderer();
            this.styleEditor = this._createStyleEditor();
            Mapbender.elementRegistry.waitReady(target).then(function(mbMap) {
                widget.mbMap = mbMap;
                widget.setup();
                // Let data manager base method trigger "ready" event and start loading data
                widget._start();
            }, function() {
                Mapbender.checkTarget("mbDigitizer", target);
            });
        },
        _createTableRenderer: function() {
            return new Mapbender.Digitizer.TableRenderer(this);
        },
        _createToolsetRenderer: function() {
            return new Mapbender.Digitizer.Toolset(this);
        },
        _createStyleEditor: function() {
            return new Mapbender.Digitizer.FeatureStyleEditor(this);
        },
        _afterCreate: function() {
            // Invoked only by data manager _create
            // do nothing; deliberately do NOT call parent method
        },
        _schemaFactory: function(schemaConfig) {
            var schemaConfig_ = this._super(schemaConfig);
            if (!schemaConfig_.featureType || !schemaConfig_.featureType.connection || !schemaConfig_.featureType.table || !schemaConfig_.featureType.geomType) {
                throw new Error("Feature Type not correctly specified in Configuration of scheme")
            }
            if (schemaConfig_.popup && schemaConfig_.popup.buttons) {
                _.each(schemaConfig_.popup.buttons, function (button) {
                    console.error("Using Javascript code in the configuration is deprecated:", button);
                });
            }
            return new Mapbender.Digitizer.Scheme(schemaConfig_, this);
        },
        setup: function() {
            var self = this;
            Mapbender.elementRegistry.waitCreated('.mb-element-printclient').then(function (printClient) {
                self.printClient = printClient;
                $.extend(self.printClient, Mapbender.Digitizer.printPlugin);
            });
            var olMap = this.mbMap.getModel().olMap;
            this.contextMenu = this._createContextMenu(olMap);
            this.controlFactory = new Mapbender.Digitizer.DigitizingControlFactory(olMap);
            if (this.options.displayOnInactive) {
                this.activate();
            }
        },
        // reveal / hide = automatic sidepane integration API
        reveal: function() {
            this.activate();
        },
        hide: function() {
            this.deactivate();
        },
        _createContextMenu: function(olMap) {
            return new Mapbender.Digitizer.MapContextMenu(olMap, this);
        },
        _initializeEvents: function() {
            this._super();
            var self = this;
            this.element.on('click', '.-fn-toggle-tool[data-toolname]', function() {
                var $button = $(this);
                var toolName = $button.attr('data-toolname');
                var schema = $button.data('schema');
                var oldState = $button.hasClass('active');
                if (!oldState) {
                    var $activeOthers = $button.siblings('.-fn-toggle-tool.active').not($button);
                    $activeOthers.each(function() {
                        var $other = $(this);
                        self._toggleDrawingTool(schema, $other.attr('data-toolname'), false);
                    });
                    $activeOthers.removeClass('active');
                }
                $button.toggleClass('active', !oldState);
                self._toggleDrawingTool(schema, toolName, !oldState);
            });
        },
        activate: function() {
            if (!this.active) {
                this._activateSchema(this._getCurrentSchema());
                this.active = true;
            }
        },
        deactivate: function() {
            this._deactivateSchema(this._getCurrentSchema());
            this.active = false;
        },
        _activateSchema: function(schema) {
            this._super(schema);
            // HACK: externally patch renderer onto schema post-construction
            if (!schema.renderer) {
                schema.renderer = new Mapbender.Digitizer.FeatureRenderer(this.mbMap.getModel().olMap, schema);
            }
            // HACK: externally patch editor onto schema post-construction
            if (schema.allowDigitize && !schema.geometryEditor) {
                var layer = schema.renderer.getLayer();
                schema.geometryEditor = new Mapbender.Digitizer.FeatureEditor(layer, this.controlFactory);
            }

            this.contextMenu.setSchema(schema);
            this._toggleSchemaInteractions(schema, true);
            schema.renderer.layer.setVisible(true);
        },
        _toggleDrawingTool: function(schema, toolName, state) {
            schema.geometryEditor.toggleTool(toolName, schema, state);
            // @todo: disable selectControl if drawing active and vice-versa
        },
        _deactivateSchema: function(schema) {
            this._super(schema);
            this._toggleSchemaInteractions(schema, false);
            if (!(this.options.displayOnInactive || schema.displayPermanent)) {
                schema.renderer.layer.setVisible(false);
            }
        },
        _toggleSchemaInteractions: function(schema, state) {
            if (schema.geometryEditor) {
                schema.geometryEditor.setActive(state);
            }
            schema.renderer.highlightControl.setActive(state);
            schema.renderer.selectControl.setActive(state);
            if (state) {
                this.contextMenu.enable();
            } else {
                this.contextMenu.disable();
            }
        },
        _getDataStoreFromSchema: function(schema) {
            // Digitizer schema config aliases "dataStore" (upstream) as "featureType"
            return schema.featureType;
        },
        _updateToolset: function($container, schema) {
            this._super($container, schema);
            $('.btn, button', $container)
                .removeClass('button')  // no Mapbender legacy button styling
                // some .btn- color variant is required to visualize active / not active
                .addClass('btn btn-sm btn-default')
            ;
        },
        _renderToolset: function(schema) {
            var dmToolset = $(this._super(schema)).get();   // force to array of nodes
            var nodes = [];
            nodes.push(this.toolsetRenderer.renderCurrentExtentSwitch(schema));
            nodes.push(this.toolsetRenderer.renderButtons(schema, dmToolset));
            return nodes;
        },
        _openEditDialog: function(schema, feature) {
            var dialog = this._super(schema, feature);
            if (schema.geometryEditor) {
                schema.geometryEditor.pause();
            }
            return dialog;
        },
        _getEditDialogButtons: function(schema, feature) {
            var self = this;
            var buttons = [];
            if (schema.copy && schema.copy.enable) {
                buttons.push({
                    text: Mapbender.trans('mb.digitizer.feature.clone.title'),
                    click: function() {
                        self.cloneFeature(schema, feature);
                    }
                });
            }
            // @todo: don't offer this button if style cannot be saved (no style field)
            if (schema.allowCustomStyle) {
                buttons.push({
                    text: Mapbender.trans('mb.digitizer.feature.style.change'),
                    click: function() {
                        self.openStyleEditor(schema, feature);
                    }
                });
            }
            if (schema.printable && this.printClient) {
                var printClient = this.printClient;
                buttons.push({
                    text: Mapbender.trans('mb.digitizer.feature.print'),
                    click: function() {
                        printClient.printDigitizerFeature(feature, schema);
                    }
                });
            }
            buttons.push.apply(buttons, this._super(schema, feature));
            return buttons;
        },
        _getItemData: function(schema, feature) {
            // NOTE: 'data' property may not exist if feature has just been newly created by an editing tool
            if (!feature.get('data')) {
                feature.set('data', {});
            }
            return this._super(schema, feature.get('data'));
        },
        _afterRemove: function(schema, feature, id) {
            var olMap = this.mbMap.getModel().olMap;
            schema.renderer.getLayer().getSource().removeFeature(feature);
            this._super(schema, feature, id);
            // Multi-Digitizer sync support
            $(olMap).trigger({type: "Digitizer.FeatureUpdatedOnServer", feature: feature});
        },
        _prepareDataItem: function(schema, itemData) {
            var renderer = schema.renderer;
            var feature = (new ol.format.WKT()).readFeatureFromText(itemData.geometry);
            feature.set('data', itemData.properties || {});
            renderer.initializeFeature(schema, feature);
            return feature;
        },
        _afterSave: function(schema, feature, originalId, responseData) {
            // unravel dm-incompatible response format
            // @todo: should we or should we not replace feature geometry?
            // var geometry = (new ol.format.WKT()).readGeometryFromText(responseData.dataItem.geometry);
            // feature.setGEometry(geometry);

            this._super(schema, feature, originalId, {
                dataItem: responseData.dataItem.properties
            });
            feature.set('dirty', false);
            feature.set("modificationState", undefined);
            if (schema.geometryEditor) {
                schema.geometryEditor.resume();
            }
            var olMap = this.mbMap.getModel().olMap;
            $(olMap).trigger({type: "Digitizer.FeatureUpdatedOnServer", feature: feature});   // why?
        },
        _getData: function(schema) {
            var renderer = schema.renderer;
            return this._super(schema).then(function(features) {
                renderer.getLayer().getSource().clear();
                renderer.getLayer().getSource().addFeatures(features);
                return features;
            });
        },
        _cancelForm: function(schema, feature) {
            this._super(schema, feature);
            // NOTE: this also detects cloned features (via new copy functionality) as new
            var isNew = !this._getUniqueItemId(schema, feature);
            if (isNew) {
                schema.renderer.getLayer().getSource().removeFeature(feature);
            }
            // @todo: document new schema config value
            if (!isNew && schema.revertChangedGeometryOnCancel) {
                var oldGeometry = feature.get('oldGeometry');
                if (oldGeometry) {
                    feature.setGeometry(oldGeometry.clone());
                }
                feature.set('dirty', false);
            }
            if (schema.geometryEditor) {
                schema.geometryEditor.resume();
            }
        },
        _replaceItemData: function(schema, feature, newValues) {
            // NOTE: 'data' is a regular mutable data Object (see _prpareDataItem)
            this._super(schema, feature.get('data'), newValues);
        },
        _getSaveRequestData: function(schema, dataItem, newValues) {
            return {
                properties: Object.assign({}, this._getItemData(schema, dataItem), newValues || {}),
                geometry: new ol.format.WKT().writeGeometryText(dataItem.getGeometry()),
                srid: this.getProjectionCode()
            };
        },
        cloneFeature: function(schema, feature) {
            var layer = schema.renderer.getLayer();
            var newFeature = feature.clone();
            var copyDefaults = schema.copy.data || {};
            var newAttributes = {};
            if (schema.copy.overwriteValuesWithDefault) {
                Object.assign(newAttributes, feature.get('data'), copyDefaults);
            } else {
                Object.assign(newAttributes, copyDefaults, feature.get('data'));
            }
            // clear id
            newAttributes[schema.featureType.uniqueId] = null;
            // @todo: Detect the first text-type attribute and prefix it with "Copy of"
            //        Digitizer has no direct knowledge of data columns and types
            //        All we have are table column and form item configuration
            newFeature.set('data', newAttributes);

            // TODO this works, but is potentially buggy: numbers need to be relative to current zoom
            if (schema.copy.moveCopy) {
                newFeature.getGeometry().translate(schema.copy.moveCopy.x, schema.copy.moveCopy.y);
            }
            schema.renderer.getLayer().getSource().addFeature(newFeature);
            newFeature.set('dirty', true);
            newFeature.set("modificationState", "isNew");

            layer.getSource().dispatchEvent({type: 'controlFactory.FeatureAdded', feature: newFeature});
        },
        // Support method for custom Scheme class
        getProjectionCode: function() {
            return this.mbMap.getModel().getCurrentProjectionCode();
        },
        openStyleEditor: function(schema, feature) {
            var styleConfig = feature.get('basicStyle') || schema.renderer.basicStyles.default;
            this.styleEditor.openEditor(schema, feature, styleConfig);
        },
        zoomToFeature: function(schema, feature) {
            Mapbender.Model.zoomToFeature(feature);
        },
        __formatting_dummy: null
    });

})(jQuery);
<|endoftext|>


<commit_before>//SurveysIndexView Component Constructor
function SurveysIndexView() {
  var Survey = require('models/survey');
  var HeaderView = require('ui/common/components/HeaderView');
  var _ = require('lib/underscore')._;
  var TopLevelView = require('ui/common/components/TopLevelView');
  var progressBarView = require('ui/common/components/ProgressBar');
  var Palette = require('ui/common/components/Palette');
  var SurveyRowView = require('ui/common/surveys/SurveyRowView');
  var Toast = require('ui/common/components/Toast');
  var Measurements = require('ui/common/components/Measurements');
  var SyncHandler = require('models/syncHandler');

  var self = new TopLevelView('List of Surveys');

  var convertModelDataForTable = function() {
    if (table) {
      _(table.getChildren()).each(function(childView) {
        table.remove(childView);
      });
    }
    _(Survey.allSurveys()).map(function(survey) {
      var row = new SurveyRowView(survey);
      row.addEventListener('surveys_row_view.row_clicked', function() {
        self.fireEvent('surveys_index_view.table_row_clicked', {
          surveyID : survey.id
        });
      });
      row.addEventListener('surveys_row_view.add_response_clicked', function() {
        self.fireEvent('surveys_index_view.add_response_clicked', {
          surveyID : survey.id
        });
      });
      table.add(row);
    });
    self.add(table);
  };

  var showMessageIfTableIsEmpty = function() {
    if (Survey.isEmpty()) {
      self.add(label);
      self.remove(table);
    } else {
      self.remove(label);
      convertModelDataForTable();
    }
  };

  var progressComplete = function(entityBeingSynced) {
    self.refresh();
    self.remove(progressBarView);
    Ti.API.info("Entity being fetched : " + entityBeingSynced);
    if (entityBeingSynced)
      (new Toast('Successfully fetched surveys')).show();
    self.fireEvent('progress.finish');
  };

  var errorListener = function(data) {
    if (data.status >= 400) {
      alert("Your server isn't responding. Sorry about that.");
    } else if (data.status === 0) {
      Survey.truncate();
      if(!progressBarView.isHidden()) {
        alert("We have encountered an error fetching your surveys.\nPlease fetch your surveys again.");
        self.refresh();
      }
    }
    progressBarView.hideView();
    self.remove(progressBarView);
  };

  var progressSurveyComplete = function(){
    progressComplete('surveys');
    progressBarView.removeEventListener('surveys.sync.completed', progressSurveyComplete);
  };

  self.fetchAllSurveys = function() {
    var progressBar = progressBarView;
    Survey.fetchAllQuestionsCount(function(number){
      if(number === 0) {
        (new Toast("No surveys to fetch")).show();
        return;
      }
      self.add(progressBar);
      progressBar.addEventListener('surveys.sync.completed', progressSurveyComplete);
      progressBar.init('surveys.sync.completed', number);
      progressBar.setMessage("Fetching surveys...");
      Survey.fetchSurveys(new SyncHandler(progressBar.incrementValue, function(){}, errorListener));
    });
  };

  var table = Titanium.UI.createScrollView({
    top : self.headerHeight,
    height : Ti.UI.SIZE,
    layout : 'vertical'
  });

  var messageWhenEmpty = function() {
    var loggedIn = Ti.App.Properties.getString('loggedIn');
    if(loggedIn === 'true')
      return "Fetch surveys to use the application.";
    else
      return "You're not logged in yet. Please login from the menu and fetch surveys to use the application.";
  };

  var label = Ti.UI.createLabel({
    color : '#333',
    font : {
      fontSize : Measurements.FONT_BIG    },
      text : messageWhenEmpty(),
      textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
      top : '40%',
      width : 'auto',
      height : 'auto'
  });

  showMessageIfTableIsEmpty();

  self.refresh = function() {
    showMessageIfTableIsEmpty();
    self.updateUserName();
  };
  Ti.App.addEventListener('settings.refreshSurveys', self.refresh);

  var showSyncSummary = function(data) {
    alert((data.successes || 0) + " surveys successfully synced.\n" + (data.errors || 0) + " surveys' sync failed.");
  };

  self.syncAllResponses = function() {
    var progressBar = progressBarView;
    self.add(progressBar);
    progressBar.init('sync.complete.response', Survey.allResponsesCount());
    progressBar.setMessage("Syncing Responses...");
    Survey.syncAllResponses(new SyncHandler(progressBar.incrementValue, function(data) { showSyncSummary(data); progressComplete(); }));
  };

  return self;
}

module.exports = SurveysIndexView;
<commit_msg>Chnage the label when empty in refresh method of SurveyIndexView<commit_after>//SurveysIndexView Component Constructor
function SurveysIndexView() {
  var Survey = require('models/survey');
  var HeaderView = require('ui/common/components/HeaderView');
  var _ = require('lib/underscore')._;
  var TopLevelView = require('ui/common/components/TopLevelView');
  var progressBarView = require('ui/common/components/ProgressBar');
  var Palette = require('ui/common/components/Palette');
  var SurveyRowView = require('ui/common/surveys/SurveyRowView');
  var Toast = require('ui/common/components/Toast');
  var Measurements = require('ui/common/components/Measurements');
  var SyncHandler = require('models/syncHandler');

  var self = new TopLevelView('List of Surveys');

  var convertModelDataForTable = function() {
    if (table) {
      _(table.getChildren()).each(function(childView) {
        table.remove(childView);
      });
    }
    _(Survey.allSurveys()).map(function(survey) {
      var row = new SurveyRowView(survey);
      row.addEventListener('surveys_row_view.row_clicked', function() {
        self.fireEvent('surveys_index_view.table_row_clicked', {
          surveyID : survey.id
        });
      });
      row.addEventListener('surveys_row_view.add_response_clicked', function() {
        self.fireEvent('surveys_index_view.add_response_clicked', {
          surveyID : survey.id
        });
      });
      table.add(row);
    });
    self.add(table);
  };

  var showMessageIfTableIsEmpty = function() {
    if (Survey.isEmpty()) {
      setMessageWhenEmpty();
      self.add(label);
      self.remove(table);
    } else {
      self.remove(label);
      convertModelDataForTable();
    }
  };

  var progressComplete = function(entityBeingSynced) {
    self.refresh();
    self.remove(progressBarView);
    Ti.API.info("Entity being fetched : " + entityBeingSynced);
    if (entityBeingSynced)
      (new Toast('Successfully fetched surveys')).show();
    self.fireEvent('progress.finish');
  };

  var errorListener = function(data) {
    if (data.status >= 400) {
      alert("Your server isn't responding. Sorry about that.");
    } else if (data.status === 0) {
      Survey.truncate();
      if(!progressBarView.isHidden()) {
        alert("We have encountered an error fetching your surveys.\nPlease fetch your surveys again.");
        self.refresh();
      }
    }
    progressBarView.hideView();
    self.remove(progressBarView);
  };

  var progressSurveyComplete = function(){
    progressComplete('surveys');
    progressBarView.removeEventListener('surveys.sync.completed', progressSurveyComplete);
  };

  self.fetchAllSurveys = function() {
    var progressBar = progressBarView;
    Survey.fetchAllQuestionsCount(function(number){
      if(number === 0) {
        (new Toast("No surveys to fetch")).show();
        return;
      }
      self.add(progressBar);
      progressBar.addEventListener('surveys.sync.completed', progressSurveyComplete);
      progressBar.init('surveys.sync.completed', number);
      progressBar.setMessage("Fetching surveys...");
      Survey.fetchSurveys(new SyncHandler(progressBar.incrementValue, function(){}, errorListener));
    });
  };

  var table = Titanium.UI.createScrollView({
    top : self.headerHeight,
    height : Ti.UI.SIZE,
    layout : 'vertical'
  });

  var setMessageWhenEmpty = function() {
    var loggedIn = Ti.App.Properties.getString('loggedIn');
    if(loggedIn === 'true')
      label.text = "Fetch surveys to use the application.";
    else
      label.text = "You're not logged in yet. Please login from the menu and fetch surveys to use the application.";
  };

  var label = Ti.UI.createLabel({
    color : '#333',
    font : {
      fontSize : Measurements.FONT_BIG    },
      text : "",
      textAlign : Ti.UI.TEXT_ALIGNMENT_CENTER,
      top : '40%',
      width : 'auto',
      height : 'auto'
  });

  showMessageIfTableIsEmpty();

  self.refresh = function() {
    showMessageIfTableIsEmpty();
    self.updateUserName();
  };
  Ti.App.addEventListener('settings.refreshSurveys', self.refresh);

  var showSyncSummary = function(data) {
    alert((data.successes || 0) + " surveys successfully synced.\n" + (data.errors || 0) + " surveys' sync failed.");
  };

  self.syncAllResponses = function() {
    var progressBar = progressBarView;
    self.add(progressBar);
    progressBar.init('sync.complete.response', Survey.allResponsesCount());
    progressBar.setMessage("Syncing Responses...");
    Survey.syncAllResponses(new SyncHandler(progressBar.incrementValue, function(data) { showSyncSummary(data); progressComplete(); }));
  };

  return self;
}

module.exports = SurveysIndexView;
<|endoftext|>


<commit_before>/* const */ DEBUG = false;

const BUTTON_LABELS = [
    // buttonLabel, eventTitlePrefix, buttonClassNames.
    ['-',       '-',  ['jfk-button', 'jfk-button-standard', 'button-dash']],
    ['1',       '1.', ['jfk-button', 'jfk-button-standard', 'button-1']],
    ['2',       '2.', ['jfk-button', 'jfk-button-standard', 'button-2']],
    ['3',       '3.', ['jfk-button', 'jfk-button-standard', 'button-3']],
    ['4',       '4.', ['jfk-button', 'jfk-button-standard', 'button-4']],
    ['5',       '5.', ['jfk-button', 'jfk-button-standard', 'button-5']],
    ['X',       null, ['jfk-button', 'jfk-button-standard', 'button-x']],
    ['DONE',    '✓',  ['jfk-button', 'jfk-button-default',  'button-d']],
    ['NOPE',    '✗',  ['jfk-button', 'jfk-button-standard', 'button-n']],
    ['OKAY',    '▣',  ['jfk-button', 'jfk-button-standard', 'button-o']],
    ['AWESOME', 'ツ',  ['jfk-button', 'jfk-button-standard', 'button-a']],
];

const BUTTON_SELECTORS = {
    '-': '.button-dash',
    '1': '.button-1',
    '2': '.button-2',
    '3': '.button-3',
    '4': '.button-4',
    '5': '.button-5',
    'x': '.button-x',
    'd': '.button-d',
    'n': '.button-n',
    'o': '.button-o',
    'a': '.button-a',
};

const KEYCODE = {
    'DASH': 189,
};

const PRIORITY_EVENT_TITLE_PREFIXES = ['-', '1.', '2.', '3.', '4.', '5.', null];

function dispatchEvent(obj, event) {
    var evt = new Event(
        event,
        {
            target: obj,
            bubbles: true,
        }
    );
    obj.dispatchEvent(evt);
}

function insertAfter(newNode, referenceNode) {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}

function insertButtons(referenceNode, onclickAction, where, alternateReferenceNode) {
    var insertTarget = referenceNode;
    for (var i in BUTTON_LABELS) {
        var buttonLabel = BUTTON_LABELS[i][0];
        DEBUG && console.log('buttonLabel:', buttonLabel);

        var eventTitlePrefix = BUTTON_LABELS[i][1];
        DEBUG && console.log('eventTitlePrefix:', eventTitlePrefix);

        var buttonClassNames = BUTTON_LABELS[i][2];
        DEBUG && console.log('buttonClassNames:', buttonClassNames);

        var button = document.createElement('button');
        button.innerHTML = '' + buttonLabel[0] + '' + buttonLabel.slice(1);
        button.classList.add(...buttonClassNames);

        (function(myButton, myEventTitlePrefix) {
            myButton.onclick = function() {
                DEBUG && console.log('myButton.onclick');
                onclickAction(myButton, myEventTitlePrefix);
            };
        }(button, eventTitlePrefix));

        DEBUG && console.log('inserting button', button, where, insertTarget);
        if (where === 'after') {
            insertAfter(button, insertTarget);
            insertTarget = button;
        } else if (where === 'inside') {
            if (alternateReferenceNode && !PRIORITY_EVENT_TITLE_PREFIXES.includes(eventTitlePrefix)) {
                alternateReferenceNode.appendChild(button);
            } else {
                referenceNode.appendChild(button);
            }
        }
    }
}

function clickButton(clickedData) {
    DEBUG && console.group('clickButton');
    DEBUG && console.log('clickedData:', clickedData);
    DEBUG && console.log('button clicked');

    // Today.
    var date = new Date();
    var monthName = date.toLocaleString('en-us', { 'month': 'short' });
    var todayFormattedDate = monthName + ' ' + date.getDate() + ', ' + date.getFullYear();

    // Event date.
    var startDateInput = document.querySelector('[aria-label="Start date"]');
    var endDateInput = document.querySelector('[aria-label="End date"]');
    var eventDate = startDateInput.value;
    if (endDateInput.value !== startDateInput.value) {
        eventDate += ' - ' + endDateInput.value;
    }
    DEBUG && console.log('eventDate:', eventDate);

    var eventTitle = document.querySelector('[aria-label="Title"]');
    var calendarEventTitle = eventTitle.value;
    DEBUG && console.log('before calendarEventTitle:', calendarEventTitle);

    // Remove leading number (e.g. "1. " in "1. My Calendar Event").
    calendarEventTitle = calendarEventTitle.replace(/^\d+\. /, '');
    // Remove leading ! character.
    calendarEventTitle = calendarEventTitle.replace(/^! /, '');
    // Remove leading - character.
    calendarEventTitle = calendarEventTitle.replace(/^- /, '');
    // Remove leading ~ character.
    calendarEventTitle = calendarEventTitle.replace(/^~ /, '');

    var buttonLabel = clickedData.button.innerText;
    DEBUG && console.log('buttonLabel:', buttonLabel);
    if (buttonLabel === 'DONE') {
        // Remove leading "Tentative: ".
        calendarEventTitle = calendarEventTitle.replace(/^Tentative: /, '');
    }

    DEBUG && console.log(' after calendarEventTitle:', calendarEventTitle);

    // "✓ My Event; Dec 31, 2015; event date: Jan 1, 2016"
    // "1. My Event"
    // "My Event"
    var eventTitlePrefix = clickedData['eventTitlePrefix'];
    var newCalendarEventTitle = calendarEventTitle;
    if (eventTitlePrefix !== null) {
        newCalendarEventTitle = eventTitlePrefix + ' ' + calendarEventTitle;
        if (! PRIORITY_EVENT_TITLE_PREFIXES.includes(eventTitlePrefix)) {
            newCalendarEventTitle += ';' +
                ' ' + todayFormattedDate + ';' +
                ' event date: ' + eventDate;
        }
    }
    DEBUG && console.log('newCalendarEventTitle:', newCalendarEventTitle);
    eventTitle.value = newCalendarEventTitle;

    setTimeout(function() {
        dispatchEvent(eventTitle, 'input');
    }, 200);

    var saveButton = document.querySelector('[aria-label="Save"]');
    setTimeout(function() {
        saveButton.click();
    }, 700);

    DEBUG && console.groupEnd();
}

function handleKeyEvent(event) {
    DEBUG && console.info('handleKeyEvent');
    var character = String.fromCharCode(event.which);
    if (!event.shiftKey) {
        character = character.toLowerCase();
    }
    if (event.which === KEYCODE.DASH) {
        character = '-';
    }
    DEBUG && console.log('character:', character);

    // Ignore events with modifiers.
    if (event.shiftKey) {
        return;
    }
    if (event.altKey) {
        return;
    }
    if (event.ctrlKey) {
        return;
    }
    if (event.metaKey) {
        return;
    }

    // Take requested action when event bubble is open and a keyboard shortcut matching the key pressed is found.
    var eventBubble = document.querySelector('#xDetDlg[data-eventid]')
    if (eventBubble && character in BUTTON_SELECTORS) {
        event.preventDefault();

        var buttonSelector = BUTTON_SELECTORS[character];
        var buttonToClick = document.querySelector(buttonSelector);
        if (buttonToClick) {
            buttonToClick.click();
        }
    }
}

document.addEventListener('keydown', handleKeyEvent);

var buttonClickedData;
(function(){
    var pathname = window.location.pathname;
    setInterval(function() {
        if (pathname !== window.location.pathname) {
            DEBUG && console.log('pathname changed');
            pathname = window.location.pathname;
            if (pathname.match(/^\/calendar\/r\/eventedit\//)) {
                DEBUG && console.log('on event edit page');

                if (buttonClickedData) {
                    clickButton(buttonClickedData);
                    buttonClickedData = null;
                } else {
                    // Add action buttons to calendar event edit page.
                    DEBUG && console.log('adding buttons');
                    var onclickAction = function(myButton, myEventTitlePrefix) {
                        buttonClickedData = {
                            'button': myButton,
                            'eventTitlePrefix': myEventTitlePrefix,
                        };
                        clickButton(buttonClickedData);
                        buttonClickedData = null;
                    };

                    var saveButton = document.querySelector('[aria-label="Save"]');

                    var buttonWrapper = document.createElement('div');
                    buttonWrapper.style.left = '350px';
                    buttonWrapper.style.position = 'absolute';
                    buttonWrapper.style.top = '65px';
                    buttonWrapper.style.width = '350px';
                    buttonWrapper.style.zIndex = '1';

                    insertAfter(buttonWrapper, saveButton);
                    insertButtons(buttonWrapper, onclickAction, 'inside');
                }
            }
        }
    }, 1000);
})();

document.onclick = function(event) {
    DEBUG && console.group('element clicked');
    DEBUG && console.log('element clicked:', event.target);

    var target = event.target;
    if (!target) {
        return;
    }
    var eventIdFound = false;
    for (var i = 0; i < 5; i++) {
        if (!target) {
            break;
        // Ignore event bubble.
        } else if (target.getAttribute('id') === 'xDetDlg') {
            break;
        } else if (target.getAttribute('data-eventid')) {
            eventIdFound = true;
            break;
        }
        target = target.parentElement;
    }
    DEBUG && console.log('target:', target);
    if (eventIdFound) {
        var eventId = target.getAttribute('data-eventid');
        DEBUG && console.log('event id found:', eventId);

        function modifyEventBubble(attempt) {
            attempt = attempt || 1;
            if (attempt >= 3) {
                return;
            }
            attempt += 1;

            var eventBubble = document.querySelector('#xDetDlg[data-eventid="' + eventId + '"]');
            DEBUG && console.log('eventBubble:', eventBubble);
            if (!eventBubble) {
                setTimeout(modifyEventBubble, 500, attempt);
                return;
            }

            var editEventButton = eventBubble.querySelector('[data-tooltip="Edit event"]');
            var eventEditable = editEventButton ? true : false;
            if (!eventEditable) {
                return;
            }

            var lastEventBubbleMetaItem = document.querySelector('#xDtlDlgCt > div:last-child');

            var newEventBubbleMetaItem = document.createElement('div');
            newEventBubbleMetaItem.classList.add(...lastEventBubbleMetaItem.classList);
            insertAfter(newEventBubbleMetaItem, lastEventBubbleMetaItem);

            var lastNewEventBubbleMetaItem = document.createElement('div');
            lastNewEventBubbleMetaItem.classList.add(...lastEventBubbleMetaItem.classList);
            lastNewEventBubbleMetaItem.classList.add('_gcw-last-event-bubble-meta');
            insertAfter(lastNewEventBubbleMetaItem, newEventBubbleMetaItem);

            var onclickAction = function(myButton, myEventTitlePrefix) {
                buttonClickedData = {
                    'button': myButton,
                    'eventTitlePrefix': myEventTitlePrefix,
                };

                var editEventButton = document.querySelector('[aria-label="Edit event"]');
                DEBUG && console.log('editEventButton:', editEventButton);
                editEventButton.click();
            };
            insertButtons(newEventBubbleMetaItem, onclickAction, 'inside', lastNewEventBubbleMetaItem);
        }
        setTimeout(modifyEventBubble, 100);
    }
    DEBUG && console.groupEnd();
};
<commit_msg>Reuse event title prefix instead of using innerText<commit_after>/* const */ DEBUG = false;

const BUTTON_LABELS = [
    // buttonLabel, eventTitlePrefix, buttonClassNames.
    ['-',       '-',  ['jfk-button', 'jfk-button-standard', 'button-dash']],
    ['1',       '1.', ['jfk-button', 'jfk-button-standard', 'button-1']],
    ['2',       '2.', ['jfk-button', 'jfk-button-standard', 'button-2']],
    ['3',       '3.', ['jfk-button', 'jfk-button-standard', 'button-3']],
    ['4',       '4.', ['jfk-button', 'jfk-button-standard', 'button-4']],
    ['5',       '5.', ['jfk-button', 'jfk-button-standard', 'button-5']],
    ['X',       null, ['jfk-button', 'jfk-button-standard', 'button-x']],
    ['DONE',    '✓',  ['jfk-button', 'jfk-button-default',  'button-d']],
    ['NOPE',    '✗',  ['jfk-button', 'jfk-button-standard', 'button-n']],
    ['OKAY',    '▣',  ['jfk-button', 'jfk-button-standard', 'button-o']],
    ['AWESOME', 'ツ',  ['jfk-button', 'jfk-button-standard', 'button-a']],
];

const BUTTON_SELECTORS = {
    '-': '.button-dash',
    '1': '.button-1',
    '2': '.button-2',
    '3': '.button-3',
    '4': '.button-4',
    '5': '.button-5',
    'x': '.button-x',
    'd': '.button-d',
    'n': '.button-n',
    'o': '.button-o',
    'a': '.button-a',
};

const KEYCODE = {
    'DASH': 189,
};

const PRIORITY_EVENT_TITLE_PREFIXES = ['-', '1.', '2.', '3.', '4.', '5.', null];

function dispatchEvent(obj, event) {
    var evt = new Event(
        event,
        {
            target: obj,
            bubbles: true,
        }
    );
    obj.dispatchEvent(evt);
}

function insertAfter(newNode, referenceNode) {
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}

function insertButtons(referenceNode, onclickAction, where, alternateReferenceNode) {
    var insertTarget = referenceNode;
    for (var i in BUTTON_LABELS) {
        var buttonLabel = BUTTON_LABELS[i][0];
        DEBUG && console.log('buttonLabel:', buttonLabel);

        var eventTitlePrefix = BUTTON_LABELS[i][1];
        DEBUG && console.log('eventTitlePrefix:', eventTitlePrefix);

        var buttonClassNames = BUTTON_LABELS[i][2];
        DEBUG && console.log('buttonClassNames:', buttonClassNames);

        var button = document.createElement('button');
        button.innerHTML = '' + buttonLabel[0] + '' + buttonLabel.slice(1);
        button.classList.add(...buttonClassNames);

        (function(myButton, myEventTitlePrefix) {
            myButton.onclick = function() {
                DEBUG && console.log('myButton.onclick');
                onclickAction(myButton, myEventTitlePrefix);
            };
        }(button, eventTitlePrefix));

        DEBUG && console.log('inserting button', button, where, insertTarget);
        if (where === 'after') {
            insertAfter(button, insertTarget);
            insertTarget = button;
        } else if (where === 'inside') {
            if (alternateReferenceNode && !PRIORITY_EVENT_TITLE_PREFIXES.includes(eventTitlePrefix)) {
                alternateReferenceNode.appendChild(button);
            } else {
                referenceNode.appendChild(button);
            }
        }
    }
}

function clickButton(clickedData) {
    DEBUG && console.group('clickButton');
    DEBUG && console.log('clickedData:', clickedData);
    DEBUG && console.log('button clicked');

    // Today.
    var date = new Date();
    var monthName = date.toLocaleString('en-us', { 'month': 'short' });
    var todayFormattedDate = monthName + ' ' + date.getDate() + ', ' + date.getFullYear();

    // Event date.
    var startDateInput = document.querySelector('[aria-label="Start date"]');
    var endDateInput = document.querySelector('[aria-label="End date"]');
    var eventDate = startDateInput.value;
    if (endDateInput.value !== startDateInput.value) {
        eventDate += ' - ' + endDateInput.value;
    }
    DEBUG && console.log('eventDate:', eventDate);

    var eventTitle = document.querySelector('[aria-label="Title"]');
    var calendarEventTitle = eventTitle.value;
    DEBUG && console.log('before calendarEventTitle:', calendarEventTitle);

    // Remove leading number (e.g. "1. " in "1. My Calendar Event").
    calendarEventTitle = calendarEventTitle.replace(/^\d+\. /, '');
    // Remove leading ! character.
    calendarEventTitle = calendarEventTitle.replace(/^! /, '');
    // Remove leading - character.
    calendarEventTitle = calendarEventTitle.replace(/^- /, '');
    // Remove leading ~ character.
    calendarEventTitle = calendarEventTitle.replace(/^~ /, '');

    var eventTitlePrefix = clickedData['eventTitlePrefix'];
    DEBUG && console.log('eventTitlePrefix:', eventTitlePrefix);
    if (eventTitlePrefix === '✓') {
        // Remove leading "Tentative: ".
        calendarEventTitle = calendarEventTitle.replace(/^Tentative: /, '');
    }

    DEBUG && console.log(' after calendarEventTitle:', calendarEventTitle);

    // "✓ My Event; Dec 31, 2015; event date: Jan 1, 2016"
    // "1. My Event"
    // "My Event"
    var newCalendarEventTitle = calendarEventTitle;
    if (eventTitlePrefix !== null) {
        newCalendarEventTitle = eventTitlePrefix + ' ' + calendarEventTitle;
        if (! PRIORITY_EVENT_TITLE_PREFIXES.includes(eventTitlePrefix)) {
            newCalendarEventTitle += ';' +
                ' ' + todayFormattedDate + ';' +
                ' event date: ' + eventDate;
        }
    }
    DEBUG && console.log('newCalendarEventTitle:', newCalendarEventTitle);
    eventTitle.value = newCalendarEventTitle;

    setTimeout(function() {
        dispatchEvent(eventTitle, 'input');
    }, 200);

    var saveButton = document.querySelector('[aria-label="Save"]');
    setTimeout(function() {
        saveButton.click();
    }, 700);

    DEBUG && console.groupEnd();
}

function handleKeyEvent(event) {
    DEBUG && console.info('handleKeyEvent');
    var character = String.fromCharCode(event.which);
    if (!event.shiftKey) {
        character = character.toLowerCase();
    }
    if (event.which === KEYCODE.DASH) {
        character = '-';
    }
    DEBUG && console.log('character:', character);

    // Ignore events with modifiers.
    if (event.shiftKey) {
        return;
    }
    if (event.altKey) {
        return;
    }
    if (event.ctrlKey) {
        return;
    }
    if (event.metaKey) {
        return;
    }

    // Take requested action when event bubble is open and a keyboard shortcut matching the key pressed is found.
    var eventBubble = document.querySelector('#xDetDlg[data-eventid]')
    if (eventBubble && character in BUTTON_SELECTORS) {
        event.preventDefault();

        var buttonSelector = BUTTON_SELECTORS[character];
        var buttonToClick = document.querySelector(buttonSelector);
        if (buttonToClick) {
            buttonToClick.click();
        }
    }
}

document.addEventListener('keydown', handleKeyEvent);

var buttonClickedData;
(function(){
    var pathname = window.location.pathname;
    setInterval(function() {
        if (pathname !== window.location.pathname) {
            DEBUG && console.log('pathname changed');
            pathname = window.location.pathname;
            if (pathname.match(/^\/calendar\/r\/eventedit\//)) {
                DEBUG && console.log('on event edit page');

                if (buttonClickedData) {
                    clickButton(buttonClickedData);
                    buttonClickedData = null;
                } else {
                    // Add action buttons to calendar event edit page.
                    DEBUG && console.log('adding buttons');
                    var onclickAction = function(myButton, myEventTitlePrefix) {
                        buttonClickedData = {
                            'button': myButton,
                            'eventTitlePrefix': myEventTitlePrefix,
                        };
                        clickButton(buttonClickedData);
                        buttonClickedData = null;
                    };

                    var saveButton = document.querySelector('[aria-label="Save"]');

                    var buttonWrapper = document.createElement('div');
                    buttonWrapper.style.left = '350px';
                    buttonWrapper.style.position = 'absolute';
                    buttonWrapper.style.top = '65px';
                    buttonWrapper.style.width = '350px';
                    buttonWrapper.style.zIndex = '1';

                    insertAfter(buttonWrapper, saveButton);
                    insertButtons(buttonWrapper, onclickAction, 'inside');
                }
            }
        }
    }, 1000);
})();

document.onclick = function(event) {
    DEBUG && console.group('element clicked');
    DEBUG && console.log('element clicked:', event.target);

    var target = event.target;
    if (!target) {
        return;
    }
    var eventIdFound = false;
    for (var i = 0; i < 5; i++) {
        if (!target) {
            break;
        // Ignore event bubble.
        } else if (target.getAttribute('id') === 'xDetDlg') {
            break;
        } else if (target.getAttribute('data-eventid')) {
            eventIdFound = true;
            break;
        }
        target = target.parentElement;
    }
    DEBUG && console.log('target:', target);
    if (eventIdFound) {
        var eventId = target.getAttribute('data-eventid');
        DEBUG && console.log('event id found:', eventId);

        function modifyEventBubble(attempt) {
            attempt = attempt || 1;
            if (attempt >= 3) {
                return;
            }
            attempt += 1;

            var eventBubble = document.querySelector('#xDetDlg[data-eventid="' + eventId + '"]');
            DEBUG && console.log('eventBubble:', eventBubble);
            if (!eventBubble) {
                setTimeout(modifyEventBubble, 500, attempt);
                return;
            }

            var editEventButton = eventBubble.querySelector('[data-tooltip="Edit event"]');
            var eventEditable = editEventButton ? true : false;
            if (!eventEditable) {
                return;
            }

            var lastEventBubbleMetaItem = document.querySelector('#xDtlDlgCt > div:last-child');

            var newEventBubbleMetaItem = document.createElement('div');
            newEventBubbleMetaItem.classList.add(...lastEventBubbleMetaItem.classList);
            insertAfter(newEventBubbleMetaItem, lastEventBubbleMetaItem);

            var lastNewEventBubbleMetaItem = document.createElement('div');
            lastNewEventBubbleMetaItem.classList.add(...lastEventBubbleMetaItem.classList);
            lastNewEventBubbleMetaItem.classList.add('_gcw-last-event-bubble-meta');
            insertAfter(lastNewEventBubbleMetaItem, newEventBubbleMetaItem);

            var onclickAction = function(myButton, myEventTitlePrefix) {
                buttonClickedData = {
                    'button': myButton,
                    'eventTitlePrefix': myEventTitlePrefix,
                };

                var editEventButton = document.querySelector('[aria-label="Edit event"]');
                DEBUG && console.log('editEventButton:', editEventButton);
                editEventButton.click();
            };
            insertButtons(newEventBubbleMetaItem, onclickAction, 'inside', lastNewEventBubbleMetaItem);
        }
        setTimeout(modifyEventBubble, 100);
    }
    DEBUG && console.groupEnd();
};
<|endoftext|>


<commit_before>/**
 * Loads media, hooking an optional onload function.
 *
 * @param {String | NodeList | Element} [media]
 *        String - (optional) A CSS selector targeting the noscript elements to be loaded.
 *        NodeList - (optional) A nodelist of noscript elements to be loaded.
 *        Element - (optional) A singular noscript element to be loaded.
 *        If not defined, targets every 'noscript' element on the page.
 * @param {Function} - [onload] - (optional) The onload function fired for each targeted element.
 */
function loadMedia(media, onload) {

   'use strict';

   /**
    * Creates a new element based of noscript's contents.
    * Hooks the onload function.
    * Replaces the noscript element with the created element.
    *
    * @param {String | NodeList | Element} [media]
    *        String - (optional) A CSS selector targeting the noscript elements to be loaded.
    *        NodeList - (optional) A nodelist of noscript elements to be loaded.
    *        Element - (optional) A singular noscript element to be loaded.
    *        If not defined, targets every 'noscript' element on the page.
    * @param {Function} - [onload] - (optional) The onload function fired for each targeted element.
    */
   function replaceNoscript(media, onload) {
      if (media === undefined) {
         media = document.querySelectorAll('noscript');
      } else if (media instanceof Element) {
         media = [media];
      } else if (typeof media === 'string') {
         media = document.querySelectorAll(media);
      }

      for (var i = 0, noscript, img; i < media.length; i++) {
         (img = document.createElement('div')).innerHTML = (noscript = media[i]).textContent || (noscript = media[i]).innerHTML;
         img.firstChild.onload = onload;
         noscript.parentElement.replaceChild(img.firstChild, noscript);
      }
   }

   /**
    * Fires replaceNoscript either on DOMContentLoaded or after
    *
    * @see https://gist.github.com/tvler/8fd53d11ed775ebc72419bb5d96b8696
    * @author tvler
    */
    var onwheneva = (function() {
      replaceNoscript(media, onload);
    });

   if (document.readyState !== 'loading') {
      onwheneva();
   } else {
      document.addEventListener('DOMContentLoaded', onwheneva);
   }
}
<commit_msg>just use innerHTML for brevity<commit_after>/**
 * Loads media, hooking an optional onload function.
 *
 * @param {String | NodeList | Element} [media]
 *        String - (optional) A CSS selector targeting the noscript elements to be loaded.
 *        NodeList - (optional) A nodelist of noscript elements to be loaded.
 *        Element - (optional) A singular noscript element to be loaded.
 *        If not defined, targets every 'noscript' element on the page.
 * @param {Function} - [onload] - (optional) The onload function fired for each targeted element.
 */
function loadMedia(media, onload) {

   'use strict';

   /**
    * Creates a new element based of noscript's contents.
    * Hooks the onload function.
    * Replaces the noscript element with the created element.
    *
    * @param {String | NodeList | Element} [media]
    *        String - (optional) A CSS selector targeting the noscript elements to be loaded.
    *        NodeList - (optional) A nodelist of noscript elements to be loaded.
    *        Element - (optional) A singular noscript element to be loaded.
    *        If not defined, targets every 'noscript' element on the page.
    * @param {Function} - [onload] - (optional) The onload function fired for each targeted element.
    */
   function replaceNoscript(media, onload) {
      if (media === undefined) {
         media = document.querySelectorAll('noscript');
      } else if (media instanceof Element) {
         media = [media];
      } else if (typeof media === 'string') {
         media = document.querySelectorAll(media);
      }

      for (var i = 0, noscript, img; i < media.length; i++) {
         (img = document.createElement('div')).innerHTML = (noscript = media[i]).innerHTML;
         img.firstChild.onload = onload;
         noscript.parentElement.replaceChild(img.firstChild, noscript);
      }
   }

   /**
    * Fires replaceNoscript either on DOMContentLoaded or after
    *
    * @see https://gist.github.com/tvler/8fd53d11ed775ebc72419bb5d96b8696
    * @author tvler
    */
    var onwheneva = (function() {
      replaceNoscript(media, onload);
    });

   if (document.readyState !== 'loading') {
      onwheneva();
   } else {
      document.addEventListener('DOMContentLoaded', onwheneva);
   }
}
<|endoftext|>


<commit_before>/**
 * @fileOverview Contacts (Address book).
 */
'use strict';

define(['model/contacts'], function(Contacts) {
  describe('Contacts model', function() {

    var contacts, satoshiForest, _store, satoshiForestNew;
    var store = {
      init: function(key, value) {
        return value;
      },
      save: function() {
        _store = [];
        for(var i = 0; i < contacts.contacts.length; i++) {
          _store[i] = contacts.contacts[i].name;
        }
      }
    };

    beforeEach(function() {
      contacts = new Contacts(store);
      satoshiForestNew = {name: 'Satoshi Forest', address: '1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd'},
      satoshiForest = {name: 'Satoshi Forest', mainKey: 0, pubKeys: [{data: '1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd', type: 'address', address: '1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd'}]};
      _store = [];
    });

    it('is created properly', function() {
      expect(contacts.store).toBe(store);
      expect(contacts.contacts).toEqual([]);
    });

    it('adds contact', function() {
      contacts.addContact(satoshiForestNew);
      expect(contacts.contacts.length).toBe(1);
      expect(satoshiForestNew.hash).toBe('ca308ce5eeda89f8a7607f4a3106eb4a3a52eddf84933b03afb8e1bc0799ecf3');
      expect(_store).toContain('Satoshi Forest');
    });

     it('finds a contact', function() {
      var libBitcoin =  {name: 'libbitcoin', address: '04cf2e5b02d6f02340f5a9defbbf710c388b8451c82145b1419fe9696837b1cdefc569a2a79baa6da2f747c3b25a102a081dfd5e799abc41262103e0d17114770b'};
      var libBitcoin2 =  {name: 'libbitcoin2', address: '04cf2e5b02d6f02340f5a9defbbf710c388b8451c82145b1419fe9696837b1cdefc569a2a79baa6da2f747c3b25a102a081dfd5e799abc41262103e0d17114770b'};
      var libBitcoinPub = [4,207,46,91,2,214,240,35,64,245,169,222,251,191,113,12,56,139,132,81,200,
                           33,69,177,65,159,233,105,104,55,177,205,239,197,105,162,167,155,170,109,162,
                           247,71,195,178,90,16,42,8,29,253,94,121,154,188,65,38,33,3,224,209,113,20,119,11];
      contacts.addContact(libBitcoin2);
      contacts.addContact(libBitcoin);
      // this is to trigger an error that should not break the search
      contacts.contacts[0].pubKeys[0].data = 'foo';
      var libBitcoinShort = [3,207,46,91,2,214,240,35,64,245,169,222,251,191,113,12,56,139,132,81,200,33,69,177,65,159,233,105,104,55,177,205,239];

      var contact = contacts.findByPubKey(libBitcoinPub);

      expect(contact.name).toEqual(libBitcoin.name)
      expect(contact.address).toEqual(libBitcoin.address)

      contact = contacts.findByPubKey(libBitcoinShort);

      expect(contact.name).toEqual(libBitcoin.name)
      expect(contact.address).toEqual(libBitcoin.address)
    });

    // it('initializes contacts'); // TODO Delete it in DarkWallet 1.0

    it('generates a contact hash', function() {
      var data = '04cf2e5b02d6f02340f5a9defbbf710c388b8451c82145b1419fe9696837b1cdefc569a2a79baa6da2f747c3b25a102a081dfd5e799abc41262103e0d17114770b';
      expect(contacts.generateContactHash(data)).toBe("1e3e2b5cb607bcd53de71e3b2c62dc165cf9ddae9c22b894e3f23166f29b9130");
    });

    it('throws updating a wrong index', function() {
      contacts.addContact(satoshiForestNew);
      contacts.contacts[0].pubKeys = [];
      expect(function() {
         contacts.updateKey(contacts.contacts[0], data, 3);
      }).toThrow();
    });
    it('updates contact hash', function() {
      contacts.updateContactHash(satoshiForest);
      expect(satoshiForest.hash).toBe('ca308ce5eeda89f8a7607f4a3106eb4a3a52eddf84933b03afb8e1bc0799ecf3');
    });

    it('prepares an address', function() {
      var data = '04cf2e5b02d6f02340f5a9defbbf710c388b8451c82145b1419fe9696837b1cdefc569a2a79baa6da2f747c3b25a102a081dfd5e799abc41262103e0d17114770b';
      var key = contacts.prepareAddress(data);
      expect(key.data).toBe(data);
      expect(key.address).toBe('1C1vwLbunGupvKUMhgrhkFnUTZcGe8wpH2');
      expect(key.pubKey).toEqual([3,207,46,91,2,214,240,35,64,245,169,222,251,191,113,12,56,139,132,81,200,33,69,177,65,159,233,105,104,55,177,205,239]);
      expect(key.type).toBe('pubkey');

    });

    it('prepares a stealth address', function() {
      var data = '6aeUL6NvtpdchLf3UWkWsyqQZR4mwCAephsUrp5nhLDAN2kRcUMVgjdettWEd5DgXWM6tUBHe5vpMfS5L5iu3ChxK3uthRceSobvxYa';
      var key = contacts.prepareAddress(data);
      expect(key.data).toBe(data);
      expect(key.address).toBe(data);
      expect(key.pubKey).toEqual([3,6,43,123,39,102,203,84,146,220,178,28,26,66,64,231,94,38,228,218,231,209,238,160,62,154,123,54,79,90,109,5,168]);
      expect(key.type).toBe('stealth');

    });

    it('updates contact', function() {
      contacts.addContact(satoshiForestNew);
      var forest = contacts.contacts[0];
      forest.name = "Sean's Outpost";
      contacts.updateContact(forest, 'PSZVWeaa8shwLVGSCo9WZrM8zGtdJuFsBW', 0);
      expect(forest.pubKeys[forest.mainKey].address).toBe('PSZVWeaa8shwLVGSCo9WZrM8zGtdJuFsBW');
      expect(forest.hash).toBe('c410cf311c6cda613d9b494164b304fa1dc413494dcc045477c65008361c47d8');
      expect(_store).toContain("Sean's Outpost");

      expect(function() {
        contacts.updateContact({name: 'Dorian', address: 'unknown'});
      }).toThrow();
    });

    it('deletes contact', function() {
      contacts.addContact(satoshiForestNew);
      var forest = contacts.contacts[0];
      contacts.deleteContact(forest);
      expect(contacts.contacts).not.toContain(forest);
      expect(_store).not.toContain('Satoshi Forest');

      // now try when it's not there any more
      expect(function() {
        contacts.deleteContact(satoshiForest);
      }).toThrow();

    });
  });

  /**
   * Model upgrade original version, just wipes out the store
   */
  describe('Model upgrade 1', function() {
    var contacts;
    var store = {
      init: function(key, value) {
        if (key == 'contacts') {
            return {'foo': 'bar'};
        }
        return value;
      },
      save: function() {
      },
      set: function() {
      }
    };

    beforeEach(function() {
      contacts = new Contacts(store);
    });

    it('it upgrades from array properly', function() {
      expect(contacts.store).toBe(store);
      expect(contacts.contacts).toEqual([]);
    });

  });

  /**
   * Model upgrade from single address to array of address/keys.
   */
  describe('Model upgrade 2', function() {
    var contacts;
    var satoshiForestNew = {name: 'Satoshi Forest', address: '1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd'};
    var satoshiForest = {name: 'Satoshi Forest', mainKey: 0, pubKeys: [{data: '1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd', type: 'address', address: '1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd'}]};
    var store = {
      init: function(key, value) {
        if (key == 'contacts') {
            return [satoshiForestNew, undefined];
        }
        return value;
      },
      save: function() {
      },
      set: function() {
      }
    };

    beforeEach(function() {
      contacts = new Contacts(store);
    });

    it('it upgrades from array properly', function() {
      expect(contacts.store).toBe(store);
      expect(contacts.contacts).toEqual([satoshiForestNew]);
    });

  });

});
<commit_msg>fix contacts unit tests for changes in how main address is derived.<commit_after>/**
 * @fileOverview Contacts (Address book).
 */
'use strict';

define(['model/contacts'], function(Contacts) {
  describe('Contacts model', function() {

    var contacts, satoshiForest, _store, satoshiForestNew;
    var store = {
      init: function(key, value) {
        return value;
      },
      save: function() {
        _store = [];
        for(var i = 0; i < contacts.contacts.length; i++) {
          _store[i] = contacts.contacts[i].name;
        }
      }
    };

    beforeEach(function() {
      contacts = new Contacts(store);
      satoshiForestNew = {name: 'Satoshi Forest', address: '1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd'},
      satoshiForest = {name: 'Satoshi Forest', mainKey: 0, pubKeys: [{data: '1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd', type: 'address', address: '1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd'}]};
      _store = [];
    });

    it('is created properly', function() {
      expect(contacts.store).toBe(store);
      expect(contacts.contacts).toEqual([]);
    });

    it('adds contact', function() {
      contacts.addContact(satoshiForestNew);
      expect(contacts.contacts.length).toBe(1);
      expect(satoshiForestNew.hash).toBe('ca308ce5eeda89f8a7607f4a3106eb4a3a52eddf84933b03afb8e1bc0799ecf3');
      expect(_store).toContain('Satoshi Forest');
    });

     it('finds a contact', function() {
      var libBitcoin =  {name: 'libbitcoin', address: '04cf2e5b02d6f02340f5a9defbbf710c388b8451c82145b1419fe9696837b1cdefc569a2a79baa6da2f747c3b25a102a081dfd5e799abc41262103e0d17114770b'};
      var libBitcoin2 =  {name: 'libbitcoin2', address: '04cf2e5b02d6f02340f5a9defbbf710c388b8451c82145b1419fe9696837b1cdefc569a2a79baa6da2f747c3b25a102a081dfd5e799abc41262103e0d17114770b'};
      var libBitcoinPub = [4,207,46,91,2,214,240,35,64,245,169,222,251,191,113,12,56,139,132,81,200,
                           33,69,177,65,159,233,105,104,55,177,205,239,197,105,162,167,155,170,109,162,
                           247,71,195,178,90,16,42,8,29,253,94,121,154,188,65,38,33,3,224,209,113,20,119,11];
      contacts.addContact(libBitcoin2);
      contacts.addContact(libBitcoin);
      // this is to trigger an error that should not break the search
      contacts.contacts[0].pubKeys[0].data = 'foo';
      var libBitcoinShort = [3,207,46,91,2,214,240,35,64,245,169,222,251,191,113,12,56,139,132,81,200,33,69,177,65,159,233,105,104,55,177,205,239];

      var contact = contacts.findByPubKey(libBitcoinPub);

      expect(contact.name).toEqual(libBitcoin.name)
      expect(contact.address).toEqual(libBitcoin.address)

      contact = contacts.findByPubKey(libBitcoinShort);

      expect(contact.name).toEqual(libBitcoin.name)
      expect(contact.address).toEqual(libBitcoin.address)
    });

    // it('initializes contacts'); // TODO Delete it in DarkWallet 1.0

    it('generates a contact hash', function() {
      var data = '04cf2e5b02d6f02340f5a9defbbf710c388b8451c82145b1419fe9696837b1cdefc569a2a79baa6da2f747c3b25a102a081dfd5e799abc41262103e0d17114770b';
      expect(contacts.generateContactHash(data)).toBe("8f22baa6aeb2005c90187e52bedbf2201872bf225d247dc1c09541de2c393de0");
    });

    it('throws updating a wrong index', function() {
      contacts.addContact(satoshiForestNew);
      contacts.contacts[0].pubKeys = [];
      expect(function() {
         contacts.updateKey(contacts.contacts[0], data, 3);
      }).toThrow();
    });
    it('updates contact hash', function() {
      contacts.updateContactHash(satoshiForest);
      expect(satoshiForest.hash).toBe('ca308ce5eeda89f8a7607f4a3106eb4a3a52eddf84933b03afb8e1bc0799ecf3');
    });

    it('prepares an address', function() {
      var data = '04cf2e5b02d6f02340f5a9defbbf710c388b8451c82145b1419fe9696837b1cdefc569a2a79baa6da2f747c3b25a102a081dfd5e799abc41262103e0d17114770b';
      var key = contacts.prepareAddress(data);
      var libBitcoinPub = [4,207,46,91,2,214,240,35,64,245,169,222,251,191,113,12,56,139,132,81,200,
                           33,69,177,65,159,233,105,104,55,177,205,239,197,105,162,167,155,170,109,162,
                           247,71,195,178,90,16,42,8,29,253,94,121,154,188,65,38,33,3,224,209,113,20,119,11];
      expect(key.data).toBe(data);
      expect(key.address).toBe('1Fufjpf9RM2aQsGedhSpbSCGRHrmLMJ7yY');
      expect(key.pubKey).toEqual(libBitcoinPub);
      expect(key.type).toBe('pubkey');

    });

    it('prepares a stealth address', function() {
      var data = '6aeUL6NvtpdchLf3UWkWsyqQZR4mwCAephsUrp5nhLDAN2kRcUMVgjdettWEd5DgXWM6tUBHe5vpMfS5L5iu3ChxK3uthRceSobvxYa';
      var key = contacts.prepareAddress(data);
      expect(key.data).toBe(data);
      expect(key.address).toBe(data);
      expect(key.pubKey).toEqual([3,6,43,123,39,102,203,84,146,220,178,28,26,66,64,231,94,38,228,218,231,209,238,160,62,154,123,54,79,90,109,5,168]);
      expect(key.type).toBe('stealth');

    });

    it('updates contact', function() {
      contacts.addContact(satoshiForestNew);
      var forest = contacts.contacts[0];
      forest.name = "Sean's Outpost";
      contacts.updateContact(forest, 'PSZVWeaa8shwLVGSCo9WZrM8zGtdJuFsBW', 0);
      expect(forest.pubKeys[forest.mainKey].address).toBe('PSZVWeaa8shwLVGSCo9WZrM8zGtdJuFsBW');
      expect(forest.hash).toBe('c410cf311c6cda613d9b494164b304fa1dc413494dcc045477c65008361c47d8');
      expect(_store).toContain("Sean's Outpost");

      expect(function() {
        contacts.updateContact({name: 'Dorian', address: 'unknown'});
      }).toThrow();
    });

    it('deletes contact', function() {
      contacts.addContact(satoshiForestNew);
      var forest = contacts.contacts[0];
      contacts.deleteContact(forest);
      expect(contacts.contacts).not.toContain(forest);
      expect(_store).not.toContain('Satoshi Forest');

      // now try when it's not there any more
      expect(function() {
        contacts.deleteContact(satoshiForest);
      }).toThrow();

    });
  });

  /**
   * Model upgrade original version, just wipes out the store
   */
  describe('Model upgrade 1', function() {
    var contacts;
    var store = {
      init: function(key, value) {
        if (key == 'contacts') {
            return {'foo': 'bar'};
        }
        return value;
      },
      save: function() {
      },
      set: function() {
      }
    };

    beforeEach(function() {
      contacts = new Contacts(store);
    });

    it('it upgrades from array properly', function() {
      expect(contacts.store).toBe(store);
      expect(contacts.contacts).toEqual([]);
    });

  });

  /**
   * Model upgrade from single address to array of address/keys.
   */
  describe('Model upgrade 2', function() {
    var contacts;
    var satoshiForestNew = {name: 'Satoshi Forest', address: '1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd'};
    var satoshiForest = {name: 'Satoshi Forest', mainKey: 0, pubKeys: [{data: '1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd', type: 'address', address: '1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd'}]};
    var store = {
      init: function(key, value) {
        if (key == 'contacts') {
            return [satoshiForestNew];
        }
        return value;
      },
      save: function() {
      },
      set: function() {
      }
    };

    beforeEach(function() {
      contacts = new Contacts(store);
    });

    it('it upgrades from array properly', function() {
      expect(contacts.store).toBe(store);
      expect(contacts.contacts[0]).toEqual(satoshiForestNew);
    });

  });

});
<|endoftext|>


<commit_before>var _ = require('lodash'),
    expect = require('expect.js'),
    sdk = require('../../lib/index.js'),

    fixtures = require('../fixtures');

/* global describe, it */
describe('PropertyBase', function () {
    describe('meta properties', function () {
        it('should return all the meta properties', function () {
            var definition = {
                    _postman_one: { a: 'b' },
                    _postman_two: 'something',
                    _three: _.noop
                },
                base = new sdk.PropertyBase(definition);

            expect(base.meta().postman_one).to.eql(definition._postman_one);
            expect(base.meta()).to.have.property('postman_two', definition._postman_two);
            expect(base.meta()).to.have.property('three', definition._three);
        });

        it('should pick given meta properties', function () {
            var definition = {
                    _postman_one: { a: 'b' },
                    _postman_two: 'something',
                    _three: _.noop
                },
                base = new sdk.PropertyBase(definition);

            expect(base.meta('postman_one').postman_one).to.eql(definition._postman_one);
            expect(base.meta('postman_one')).to.not.have.property('postman_two');
            expect(base.meta('postman_one')).to.not.have.property('three');
        });
    });

    describe('forEachParent helper', function () {
        var collection = new sdk.Collection(fixtures.nestedCollectionV2),
            item = collection.items.members[1].items.members[0].items.members[0];

        it('should set default options correctly when none are specified', function () {
            var chain = [],
                expectedOrder = ['F2', 'F2.F3'];

            item.forEachParent(chain.unshift.bind(chain));
            expect(_.map(chain, 'name')).to.eql(expectedOrder);
        });

        it('should return the collection as well when required', function () {
            var chain = [],
                expectedOrder = ['F2', 'F2.F3'];

            item.forEachParent({ withRoot: true }, chain.unshift.bind(chain));
            expect(chain.shift().id).to.be(fixtures.nestedCollectionV2.info._postman_id);
            expect(_.map(chain, 'name')).to.eql(expectedOrder);
        });
    });
});
<commit_msg>Simplified `withRoot` checks.<commit_after>var _ = require('lodash'),
    expect = require('expect.js'),
    sdk = require('../../lib/index.js'),

    fixtures = require('../fixtures');

/* global describe, it */
describe('PropertyBase', function () {
    describe('meta properties', function () {
        it('should return all the meta properties', function () {
            var definition = {
                    _postman_one: { a: 'b' },
                    _postman_two: 'something',
                    _three: _.noop
                },
                base = new sdk.PropertyBase(definition);

            expect(base.meta().postman_one).to.eql(definition._postman_one);
            expect(base.meta()).to.have.property('postman_two', definition._postman_two);
            expect(base.meta()).to.have.property('three', definition._three);
        });

        it('should pick given meta properties', function () {
            var definition = {
                    _postman_one: { a: 'b' },
                    _postman_two: 'something',
                    _three: _.noop
                },
                base = new sdk.PropertyBase(definition);

            expect(base.meta('postman_one').postman_one).to.eql(definition._postman_one);
            expect(base.meta('postman_one')).to.not.have.property('postman_two');
            expect(base.meta('postman_one')).to.not.have.property('three');
        });
    });

    describe('forEachParent helper', function () {
        var collection = new sdk.Collection(fixtures.nestedCollectionV2),
            item = collection.items.members[1].items.members[0].items.members[0];

        it('should set default options correctly when none are specified', function () {
            var chain = [],
                expectedOrder = ['F2', 'F2.F3'];

            item.forEachParent(chain.unshift.bind(chain));
            expect(_.map(chain, 'name')).to.eql(expectedOrder);
        });

        it('should return the collection as well when required', function () {
            var chain = [],
                expectedOrder = ['multi-level-folders-v2', 'F2', 'F2.F3'];

            item.forEachParent({ withRoot: true }, chain.unshift.bind(chain));
            expect(_.map(chain, 'name')).to.eql(expectedOrder);
        });
    });
});
<|endoftext|>


<commit_before>const express = require('express');
const router = express.Router();
const fs = require('fs');
const multer = require('multer');
const upload = multer({
    dest: 'uploads/images/',
})

router.route('/')
    .post(upload.single('image'), (req, res) => {
        let base64 = req.body.base64;
        //去掉base64数据最前面的＂图片类型＂字符串
        let type = base64.match(/^data:image\/(.+);/)[1];
        base64 = base64.replace(/^data:image\/\w+;base64,/, "");
        // 解码base64成二进制数据．
        let data = new Buffer(base64, 'base64');
        const name = `uploads/images/${String(new Date()).replace(/[ :]/g,'').match(/.{6}(.{12})/)[1]}.${type}`;
        fs.open(name, "a", 0644, function(e, fd) {
            if (e) throw e;
            fs.write(fd, data, function(e) {
                if (e) throw e;
                fs.closeSync(fd);
                res.json({
                    'path': name.replace('uploads', ''),
                });
            });
        });

    });

module.exports = router;<commit_msg>把后端base64写入图片文件的部分换成了打印．测试<commit_after>const express = require('express');
const router = express.Router();
const fs = require('fs');
const multer = require('multer');
const upload = multer({
    dest: 'uploads/images/',
})

router.route('/')
    .post(upload.single('image'), (req, res) => {
        let base64 = req.body.base64;
        //去掉base64数据最前面的＂图片类型＂字符串
        let type = base64.match(/^data:image\/(.+);/)[1];
        base64 = base64.replace(/^data:image\/\w+;base64,/, "");
        // 解码base64成二进制数据．
        let data = new Buffer(base64, 'base64');
        const name = `uploads/images/${String(new Date()).replace(/[ :]/g,'').match(/.{6}(.{12})/)[1]}.${type}`;
        console.log('____________name:', name);
        console.log(base64);
        res.json({
            'path': 'temppath',
        });
        // fs.open(name, "a", 0644, function(e, fd) {
        //     if (e) throw e;
        //     fs.write(fd, data, function(e) {
        //         if (e) throw e;
        //         fs.closeSync(fd);
        //         res.json({
        //             'path': name.replace('uploads', ''),
        //         });
        //     });
        // });

    });

module.exports = router;<|endoftext|>


<commit_before>/**
 * Function to create popover help for new lexos users.
 * @returns {void}
 */
function sidebarPopover () {
  if (localStorage.getItem('visited') !== 'yes') {
    localStorage.setItem('visited', 'yes')
    $('#toggler').popover({
      'html': 'true',
      'title': 'In the Margins!',
      'content': 'View instructions for any screen in Lexos by clicking the In the Margins Tab!\n
<button type="button" id="gotit" class="btn btn-primary" style="background-color: #0068AF">Got it!</button>
'
    }).popover('show').data('bs.popover').tip().css({
      'width': '170px',
      'text-align': 'center'
    })
    $('#gotit').on('click', function () {
      $('#toggler').popover('destroy')
    })
    $('#toggler').on('click', function () {
      $('#toggler').popover('destroy')
    })
  }
}

$.fn.center = function () {
  this.css('top', Math.max(0, ((($(window).height()) - $(this).outerHeight()) / 2) - 200) + 'px')
  this.css('left', Math.max(0, (($(window).width() - $(this).outerWidth()) / 2)) + 'px')
  return this
}

$('form').attr('method', 'post')

$(document).ready(function () {
  $('#getviz').click(function (e) {
    if (numActiveDocs < 1) {
      msg = 'You have no active documents. Please activate at least one document using the Manage tool or upload a new document.'
      $('#error-modal-message').html(msg)
      $('#error-modal').modal()
      e.preventDefault()
      return false
    } else if ($("input[name='segmentlist']:checked").length < 1) {
      msg = 'You have no active documents. Please activate at least one document using the Manage tool or upload a new document.'
      $('#error-modal-message').html(msg)
      $('#error-modal').modal()
      e.preventDefault()
      return false
    }
  })
})

$(function () {
  sidebarPopover();
  // Load the Scalar API and cache it.
  /*	$.ajax({
          url: "/static/lib/scalarapi.js",
          dataType: "script",
          cache: true
      }); */

  // Initialise tooltips
	/* Note: The standard Lexos tooltip will have the following html:

	   @style may be used to adjust the size and placement of the trigger icon.
	   The default is margin-right:10px;font-size:14px;.
	*/

  $('[data-toggle="tooltip"]').tooltip()

  // Initialise popovers -- mouseleave handling from http://www.bootply.com/98529

	/* Note: The standard Lexos popover will have the following html:

	   @style may be used to give to adjust the size and placement of the trigger icon.
	   @title may be used to give the popover a header.
	*/

  $('[data-toggle=popover]').popover({
    trigger: 'manual',
    animate: false,
    html: true,
    placement: 'right',
    template:
    '
\

\

\

\

'
  }).click(function (e) {
    e.preventDefault()
  }).mouseenter(function (e) {
    $(this).popover('show')
  })
})

$(function () {
  // Handle exceptions for submitting forms and display error messages on screen
  $('form').attr('method', 'post')
  $('form').submit(function () {
    $('#num_active_files').val()
    if ($('#num_active_files').val() == '0') {
      $('#error-modal').modal()
      return false
    } else {
      return true
    }
  })

  // Add "selected" class to parent of selected link
  $('.sublist li .selected').parents('.headernavitem').addClass('selected')

  // display/hide expandable divs here
  $('.has-expansion .icon-arrow-right').click(function () {
    $(this).toggleClass('showing')

    $(this).parent('legend').siblings('.expansion').slideToggle(500)
  })

  // Gray out all disabled inputs
  $.each($('input'), function () {
    if ($(this).prop('disabled')) {
      $(this).parent('label').addClass('disabled')
    }
  })

  // Redirect all clicks on "Upload Buttons" to their file upload input
  $('.upload-bttn').click(function () {
    $(this).siblings('input[type=file]').click()
  })

  // Show the nested submenu of clustering when mouse hover over the corresponding navbar, otherwise hide the nested menu
  $('#clustering-menu, #clustering-submenu').mouseover(function () {
    $('#clustering-submenu').css({ 'opacity': 1, 'visibility': 'visible' })
  }).mouseleave(function () {
    $('#clustering-submenu').css({ 'opacity': 0, 'visibility': 'hidden' })
  })

  // Highlight the nested label of the navBar when nested pages are active
  if ($('.nestedElement').hasClass('selected')) {
    $('.nestedLabel').addClass('selected')
  }

  $(document).on('click', '#savesettings', function (event) {
    event.preventDefault()
    var form = {}
    $.each($('#settingsform').serializeArray(), function (i, field) {
      form[field.name] = field.value || ''
    })

    if ($('#beta_onbox').is(':checked')) {
      form['beta_onbox'] = true
    } else {
      $.extend(form, { 'beta_onbox': false })
    }
    $.ajax({
      'url': '/updatesettings',
      'type': 'POST',
      'contentType': 'application/json; charset=utf-8',
      'dataType': 'json',
      'data': JSON.stringify(form),
      'error': function () {
        alert('Error! Your settings could not be saved.')
      }
    }).done(function (response) {
      window.location = window.location.pathname
      // console.log("Response: "+JSON.stringify(response));
    })
  })
})

function getFormValues () {
	/* Gathers all the form values and returns them as a FormData object. In Flask,
	access form values through request.form and files through request.files. Returns
	a JSON object. */

  var formData = new FormData($('form')[0])

  $.ajax({
    url: '/previewScrubbing',
    type: 'POST',
    processData: false, // important
    contentType: false, // important
    data: formData,
    'error': function () { alert('error') }
  }).done(function (response) {
    response = JSON.parse(response)
    return response
  })
}
<commit_msg>changed to .click<commit_after>/**
 * Function to create popover help for new lexos users.
 * @returns {void}
 */
function sidebarPopover () {
  if (localStorage.getItem('visited') !== 'yes') {
    localStorage.setItem('visited', 'yes')
    $('#toggler').popover({
      'html': 'true',
      'title': 'In the Margins!',
      'content': 'View instructions for any screen in Lexos by clicking the In the Margins Tab!\n
<button type="button" id="gotit" class="btn btn-primary" style="background-color: #0068AF">Got it!</button>
'
    }).popover('show').data('bs.popover').tip().css({
      'width': '170px',
      'text-align': 'center'
    })
    $('#gotit').click(function () {
      $('#toggler').popover('destroy')
    })
    $('#toggler').click(function () {
      $('#toggler').popover('destroy')
    })
  }
}

$.fn.center = function () {
  this.css('top', Math.max(0, ((($(window).height()) - $(this).outerHeight()) / 2) - 200) + 'px')
  this.css('left', Math.max(0, (($(window).width() - $(this).outerWidth()) / 2)) + 'px')
  return this
}

$('form').attr('method', 'post')

$(document).ready(function () {
  $('#getviz').click(function (e) {
    if (numActiveDocs < 1) {
      msg = 'You have no active documents. Please activate at least one document using the Manage tool or upload a new document.'
      $('#error-modal-message').html(msg)
      $('#error-modal').modal()
      e.preventDefault()
      return false
    } else if ($("input[name='segmentlist']:checked").length < 1) {
      msg = 'You have no active documents. Please activate at least one document using the Manage tool or upload a new document.'
      $('#error-modal-message').html(msg)
      $('#error-modal').modal()
      e.preventDefault()
      return false
    }
  })
})

$(function () {
  sidebarPopover();
  // Load the Scalar API and cache it.
  /*	$.ajax({
          url: "/static/lib/scalarapi.js",
          dataType: "script",
          cache: true
      }); */

  // Initialise tooltips
	/* Note: The standard Lexos tooltip will have the following html:

	   @style may be used to adjust the size and placement of the trigger icon.
	   The default is margin-right:10px;font-size:14px;.
	*/

  $('[data-toggle="tooltip"]').tooltip()

  // Initialise popovers -- mouseleave handling from http://www.bootply.com/98529

	/* Note: The standard Lexos popover will have the following html:

	   @style may be used to give to adjust the size and placement of the trigger icon.
	   @title may be used to give the popover a header.
	*/

  $('[data-toggle=popover]').popover({
    trigger: 'manual',
    animate: false,
    html: true,
    placement: 'right',
    template:
    '
\

\

\

\

'
  }).click(function (e) {
    e.preventDefault()
  }).mouseenter(function (e) {
    $(this).popover('show')
  })
})

$(function () {
  // Handle exceptions for submitting forms and display error messages on screen
  $('form').attr('method', 'post')
  $('form').submit(function () {
    $('#num_active_files').val()
    if ($('#num_active_files').val() == '0') {
      $('#error-modal').modal()
      return false
    } else {
      return true
    }
  })

  // Add "selected" class to parent of selected link
  $('.sublist li .selected').parents('.headernavitem').addClass('selected')

  // display/hide expandable divs here
  $('.has-expansion .icon-arrow-right').click(function () {
    $(this).toggleClass('showing')

    $(this).parent('legend').siblings('.expansion').slideToggle(500)
  })

  // Gray out all disabled inputs
  $.each($('input'), function () {
    if ($(this).prop('disabled')) {
      $(this).parent('label').addClass('disabled')
    }
  })

  // Redirect all clicks on "Upload Buttons" to their file upload input
  $('.upload-bttn').click(function () {
    $(this).siblings('input[type=file]').click()
  })

  // Show the nested submenu of clustering when mouse hover over the corresponding navbar, otherwise hide the nested menu
  $('#clustering-menu, #clustering-submenu').mouseover(function () {
    $('#clustering-submenu').css({ 'opacity': 1, 'visibility': 'visible' })
  }).mouseleave(function () {
    $('#clustering-submenu').css({ 'opacity': 0, 'visibility': 'hidden' })
  })

  // Highlight the nested label of the navBar when nested pages are active
  if ($('.nestedElement').hasClass('selected')) {
    $('.nestedLabel').addClass('selected')
  }

  $(document).on('click', '#savesettings', function (event) {
    event.preventDefault()
    var form = {}
    $.each($('#settingsform').serializeArray(), function (i, field) {
      form[field.name] = field.value || ''
    })

    if ($('#beta_onbox').is(':checked')) {
      form['beta_onbox'] = true
    } else {
      $.extend(form, { 'beta_onbox': false })
    }
    $.ajax({
      'url': '/updatesettings',
      'type': 'POST',
      'contentType': 'application/json; charset=utf-8',
      'dataType': 'json',
      'data': JSON.stringify(form),
      'error': function () {
        alert('Error! Your settings could not be saved.')
      }
    }).done(function (response) {
      window.location = window.location.pathname
      // console.log("Response: "+JSON.stringify(response));
    })
  })
})

function getFormValues () {
	/* Gathers all the form values and returns them as a FormData object. In Flask,
	access form values through request.form and files through request.files. Returns
	a JSON object. */

  var formData = new FormData($('form')[0])

  $.ajax({
    url: '/previewScrubbing',
    type: 'POST',
    processData: false, // important
    contentType: false, // important
    data: formData,
    'error': function () { alert('error') }
  }).done(function (response) {
    response = JSON.parse(response)
    return response
  })
}
<|endoftext|>