<commit_before>L.ObjectsLayer = L.GeoJSON.extend({
    options: {
        indexing: true,
        highlight: true,
        objectUrl: null,
        styles: {
            default: {'color': 'blue', 'weight': 2, 'opacity': 0.8},
            highlight: {'color': 'red', 'weight': 5, 'opacity': 1},
            select: {'color': 'red', 'weight': 7, 'opacity': 1},
        }
    },

    includes: L.Mixin.Events,

    initialize: function (geojson, options) {
        // Pointers to all layers by pk - immutable
        this._objects = {};
        // Hold the currently visible layers (subset of _objects)
        this._current_objects = {};
        this.loading = false;

        var options = options || {};

        var onFeatureParse = function (geojson, layer) {
            this._mapObjects(geojson, layer);
            if (this._onEachFeature) this._onEachFeature(geojson, layer);
        };
        var pointToLayer = function (geojson, latlng) {
            if (this._pointToLayer) return this._pointToLayer(geojson, latlng);
            return new L.CircleMarker(latlng);
        };
        this._onEachFeature = options.onEachFeature;
        options.onEachFeature = L.Util.bind(onFeatureParse, this);
        this._pointToLayer = options.pointToLayer;
        options.pointToLayer = L.Util.bind(pointToLayer, this);

        options.style = options.style || this.options.styles.default;
        L.Util.setOptions(this, options);
        this.options.styles.default = options.style || this.options.styles.default;

        // Highlight on mouse over
        if (this.options.highlight) {
            this.on('mouseover', function(e) {
                this.highlight(e.layer.properties.pk);
            }, this);
            this.on('mouseout', function(e) {
                this.highlight(e.layer.properties.pk, false);
            }, this);
        }

        // Optionnaly make them clickable
        if (this.options.objectUrl) {
            this.on('dblclick', function(e) {
                window.location = this.options.objectUrl(e.layer.properties, e.layer);
            }, this);
        }

        var dataurl = null;
        if (typeof(geojson) == 'string') {
            dataurl = geojson;
            geojson = null;
        }
        L.GeoJSON.prototype.initialize.call(this, geojson, this.options);

        if (dataurl) {
            this.load(dataurl);
        }
    },

    _mapObjects: function (geojson, layer) {
        var pk = geojson.properties.pk
        this._objects[pk] = this._current_objects[pk] = layer;
        layer.properties = geojson.properties;
        this.indexLayer(layer);
    },

    load: function (url, force) {
        if (!!force && url.indexOf("?") != -1) {
            url += '?_u=' + (new Date().getTime());
        }
        var jsonLoad = function (data) {
            var features = jQuery.grep(data.features, function(obj, i) {
                return obj.geometry != null;
            });
            data.features = features;
            this.addData(data);
            this.loading = false;
            this.fire('loaded');
        };
        var jsonError = function () {
            this.loading = false;
            this.fire('loaded');
            console.error("Could not load url '" + url + "'");
            if (this._map) $(this._map._container).addClass('map-error');
        };
        this.loading = true;
        this.fire('loading');
        $.getJSON(url, L.Util.bind(jsonLoad, this))
         .error(L.Util.bind(jsonError, this));
    },

    getLayer: function (pk) {
        return this._objects[pk];
    },

    getPk: function(layer) {
        return layer.properties && layer.properties.pk;
    },

    // Show all layers matching the pks
    updateFromPks: function(pks) {
        var self = this
          , new_objects = {}
          , already_added_layer
          , to_add_layer
        ;

        // Gather all layer to see in new objects
        // Remove them from _current_objects if they are already shown
        // This way _current_objects will only contain layer to be removed
        $.each(pks, function(idx, to_add_pk) {
            already_added_layer = self._current_objects[to_add_pk];
            if (already_added_layer) {
                new_objects[to_add_pk] = already_added_layer;
                delete self._current_objects[to_add_pk]
            } else {
                to_add_layer = new_objects[to_add_pk] = self._objects[to_add_pk];
                // list can be ready before map, on first load
                if (to_add_layer) self.addLayer(to_add_layer);
            }
        });

        // Remove all remaining layer
        $.each(self._current_objects, function(pk, layer) {
            self.removeLayer(layer);
        });

        self._current_objects = new_objects;
    },

    jumpTo: function (pk) {
        var layer = this.getLayer(pk);
        if (!layer) return;
        this._map.fitBounds(layer.getBounds());
    },

    highlight: function (pk, on) {
        var on = on === undefined ? true : on,
            layer = this.getLayer(pk);
        if (!layer) return;

        if (on) {
            layer._defaultStyle = layer._defaultStyle ? layer._defaultStyle : this.options.styles.default;
            layer.setStyle(this.options.styles.highlight);
            this.fire('highlight', {layer: layer});
        }
        else {
            layer.setStyle(layer._defaultStyle);
        }
    },

    select: function(pk, on) {
        var on = on === undefined ? true : on,
            layer = this.getLayer(pk);
        if (!layer) return;

        if (on) {
            layer._defaultStyle = this.options.styles.select;
            layer.setStyle(layer._defaultStyle);
            this.fire('select', {layer: layer});
        }
        else {
            layer._defaultStyle = this.options.styles.default;
            layer.setStyle(layer._defaultStyle);
        }
    }
});

L.ObjectsLayer.include(L.LayerIndexMixin);<commit_msg>Show spin during topology graph loading (ref #575)<commit_after>L.ObjectsLayer = L.GeoJSON.extend({
    options: {
        indexing: true,
        highlight: true,
        objectUrl: null,
        styles: {
            default: {'color': 'blue', 'weight': 2, 'opacity': 0.8},
            highlight: {'color': 'red', 'weight': 5, 'opacity': 1},
            select: {'color': 'red', 'weight': 7, 'opacity': 1},
        }
    },

    includes: L.Mixin.Events,

    initialize: function (geojson, options) {
        // Pointers to all layers by pk - immutable
        this._objects = {};
        // Hold the currently visible layers (subset of _objects)
        this._current_objects = {};
        this.loading = false;

        var options = options || {};

        var onFeatureParse = function (geojson, layer) {
            this._mapObjects(geojson, layer);
            if (this._onEachFeature) this._onEachFeature(geojson, layer);
        };
        var pointToLayer = function (geojson, latlng) {
            if (this._pointToLayer) return this._pointToLayer(geojson, latlng);
            return new L.CircleMarker(latlng);
        };
        this._onEachFeature = options.onEachFeature;
        options.onEachFeature = L.Util.bind(onFeatureParse, this);
        this._pointToLayer = options.pointToLayer;
        options.pointToLayer = L.Util.bind(pointToLayer, this);

        options.style = options.style || this.options.styles.default;
        L.Util.setOptions(this, options);
        this.options.styles.default = options.style || this.options.styles.default;

        // Highlight on mouse over
        if (this.options.highlight) {
            this.on('mouseover', function(e) {
                this.highlight(e.layer.properties.pk);
            }, this);
            this.on('mouseout', function(e) {
                this.highlight(e.layer.properties.pk, false);
            }, this);
        }

        // Optionnaly make them clickable
        if (this.options.objectUrl) {
            this.on('dblclick', function(e) {
                window.location = this.options.objectUrl(e.layer.properties, e.layer);
            }, this);
        }

        var dataurl = null;
        if (typeof(geojson) == 'string') {
            dataurl = geojson;
            geojson = null;
        }
        L.GeoJSON.prototype.initialize.call(this, geojson, this.options);

        // Fire Leaflet.Spin events
        this.on('loaded loading', function (e) {
            this.fire('data:' + e.type);
        }, this);

        if (dataurl) {
            this.load(dataurl);
        }
    },

    _mapObjects: function (geojson, layer) {
        var pk = geojson.properties.pk
        this._objects[pk] = this._current_objects[pk] = layer;
        layer.properties = geojson.properties;
        this.indexLayer(layer);
    },

    load: function (url, force) {
        if (!!force && url.indexOf("?") != -1) {
            url += '?_u=' + (new Date().getTime());
        }
        var jsonLoad = function (data) {
            var features = jQuery.grep(data.features, function(obj, i) {
                return obj.geometry != null;
            });
            data.features = features;
            this.addData(data);
            this.loading = false;
            this.fire('loaded');
        };
        var jsonError = function () {
            this.loading = false;
            this.fire('loaded');
            console.error("Could not load url '" + url + "'");
            if (this._map) $(this._map._container).addClass('map-error');
        };
        this.loading = true;
        this.fire('loading');
        $.getJSON(url, L.Util.bind(jsonLoad, this))
         .error(L.Util.bind(jsonError, this));
    },

    getLayer: function (pk) {
        return this._objects[pk];
    },

    getPk: function(layer) {
        return layer.properties && layer.properties.pk;
    },

    // Show all layers matching the pks
    updateFromPks: function(pks) {
        var self = this
          , new_objects = {}
          , already_added_layer
          , to_add_layer
        ;

        // Gather all layer to see in new objects
        // Remove them from _current_objects if they are already shown
        // This way _current_objects will only contain layer to be removed
        $.each(pks, function(idx, to_add_pk) {
            already_added_layer = self._current_objects[to_add_pk];
            if (already_added_layer) {
                new_objects[to_add_pk] = already_added_layer;
                delete self._current_objects[to_add_pk]
            } else {
                to_add_layer = new_objects[to_add_pk] = self._objects[to_add_pk];
                // list can be ready before map, on first load
                if (to_add_layer) self.addLayer(to_add_layer);
            }
        });

        // Remove all remaining layer
        $.each(self._current_objects, function(pk, layer) {
            self.removeLayer(layer);
        });

        self._current_objects = new_objects;
    },

    jumpTo: function (pk) {
        var layer = this.getLayer(pk);
        if (!layer) return;
        this._map.fitBounds(layer.getBounds());
    },

    highlight: function (pk, on) {
        var on = on === undefined ? true : on,
            layer = this.getLayer(pk);
        if (!layer) return;

        if (on) {
            layer._defaultStyle = layer._defaultStyle ? layer._defaultStyle : this.options.styles.default;
            layer.setStyle(this.options.styles.highlight);
            this.fire('highlight', {layer: layer});
        }
        else {
            layer.setStyle(layer._defaultStyle);
        }
    },

    select: function(pk, on) {
        var on = on === undefined ? true : on,
            layer = this.getLayer(pk);
        if (!layer) return;

        if (on) {
            layer._defaultStyle = this.options.styles.select;
            layer.setStyle(layer._defaultStyle);
            this.fire('select', {layer: layer});
        }
        else {
            layer._defaultStyle = this.options.styles.default;
            layer.setStyle(layer._defaultStyle);
        }
    }
});

L.ObjectsLayer.include(L.LayerIndexMixin);<|endoftext|>


<commit_before>import Discord from 'discord.js'
import Fuse from 'fuse.js'
import HearthstoneJSON from 'hearthstonejson'

import toMarkdown from 'to-markdown'

// RGB
const CLASS_COLORS = {
    'WARRIOR': [196, 30, 59],
    'SHAMAN': [0, 122, 222],
    'ROGUE': [0, 0, 0],
    'PALADIN': [255, 245, 105],
    'HUNTER': [171, 212, 115],
    'DRUID': [199, 156, 110],
    'WARLOCK': [148, 130, 201],
    'MAGE': [105, 204, 240],
    'PRIEST': [255, 255, 255],
    'NEUTRAL': [128, 128, 128]
}

export default class CardsHelper {
    static getAll() {
        const hsjson = new HearthstoneJSON()
        return new Promise((resolve, reject) => {
            try {
                hsjson.getLatest(cards => { resolve(cards) })
            } catch (ex) {
                reject(ex)
            }
        })
    }

    static detectMentions(text) {
        let pattern = /:{2}([^:\?]+)\??([^:]*):{2}/g
        let matches = []
        let match = pattern.exec(text)
        while (match) {
            matches.push(match)
            match = pattern.exec(text)
        }
        return matches
    }

    static async search(pattern) {
        let uncollectibleOnly = false
        if (pattern.startsWith('@')) {
            pattern = pattern.substring(1)
            uncollectibleOnly = true
        }
        let allCards = await this.getAll().catch(console.error)

        let uncollectibleFuse = new Fuse(
            allCards.filter(card => { return !card.collectible }),
            { keys: ['name'], include: ['score'] }
        )
        let foundUncollectible = uncollectibleFuse.search(pattern)
        if (uncollectibleOnly) { return foundUncollectible[0].item }

        let collectibleFuse = new Fuse(
            allCards.filter(card => { return card.collectible }),
            { keys: ['name'], include: ['score'] }
        )
        let foundCollectible = collectibleFuse.search(pattern)
        if (foundUncollectible[0].score < foundCollectible[0].score) { return foundUncollectible[0].item }
        return foundCollectible[0].item
    }

    static getTextOutput(card, addon) {
        if (addon === 'image') {
            return `http://media.services.zam.com/v1/media/byName/hs/cards/enus/${card.id}.png`
        }
        if (addon === 'gold') {
            return `http://media.services.zam.com/v1/media/byName/hs/cards/enus/animated/${card.id}_premium.gif`
        }
        if (addon === 'art') {
            return `https://art.hearthstonejson.com/v1/512x/${card.id}.jpg`
        }

        let result = `${card.name} -`
        if (card.cost) { result += ` ${card.cost} Mana` }
        if (card.attack) { result += ` ${card.attack}/${card.health || card.durability}` }
        result += ` ${card.playerClass.toLowerCase().capitalizeFirstLetter()}`
        result += ` ${card.type.toLowerCase().capitalizeFirstLetter()}`
        if (card.collectionText) { result += `\n${toMarkdown(card.collectionText)}` }
        else if (card.text) { result += `\n${toMarkdown(card.text)}` }
        if (addon === 'flavor' && card.flavor) { result += `\n${card.flavor}` }
        return result
    }

    static getEmbedObject(card, addon) {
        let res = new Discord.RichEmbed()
        if (addon === 'image') {
            return res.setImage(`http://media.services.zam.com/v1/media/byName/hs/cards/enus/${card.id}.png`)
        }
        if (addon === 'gold') {
            return res.setImage(`http://media.services.zam.com/v1/media/byName/hs/cards/enus/animated/${card.id}_premium.gif`)
        }
        if (addon === 'art') { res.setImage(`https://art.hearthstonejson.com/v1/512x/${card.id}.jpg`) }

        res.setTitle(card.name).setColor(CLASS_COLORS[card.playerClass])

        let desc = ''
        if (card.cost != null) { desc += ` ${card.cost} Mana` }
        if (card.attack) { desc += ` ${card.attack}/${card.health || card.durability}` }
        desc += ` ${card.playerClass.toLowerCase().capitalizeFirstLetter()}`
        desc += ` ${card.type.toLowerCase().capitalizeFirstLetter()}`
        res.setDescription(desc)

        if (card.collectionText) { res.addField('Text', toMarkdown(card.collectionText)) }
        else if (card.text) { res.addField('Text', toMarkdown(card.text)) }
        if (addon === 'flavor' && card.flavor) { res.addField('Flavor', card.flavor) }

        return res
    }
}

String.prototype.capitalizeFirstLetter = function() {
    return this.charAt(0).toUpperCase() + this.slice(1)
}
<commit_msg>Account for no matches in either collection<commit_after>import Discord from 'discord.js'
import Fuse from 'fuse.js'
import HearthstoneJSON from 'hearthstonejson'

import toMarkdown from 'to-markdown'

// RGB
const CLASS_COLORS = {
    'WARRIOR': [196, 30, 59],
    'SHAMAN': [0, 122, 222],
    'ROGUE': [0, 0, 0],
    'PALADIN': [255, 245, 105],
    'HUNTER': [171, 212, 115],
    'DRUID': [199, 156, 110],
    'WARLOCK': [148, 130, 201],
    'MAGE': [105, 204, 240],
    'PRIEST': [255, 255, 255],
    'NEUTRAL': [128, 128, 128]
}

export default class CardsHelper {
    static getAll() {
        const hsjson = new HearthstoneJSON()
        return new Promise((resolve, reject) => {
            try {
                hsjson.getLatest(cards => { resolve(cards) })
            } catch (ex) {
                reject(ex)
            }
        })
    }

    static detectMentions(text) {
        let pattern = /:{2}([^:\?]+)\??([^:]*):{2}/g
        let matches = []
        let match = pattern.exec(text)
        while (match) {
            matches.push(match)
            match = pattern.exec(text)
        }
        return matches
    }

    static async search(pattern) {
        let uncollectibleOnly = false
        if (pattern.startsWith('@')) {
            pattern = pattern.substring(1)
            uncollectibleOnly = true
        }
        let allCards = await this.getAll().catch(console.error)

        let uncollectibleFuse = new Fuse(
            allCards.filter(card => { return !card.collectible }),
            { keys: ['name'], include: ['score'] }
        )
        let foundUncollectible = uncollectibleFuse.search(pattern)
        if (uncollectibleOnly) { return foundUncollectible[0].item }

        let collectibleFuse = new Fuse(
            allCards.filter(card => { return card.collectible }),
            { keys: ['name'], include: ['score'] }
        )
        let foundCollectible = collectibleFuse.search(pattern)

        if (foundCollectible.length < 1 && foundUncollectible.length > 0) { return foundUncollectible[0].item }
        if (foundCollectible.length > 0 && foundUncollectible.length < 1) { return foundCollectible[0].item }
        if (foundUncollectible[0].score < foundCollectible[0].score) { return foundUncollectible[0].item }
        return foundCollectible[0].item
    }

    static getTextOutput(card, addon) {
        if (addon === 'image') {
            return `http://media.services.zam.com/v1/media/byName/hs/cards/enus/${card.id}.png`
        }
        if (addon === 'gold') {
            return `http://media.services.zam.com/v1/media/byName/hs/cards/enus/animated/${card.id}_premium.gif`
        }
        if (addon === 'art') {
            return `https://art.hearthstonejson.com/v1/512x/${card.id}.jpg`
        }

        let result = `${card.name} -`
        if (card.cost) { result += ` ${card.cost} Mana` }
        if (card.attack) { result += ` ${card.attack}/${card.health || card.durability}` }
        result += ` ${card.playerClass.toLowerCase().capitalizeFirstLetter()}`
        result += ` ${card.type.toLowerCase().capitalizeFirstLetter()}`
        if (card.collectionText) { result += `\n${toMarkdown(card.collectionText)}` }
        else if (card.text) { result += `\n${toMarkdown(card.text)}` }
        if (addon === 'flavor' && card.flavor) { result += `\n${card.flavor}` }
        return result
    }

    static getEmbedObject(card, addon) {
        let res = new Discord.RichEmbed()
        if (addon === 'image') {
            return res.setImage(`http://media.services.zam.com/v1/media/byName/hs/cards/enus/${card.id}.png`)
        }
        if (addon === 'gold') {
            return res.setImage(`http://media.services.zam.com/v1/media/byName/hs/cards/enus/animated/${card.id}_premium.gif`)
        }
        if (addon === 'art') { res.setImage(`https://art.hearthstonejson.com/v1/512x/${card.id}.jpg`) }

        res.setTitle(card.name).setColor(CLASS_COLORS[card.playerClass])

        let desc = ''
        if (card.cost != null) { desc += ` ${card.cost} Mana` }
        if (card.attack) { desc += ` ${card.attack}/${card.health || card.durability}` }
        desc += ` ${card.playerClass.toLowerCase().capitalizeFirstLetter()}`
        desc += ` ${card.type.toLowerCase().capitalizeFirstLetter()}`
        res.setDescription(desc)

        if (card.collectionText) { res.addField('Text', toMarkdown(card.collectionText)) }
        else if (card.text) { res.addField('Text', toMarkdown(card.text)) }
        if (addon === 'flavor' && card.flavor) { res.addField('Flavor', card.flavor) }

        return res
    }
}

String.prototype.capitalizeFirstLetter = function() {
    return this.charAt(0).toUpperCase() + this.slice(1)
}
<|endoftext|>


<commit_before>var command = {
  command: "migrate",
  description: "Run migrations to deploy contracts",
  builder: {
    "reset": {
      type: "boolean",
      default: false
    },
    "compile-all": {
      describe: "recompile all contracts",
      type: "boolean",
      default: false
    },
    "dry-run": {
      describe: "Run migrations against an in-memory fork, for testing",
      type: "boolean",
      default: false
    },
    "skip-dry-run": {
      describe: "Skip the test or 'dry run' migrations",
      type: "boolean",
      default: false
    },
    "f": {
      describe: "Specify a migration number to run from",
      type: "number"
    },
    "to": {
      describe: "Specify a migration number to run to",
      type: "number"
    },
    "interactive": {
      describe: "Manually authorize deployments after seeing a preview",
      type: "boolean",
      default: false
    }
  },
  help: {
    usage:
      "truffle migrate [--reset] [--f <number>] [--to <number>] " +
      "[--network <name>]\n                                [--compile-all] " +
      "[--verbose-rpc] [--interactive] [--dry-run] [--skip-dry-run]",
    options: [
      {
        option: "--reset",
        description:
          "Run all migrations from the beginning, instead of running from the last " +
          "completed migration."
      },
      {
        option: "--f <number>",
        description:
          "Run contracts from a specific migration. The number refers to the prefix of " +
          "the migration file."
      },
      {
        option: "--to <number>",
        description:
          "Run contracts to a specific migration. The number refers to the prefix of the migration file."
      },
      {
        option: "--network <name>",
        description:
          "Specify the network to use, saving artifacts specific to that network. " +
          "Network name must exist\n                    in the configuration."
      },
      {
        option: "--compile-all",
        description:
          "Compile all contracts instead of intelligently choosing which contracts need to " +
          "be compiled."
      },
      {
        option: "--verbose-rpc",
        description:
          "Log communication between Truffle and the Ethereum client."
      },
      {
        option: "--interactive",
        description:
          "Prompt to confirm that the user wants to proceed after the dry run."
      },
      {
        option: "--dry-run",
        description: "Only perform a test or 'dry run' migration."
      },
      {
        option: "--skip-dry-run",
        description: "Do not run a test or 'dry run' migration."
      }
    ]
  },
  run: function(options, done) {
    var Config = require("truffle-config");
    var Contracts = require("truffle-workflow-compile");
    var Resolver = require("truffle-resolver");
    var Artifactor = require("truffle-artifactor");
    var Migrate = require("truffle-migrate");
    var Environment = require("../environment");
    var temp = require("temp");
    var copy = require("../copy");

    // Source: ethereum.stackexchange.com/questions/17051
    var networkWhitelist = [
      1, // Mainnet (ETH & ETC)
      2, // Morden (ETC)
      3, // Ropsten
      4, // Rinkeby
      8, // Ubiq
      42, // Kovan (Parity)
      77, // Sokol
      99, // Core

      7762959, // Musiccoin
      61717561 // Aquachain
    ];

    function setupDryRunEnvironmentThenRunMigrations(config) {
      return new Promise((resolve, reject) => {
        Environment.fork(config, function(err) {
          if (err) return reject(err);

          // Copy artifacts to a temporary directory
          temp.mkdir("migrate-dry-run-", function(err, temporaryDirectory) {
            if (err) return reject(err);

            function cleanup() {
              var args = arguments;

              // Ensure directory cleanup.
              temp.cleanup(() => {
                args.length && args[0] !== null ? reject(args[0]) : resolve();
              });
            }

            copy(config.contracts_build_directory, temporaryDirectory, function(
              err
            ) {
              if (err) return cleanup(err);

              config.contracts_build_directory = temporaryDirectory;

              // Note: Create a new artifactor and resolver with the updated config.
              // This is because the contracts_build_directory changed.
              // Ideally we could architect them to be reactive of the config changes.
              config.artifactor = new Artifactor(temporaryDirectory);
              config.resolver = new Resolver(config);

              runMigrations(config, cleanup);
            });
          });
        });
      });
    }

    function runMigrations(config, callback) {
      Migrate.launchReporter();

      if (options.f) {
        Migrate.runFrom(options.f, config, done);
      } else {
        Migrate.needsMigrating(config, function(err, needsMigrating) {
          if (err) return callback(err);

          if (needsMigrating) {
            Migrate.run(config, callback);
          } else {
            config.logger.log("Network up to date.");
            callback();
          }
        });
      }
    }

    async function executePostDryRunMigration(buildDir) {
      let accept = true;

      if (options.interactive) {
        accept = await Migrate.acceptDryRun();
      }

      if (accept) {
        const environment = require("../environment");
        const NewConfig = require("truffle-config");
        const config = NewConfig.detect(options);

        config.contracts_build_directory = buildDir;
        config.artifactor = new Artifactor(buildDir);
        config.resolver = new Resolver(config);

        environment.detect(config, err => {
          if (err) return done(err);

          config.dryRun = false;
          runMigrations(config, done);
        });
      } else {
        done();
      }
    }

    const conf = Config.detect(options);

    Contracts.compile(conf, function(err) {
      if (err) return done(err);

      Environment.detect(conf, async function(err) {
        if (err) return done(err);

        const dryRunOnly = options.dryRun === true;
        const production =
          networkWhitelist.includes(parseInt(conf.network_id)) ||
          conf.production;
        const dryRunAndMigration = production && !conf.skipDryRun;

        if (dryRunOnly) {
          try {
            await setupDryRunEnvironmentThenRunMigrations(conf);
            done();
          } catch (err) {
            done(err);
          }
        } else if (dryRunAndMigration) {
          const currentBuild = conf.contracts_build_directory;
          conf.dryRun = true;

          try {
            await setupDryRunEnvironmentThenRunMigrations(conf);
          } catch (err) {
            return done(err);
          }

          executePostDryRunMigration(currentBuild);

          // Development
        } else {
          runMigrations(conf, done);
        }
      });
    });
  }
};

module.exports = command;
<commit_msg>Pull some logic out of the run method for the migrate command<commit_after>const Config = require("truffle-config");
const Migrate = require("truffle-migrate");
const Resolver = require("truffle-resolver");
const Artifactor = require("truffle-artifactor");

const command = {
  command: "migrate",
  description: "Run migrations to deploy contracts",
  builder: {
    "reset": {
      type: "boolean",
      default: false
    },
    "compile-all": {
      describe: "recompile all contracts",
      type: "boolean",
      default: false
    },
    "dry-run": {
      describe: "Run migrations against an in-memory fork, for testing",
      type: "boolean",
      default: false
    },
    "skip-dry-run": {
      describe: "Skip the test or 'dry run' migrations",
      type: "boolean",
      default: false
    },
    "f": {
      describe: "Specify a migration number to run from",
      type: "number"
    },
    "to": {
      describe: "Specify a migration number to run to",
      type: "number"
    },
    "interactive": {
      describe: "Manually authorize deployments after seeing a preview",
      type: "boolean",
      default: false
    }
  },
  help: {
    usage:
      "truffle migrate [--reset] [--f <number>] [--to <number>] " +
      "[--network <name>]\n                                [--compile-all] " +
      "[--verbose-rpc] [--interactive] [--dry-run] [--skip-dry-run]",
    options: [
      {
        option: "--reset",
        description:
          "Run all migrations from the beginning, instead of running from the last " +
          "completed migration."
      },
      {
        option: "--f <number>",
        description:
          "Run contracts from a specific migration. The number refers to the prefix of " +
          "the migration file."
      },
      {
        option: "--to <number>",
        description:
          "Run contracts to a specific migration. The number refers to the prefix of the migration file."
      },
      {
        option: "--network <name>",
        description:
          "Specify the network to use, saving artifacts specific to that network. " +
          "Network name must exist\n                    in the configuration."
      },
      {
        option: "--compile-all",
        description:
          "Compile all contracts instead of intelligently choosing which contracts need to " +
          "be compiled."
      },
      {
        option: "--verbose-rpc",
        description:
          "Log communication between Truffle and the Ethereum client."
      },
      {
        option: "--interactive",
        description:
          "Prompt to confirm that the user wants to proceed after the dry run."
      },
      {
        option: "--dry-run",
        description: "Only perform a test or 'dry run' migration."
      },
      {
        option: "--skip-dry-run",
        description: "Do not run a test or 'dry run' migration."
      }
    ]
  },

  determineDryRunSettings: (config, options) => {
    // Source: ethereum.stackexchange.com/questions/17051
    const networkWhitelist = [
      1, // Mainnet (ETH & ETC)
      2, // Morden (ETC)
      3, // Ropsten
      4, // Rinkeby
      8, // Ubiq
      42, // Kovan (Parity)
      77, // Sokol
      99, // Core

      7762959, // Musiccoin
      61717561 // Aquachain
    ];

    let dryRunOnly;
    const networkSettingsInConfig = config.networks[config.network];
    if (networkSettingsInConfig) {
      dryRunOnly =
        options.dryRun === true ||
        networkSettingsInConfig.dryRun === true ||
        networkSettingsInConfig["dry-run"] === true;
      skipDryRun =
        options.skipDryRun === true ||
        networkSettingsInConfig.skipDryRun === true ||
        networkSettingsInConfig["skip-dry-run"] === true;
    } else {
      dryRunOnly = options.dryRun === true;
      skipDryRun = options.skipDryRun === true;
    }
    const production =
      networkWhitelist.includes(parseInt(config.network_id)) ||
      config.production;
    const dryRunAndMigrations = production && !skipDryRun;
    return { dryRunOnly, dryRunAndMigrations };
  },

  executePostDryRunMigration: async function(buildDir, options, done) {
    let accept = true;

    if (options.interactive) {
      accept = await Migrate.acceptDryRun();
    }

    if (accept) {
      const environment = require("../environment");
      const config = Config.detect(options);

      config.contracts_build_directory = buildDir;
      config.artifactor = new Artifactor(buildDir);
      config.resolver = new Resolver(config);

      environment.detect(config, err => {
        if (err) return done(err);

        config.dryRun = false;
        runMigrations(config, done);
      });
    } else {
      done();
    }
  },

  run: function(options, done) {
    const Contracts = require("truffle-workflow-compile");
    const Environment = require("../environment");
    const temp = require("temp");
    const copy = require("../copy");

    const conf = Config.detect(options);

    function setupDryRunEnvironmentThenRunMigrations(config) {
      return new Promise((resolve, reject) => {
        Environment.fork(config, function(err) {
          if (err) return reject(err);

          // Copy artifacts to a temporary directory
          temp.mkdir("migrate-dry-run-", function(err, temporaryDirectory) {
            if (err) return reject(err);

            function cleanup() {
              var args = arguments;

              // Ensure directory cleanup.
              temp.cleanup(() => {
                args.length && args[0] !== null ? reject(args[0]) : resolve();
              });
            }

            copy(config.contracts_build_directory, temporaryDirectory, function(
              err
            ) {
              if (err) return cleanup(err);

              config.contracts_build_directory = temporaryDirectory;

              // Note: Create a new artifactor and resolver with the updated config.
              // This is because the contracts_build_directory changed.
              // Ideally we could architect them to be reactive of the config changes.
              config.artifactor = new Artifactor(temporaryDirectory);
              config.resolver = new Resolver(config);

              runMigrations(config, cleanup);
            });
          });
        });
      });
    }

    function runMigrations(config, callback) {
      Migrate.launchReporter();

      if (options.f) {
        Migrate.runFrom(options.f, config, done);
      } else {
        Migrate.needsMigrating(config, function(err, needsMigrating) {
          if (err) return callback(err);

          if (needsMigrating) {
            Migrate.run(config, callback);
          } else {
            config.logger.log("Network up to date.");
            callback();
          }
        });
      }
    }

    const detectCallback = async function(error) {
      if (error) return done(error);

      const { dryRunOnly, dryRunAndMigrations } = this.determineDryRunSettings(
        conf,
        options
      );

      if (dryRunOnly) {
        try {
          await setupDryRunEnvironmentThenRunMigrations(conf);
          done();
        } catch (err) {
          done(err);
        }
      } else if (dryRunAndMigrations) {
        const currentBuild = conf.contracts_build_directory;
        conf.dryRun = true;

        try {
          await setupDryRunEnvironmentThenRunMigrations(conf);
        } catch (err) {
          return done(err);
        }

        this.executePostDryRunMigration(currentBuild, options, done);

        // Development
      } else {
        runMigrations(conf, done);
      }
    };

    const compileCallback = function(error) {
      if (error) return done(error);

      Environment.detect(conf, detectCallback.bind(this));
    };

    Contracts.compile(conf, compileCallback.bind(this));
  }
};

module.exports = command;
<|endoftext|>


<commit_before>define([
    'esri/units',
    'esri/geometry/Extent',
    'esri/config',
    /*'esri/urlUtils',*/
    'esri/tasks/GeometryService',
    'esri/layers/ImageParameters'
], function (units, Extent, esriConfig, /*urlUtils,*/ GeometryService, ImageParameters) {

    // url to your proxy page, must be on same machine hosting you app. See proxy folder for readme.
    esriConfig.defaults.io.proxyUrl = 'proxy/proxy.ashx';
    esriConfig.defaults.io.alwaysUseProxy = false;

    // add a proxy rule to force specific domain requests through proxy
    // be sure the domain is added in proxy.config
    /*urlUtils.addProxyRule({
        urlPrefix: 'www.example.com',
        proxyUrl: 'proxy/proxy.ashx'
    });*/

    // url to your geometry server.
    esriConfig.defaults.geometryService = new GeometryService('http://tasks.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer');

    // helper function returning ImageParameters for dynamic layers
    // example:
    // imageParameters: buildImageParameters({
    //     layerIds: [0],
    //     layerOption: 'show'
    // })
    function buildImageParameters (config) {
        config = config || {};
        var ip = new ImageParameters();
        //image parameters for dynamic services, set to png32 for higher quality exports
        ip.format = 'png32';
        for (var key in config) {
            if (config.hasOwnProperty(key)) {
                ip[key] = config[key];
            }
        }
        return ip;
    }

    return {
        // used for debugging your app
        isDebug: true,

        //default mapClick mode, mapClickMode lets widgets know what mode the map is in to avoid multipult map click actions from taking place (ie identify while drawing).
        defaultMapClickMode: 'identify',
        // map options, passed to map constructor. see: https://developers.arcgis.com/javascript/jsapi/map-amd.html#map1
        mapOptions: {
            basemap: 'streets',
            center: [-96.59179687497497, 39.09596293629694],
            zoom: 5,
            sliderStyle: 'small'
        },
        // panes: {
        // 	left: {
        // 		splitter: true
        // 	},
        // 	right: {
        // 		id: 'sidebarRight',
        // 		placeAt: 'outer',
        // 		region: 'right',
        // 		splitter: true,
        // 		collapsible: true
        // 	},
        // 	bottom: {
        // 		id: 'sidebarBottom',
        // 		placeAt: 'outer',
        // 		splitter: true,
        // 		collapsible: true,
        // 		region: 'bottom'
        // 	},
        // 	top: {
        // 		id: 'sidebarTop',
        // 		placeAt: 'outer',
        // 		collapsible: true,
        // 		splitter: true,
        // 		region: 'top'
        // 	}
        // },
        // collapseButtonsPane: 'center', //center or outer

        // operationalLayers: Array of Layers to load on top of the basemap: valid 'type' options: 'dynamic', 'tiled', 'feature'.
        // The 'options' object is passed as the layers options for constructor. Title will be used in the legend only. id's must be unique and have no spaces.
        // 3 'mode' options: MODE_SNAPSHOT = 0, MODE_ONDEMAND = 1, MODE_SELECTION = 2
        operationalLayers: [{
            type: 'feature',
            url: 'http://services1.arcgis.com/6bXbLtkf4y11TosO/arcgis/rest/services/Restaurants/FeatureServer/0',
            title: 'Restaurants',
            options: {
                id: 'restaurants',
                opacity: 1.0,
                visible: true,
                outFields: ['*'],
                mode: 0
            },
            editorLayerInfos: {
                disableGeometryUpdate: false
            },
            legendLayerInfos: {
                exclude: false,
                layerInfo: {
                    title: 'Restaurants'
                }
            }
        }, {
            type: 'feature',
            url: 'http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/0',
            title: 'San Francisco 311 Incidents',
            options: {
                id: 'sf311Incidents',
                opacity: 1.0,
                visible: true,
                outFields: ['req_type', 'req_date', 'req_time', 'address', 'district'],
                mode: 0
            }
        }, {
            type: 'dynamic',
            url: 'http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/PublicSafety/PublicSafetyOperationalLayers/MapServer',
            title: 'Louisville Public Safety',
            options: {
                id: 'louisvillePubSafety',
                opacity: 1.0,
                visible: true,
                imageParameters: buildImageParameters({
                    layerIds: [0, 2, 4, 5, 8, 10, 12, 21],
                    layerOption: 'show'
                })
            },
            identifyLayerInfos: {
                layerIds: [2, 4, 5, 8, 12, 21]
            },
            layerControlLayerInfos: {
                layerIds: [0, 2, 4, 5, 8, 9, 10, 12, 21]
            },
            legendLayerInfos: {
                layerInfo: {
                    hideLayers: [21]
                }
            }
        }, {
            type: 'dynamic',
            url: 'http://sampleserver6.arcgisonline.com/arcgis/rest/services/DamageAssessment/MapServer',
            title: 'Damage Assessment',
            options: {
                id: 'DamageAssessment',
                opacity: 1.0,
                visible: true,
                imageParameters: buildImageParameters()
            },
            legendLayerInfos: {
                exclude: true
            },
            layerControlLayerInfos: {
                swipe: true,
                metadataUrl: true,
                expanded: true
            }
        /*
        //examples of vector tile layers (beta in v3.15)
        }, {
            type: 'vectortile',
            title: 'Light Gray Canvas Vector',
            url: '//www.arcgis.com/sharing/rest/content/items/bdf1eec3fa79456c8c7c2bb62f86dade/resources/styles/root.json',
            options: {
                id: 'vectortile1',
                opacity: 0.8,
                visible: true
            }
        }, {
           //  taken from this demo: https://github.com/ycabon/presentations/blob/gh-pages/2015-berlin-plenary/demos/3.15-vectortile/create-by-style-object.html
            type: 'vectortile',
            title: 'Custom Vector Style',
            options: {
                id: 'vectortile2',
                opacity: 1.0,
                visible: true,
                'glyphs': '//www.arcgis.com/sharing/rest/content/items/00cd8e843bae49b3a040423e5d65416b/resources/fonts/{fontstack}/{range}.pbf',
                'sprite': '//www.arcgis.com/sharing/rest/content/items/00cd8e843bae49b3a040423e5d65416b/resources/sprites/sprite',
                'version': 8,
                'sources': {
                    'esri': {
                        'url': '//basemapsdev.arcgis.com/arcgis/rest/services/World_Basemap/VectorTileServer',
                        'type': 'vector'
                    }
                },
                'layers': [{
                    'id': 'background',
                    'type': 'background',
                    'paint': {
                        'background-color': '#556688'
                    }
                }, {
                    'id': 'Land',
                    'type': 'fill',
                    'source': 'esri',
                    'source-layer': 'Land',
                    'paint': {
                        'fill-color': '#273344'
                    },
                }, {
                    'id': 'roads',
                    'type': 'line',
                    'source': 'esri',
                    'source-layer': 'Road',
                    'layout': {
                        'line-join': 'round'
                    },
                    'paint': {
                        'line-width': 1,
                        'line-color': '#131622'
                    }
                }]
            }
        */
        }],
        // set include:true to load. For titlePane type set position the the desired order in the sidebar
        widgets: {
            growler: {
                include: true,
                id: 'growler',
                type: 'domNode',
                path: 'gis/dijit/Growler',
                srcNodeRef: 'growlerDijit',
                options: {}
            },
            geocoder: {
                include: true,
                type: 'domNode',
                path: 'esri/dijit/Search',
                srcNodeRef: 'geocoderButton',
                options: {
                    map: true,
                    visible: true,
                    enableInfoWindow: false,
                    enableButtonMode: true,
                    expanded: false
                }
            },
            identify: {
                include: true,
                id: 'identify',
                type: 'titlePane',
                path: 'gis/dijit/Identify',
                title: 'Identify',
                open: false,
                position: 3,
                options: 'config/identify'
            },
            basemaps: {
                include: true,
                id: 'basemaps',
                type: 'domNode',
                path: 'gis/dijit/Basemaps',
                srcNodeRef: 'basemapsDijit',
                options: 'config/basemaps'
            },
            mapInfo: {
                include: false,
                id: 'mapInfo',
                type: 'domNode',
                path: 'gis/dijit/MapInfo',
                srcNodeRef: 'mapInfoDijit',
                options: {
                    map: true,
                    mode: 'dms',
                    firstCoord: 'y',
                    unitScale: 3,
                    showScale: true,
                    xLabel: '',
                    yLabel: '',
                    minWidth: 286
                }
            },
            scalebar: {
                include: true,
                id: 'scalebar',
                type: 'map',
                path: 'esri/dijit/Scalebar',
                options: {
                    map: true,
                    attachTo: 'bottom-left',
                    scalebarStyle: 'line',
                    scalebarUnit: 'dual'
                }
            },
            locateButton: {
                include: true,
                id: 'locateButton',
                type: 'domNode',
                path: 'gis/dijit/LocateButton',
                srcNodeRef: 'locateButton',
                options: {
                    map: true,
                    publishGPSPosition: true,
                    highlightLocation: true,
                    useTracking: true,
                    geolocationOptions: {
                        maximumAge: 0,
                        timeout: 15000,
                        enableHighAccuracy: true
                    }
                }
            },
            overviewMap: {
                include: true,
                id: 'overviewMap',
                type: 'map',
                path: 'esri/dijit/OverviewMap',
                options: {
                    map: true,
                    attachTo: 'bottom-right',
                    color: '#0000CC',
                    height: 100,
                    width: 125,
                    opacity: 0.30,
                    visible: false
                }
            },
            homeButton: {
                include: true,
                id: 'homeButton',
                type: 'domNode',
                path: 'esri/dijit/HomeButton',
                srcNodeRef: 'homeButton',
                options: {
                    map: true,
                    extent: new Extent({
                        xmin: -180,
                        ymin: -85,
                        xmax: 180,
                        ymax: 85,
                        spatialReference: {
                            wkid: 4326
                        }
                    })
                }
            },
            legend: {
                include: true,
                id: 'legend',
                type: 'titlePane',
                path: 'esri/dijit/Legend',
                title: 'Legend',
                open: false,
                position: 0,
                options: {
                    map: true,
                    legendLayerInfos: true
                }
            },
            layerControl: {
                include: true,
                id: 'layerControl',
                type: 'titlePane',
                path: 'gis/dijit/LayerControl',
                title: 'Layers',
                open: false,
                position: 0,
                options: {
                    map: true,
                    layerControlLayerInfos: true,
                    separated: true,
                    vectorReorder: true,
                    overlayReorder: true
                }
            },
            bookmarks: {
                include: true,
                id: 'bookmarks',
                type: 'titlePane',
                path: 'gis/dijit/Bookmarks',
                title: 'Bookmarks',
                open: false,
                position: 2,
                options: 'config/bookmarks'
            },
            find: {
                include: true,
                id: 'find',
                type: 'titlePane',
                canFloat: true,
                path: 'gis/dijit/Find',
                title: 'Find',
                open: false,
                position: 3,
                options: 'config/find'
            },
            draw: {
                include: true,
                id: 'draw',
                type: 'titlePane',
                canFloat: true,
                path: 'gis/dijit/Draw',
                title: 'Draw',
                open: false,
                position: 4,
                options: {
                    map: true,
                    mapClickMode: true
                }
            },
            measure: {
                include: true,
                id: 'measurement',
                type: 'titlePane',
                canFloat: true,
                path: 'gis/dijit/Measurement',
                title: 'Measurement',
                open: false,
                position: 5,
                options: {
                    map: true,
                    mapClickMode: true,
                    defaultAreaUnit: units.SQUARE_MILES,
                    defaultLengthUnit: units.MILES
                }
            },
            print: {
                include: true,
                id: 'print',
                type: 'titlePane',
                canFloat: true,
                path: 'gis/dijit/Print',
                title: 'Print',
                open: false,
                position: 6,
                options: {
                    map: true,
                    printTaskURL: 'https://utility.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task',
                    copyrightText: 'Copyright 2014',
                    authorText: 'Me',
                    defaultTitle: 'Viewer Map',
                    defaultFormat: 'PDF',
                    defaultLayout: 'Letter ANSI A Landscape'
                }
            },
            directions: {
                include: true,
                id: 'directions',
                type: 'titlePane',
                path: 'gis/dijit/Directions',
                title: 'Directions',
                open: false,
                position: 7,
                options: {
                    map: true,
                    mapRightClickMenu: true,
                    options: {
                        routeTaskUrl: 'http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/NAServer/Route',
                        routeParams: {
                            directionsLanguage: 'en-US',
                            directionsLengthUnits: units.MILES
                        },
                        active: false //for 3.12, starts active by default, which we dont want as it interfears with mapClickMode
                    }
                }
            },
            editor: {
                include: true,
                id: 'editor',
                type: 'titlePane',
                path: 'gis/dijit/Editor',
                title: 'Editor',
                open: false,
                position: 8,
                options: {
                    map: true,
                    mapClickMode: true,
                    editorLayerInfos: true,
                    settings: {
                        toolbarVisible: true,
                        showAttributesOnClick: true,
                        enableUndoRedo: true,
                        createOptions: {
                            polygonDrawTools: ['freehandpolygon', 'autocomplete']
                        },
                        toolbarOptions: {
                            reshapeVisible: true,
                            cutVisible: true,
                            mergeVisible: true
                        }
                    }
                }
            },
            streetview: {
                include: true,
                id: 'streetview',
                type: 'titlePane',
                canFloat: true,
                position: 9,
                path: 'gis/dijit/StreetView',
                title: 'Google Street View',
                paneOptions: {
                    resizable: true,
                    resizeOptions: {
                        minSize: {
                            w: 250,
                            h: 250
                        }
                    }
                },
                options: {
                    map: true,
                    mapClickMode: true,
                    mapRightClickMenu: true
                }
            },
            help: {
                include: true,
                id: 'help',
                type: 'floating',
                path: 'gis/dijit/Help',
                title: 'Help',
                options: {}
            }

        }
    };
});<commit_msg>add examples of custom titles, layerTypes and widgetTypes<commit_after>define([
    'esri/units',
    'esri/geometry/Extent',
    'esri/config',
    /*'esri/urlUtils',*/
    'esri/tasks/GeometryService',
    'esri/layers/ImageParameters'
], function (units, Extent, esriConfig, /*urlUtils,*/ GeometryService, ImageParameters) {

    // url to your proxy page, must be on same machine hosting you app. See proxy folder for readme.
    esriConfig.defaults.io.proxyUrl = 'proxy/proxy.ashx';
    esriConfig.defaults.io.alwaysUseProxy = false;

    // add a proxy rule to force specific domain requests through proxy
    // be sure the domain is added in proxy.config
    /*urlUtils.addProxyRule({
        urlPrefix: 'www.example.com',
        proxyUrl: 'proxy/proxy.ashx'
    });*/

    // url to your geometry server.
    esriConfig.defaults.geometryService = new GeometryService('http://tasks.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer');

    // helper function returning ImageParameters for dynamic layers
    // example:
    // imageParameters: buildImageParameters({
    //     layerIds: [0],
    //     layerOption: 'show'
    // })
    function buildImageParameters (config) {
        config = config || {};
        var ip = new ImageParameters();
        //image parameters for dynamic services, set to png32 for higher quality exports
        ip.format = 'png32';
        for (var key in config) {
            if (config.hasOwnProperty(key)) {
                ip[key] = config[key];
            }
        }
        return ip;
    }

    return {
        // used for debugging your app
        isDebug: true,

        //default mapClick mode, mapClickMode lets widgets know what mode the map is in to avoid multipult map click actions from taking place (ie identify while drawing).
        defaultMapClickMode: 'identify',
        // map options, passed to map constructor. see: https://developers.arcgis.com/javascript/jsapi/map-amd.html#map1
        mapOptions: {
            basemap: 'streets',
            center: [-96.59179687497497, 39.09596293629694],
            zoom: 5,
            sliderStyle: 'small'
        },

        // panes: {
        // 	left: {
        // 		splitter: true
        // 	},
        // 	right: {
        // 		id: 'sidebarRight',
        // 		placeAt: 'outer',
        // 		region: 'right',
        // 		splitter: true,
        // 		collapsible: true
        // 	},
        // 	bottom: {
        // 		id: 'sidebarBottom',
        // 		placeAt: 'outer',
        // 		splitter: true,
        // 		collapsible: true,
        // 		region: 'bottom'
        // 	},
        // 	top: {
        // 		id: 'sidebarTop',
        // 		placeAt: 'outer',
        // 		collapsible: true,
        // 		splitter: true,
        // 		region: 'top'
        // 	}
        // },
        // collapseButtonsPane: 'center', //center or outer

        // custom titles
        /*
        titles: {
            header: 'My App',
            subHeader: 'My GIS App is the best!',
            pageTitle: 'My App'
        },
        */

        // user-defined layer types
        /*
        layerTypes: {
            myCustomLayer: 'widgets/MyCustomLayer'
        },
        */

        // user-defined widget types
        /*
        widgetTypes: [
            'myWidgetType'
        ],
        */

        // operationalLayers: Array of Layers to load on top of the basemap: valid 'type' options: 'dynamic', 'tiled', 'feature'.
        // The 'options' object is passed as the layers options for constructor. Title will be used in the legend only. id's must be unique and have no spaces.
        // 3 'mode' options: MODE_SNAPSHOT = 0, MODE_ONDEMAND = 1, MODE_SELECTION = 2
        operationalLayers: [{
            type: 'feature',
            url: 'http://services1.arcgis.com/6bXbLtkf4y11TosO/arcgis/rest/services/Restaurants/FeatureServer/0',
            title: 'Restaurants',
            options: {
                id: 'restaurants',
                opacity: 1.0,
                visible: true,
                outFields: ['*'],
                mode: 0
            },
            editorLayerInfos: {
                disableGeometryUpdate: false
            },
            legendLayerInfos: {
                exclude: false,
                layerInfo: {
                    title: 'Restaurants'
                }
            }
        }, {
            type: 'feature',
            url: 'http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/SanFrancisco/311Incidents/FeatureServer/0',
            title: 'San Francisco 311 Incidents',
            options: {
                id: 'sf311Incidents',
                opacity: 1.0,
                visible: true,
                outFields: ['req_type', 'req_date', 'req_time', 'address', 'district'],
                mode: 0
            }
        }, {
            type: 'dynamic',
            url: 'http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/PublicSafety/PublicSafetyOperationalLayers/MapServer',
            title: 'Louisville Public Safety',
            options: {
                id: 'louisvillePubSafety',
                opacity: 1.0,
                visible: true,
                imageParameters: buildImageParameters({
                    layerIds: [0, 2, 4, 5, 8, 10, 12, 21],
                    layerOption: 'show'
                })
            },
            identifyLayerInfos: {
                layerIds: [2, 4, 5, 8, 12, 21]
            },
            layerControlLayerInfos: {
                layerIds: [0, 2, 4, 5, 8, 9, 10, 12, 21]
            },
            legendLayerInfos: {
                layerInfo: {
                    hideLayers: [21]
                }
            }
        }, {
            type: 'dynamic',
            url: 'http://sampleserver6.arcgisonline.com/arcgis/rest/services/DamageAssessment/MapServer',
            title: 'Damage Assessment',
            options: {
                id: 'DamageAssessment',
                opacity: 1.0,
                visible: true,
                imageParameters: buildImageParameters()
            },
            legendLayerInfos: {
                exclude: true
            },
            layerControlLayerInfos: {
                swipe: true,
                metadataUrl: true,
                expanded: true
            }
        /*
        //examples of vector tile layers (beta in v3.15)
        }, {
            type: 'vectortile',
            title: 'Light Gray Canvas Vector',
            url: '//www.arcgis.com/sharing/rest/content/items/bdf1eec3fa79456c8c7c2bb62f86dade/resources/styles/root.json',
            options: {
                id: 'vectortile1',
                opacity: 0.8,
                visible: true
            }
        }, {
           //  taken from this demo: https://github.com/ycabon/presentations/blob/gh-pages/2015-berlin-plenary/demos/3.15-vectortile/create-by-style-object.html
            type: 'vectortile',
            title: 'Custom Vector Style',
            options: {
                id: 'vectortile2',
                opacity: 1.0,
                visible: true,
                'glyphs': '//www.arcgis.com/sharing/rest/content/items/00cd8e843bae49b3a040423e5d65416b/resources/fonts/{fontstack}/{range}.pbf',
                'sprite': '//www.arcgis.com/sharing/rest/content/items/00cd8e843bae49b3a040423e5d65416b/resources/sprites/sprite',
                'version': 8,
                'sources': {
                    'esri': {
                        'url': '//basemapsdev.arcgis.com/arcgis/rest/services/World_Basemap/VectorTileServer',
                        'type': 'vector'
                    }
                },
                'layers': [{
                    'id': 'background',
                    'type': 'background',
                    'paint': {
                        'background-color': '#556688'
                    }
                }, {
                    'id': 'Land',
                    'type': 'fill',
                    'source': 'esri',
                    'source-layer': 'Land',
                    'paint': {
                        'fill-color': '#273344'
                    },
                }, {
                    'id': 'roads',
                    'type': 'line',
                    'source': 'esri',
                    'source-layer': 'Road',
                    'layout': {
                        'line-join': 'round'
                    },
                    'paint': {
                        'line-width': 1,
                        'line-color': '#131622'
                    }
                }]
            }
        */
        }],
        // set include:true to load. For titlePane type set position the the desired order in the sidebar
        widgets: {
            growler: {
                include: true,
                id: 'growler',
                type: 'domNode',
                path: 'gis/dijit/Growler',
                srcNodeRef: 'growlerDijit',
                options: {}
            },
            geocoder: {
                include: true,
                type: 'domNode',
                path: 'esri/dijit/Search',
                srcNodeRef: 'geocoderButton',
                options: {
                    map: true,
                    visible: true,
                    enableInfoWindow: false,
                    enableButtonMode: true,
                    expanded: false
                }
            },
            identify: {
                include: true,
                id: 'identify',
                type: 'titlePane',
                path: 'gis/dijit/Identify',
                title: 'Identify',
                open: false,
                position: 3,
                options: 'config/identify'
            },
            basemaps: {
                include: true,
                id: 'basemaps',
                type: 'domNode',
                path: 'gis/dijit/Basemaps',
                srcNodeRef: 'basemapsDijit',
                options: 'config/basemaps'
            },
            mapInfo: {
                include: false,
                id: 'mapInfo',
                type: 'domNode',
                path: 'gis/dijit/MapInfo',
                srcNodeRef: 'mapInfoDijit',
                options: {
                    map: true,
                    mode: 'dms',
                    firstCoord: 'y',
                    unitScale: 3,
                    showScale: true,
                    xLabel: '',
                    yLabel: '',
                    minWidth: 286
                }
            },
            scalebar: {
                include: true,
                id: 'scalebar',
                type: 'map',
                path: 'esri/dijit/Scalebar',
                options: {
                    map: true,
                    attachTo: 'bottom-left',
                    scalebarStyle: 'line',
                    scalebarUnit: 'dual'
                }
            },
            locateButton: {
                include: true,
                id: 'locateButton',
                type: 'domNode',
                path: 'gis/dijit/LocateButton',
                srcNodeRef: 'locateButton',
                options: {
                    map: true,
                    publishGPSPosition: true,
                    highlightLocation: true,
                    useTracking: true,
                    geolocationOptions: {
                        maximumAge: 0,
                        timeout: 15000,
                        enableHighAccuracy: true
                    }
                }
            },
            overviewMap: {
                include: true,
                id: 'overviewMap',
                type: 'map',
                path: 'esri/dijit/OverviewMap',
                options: {
                    map: true,
                    attachTo: 'bottom-right',
                    color: '#0000CC',
                    height: 100,
                    width: 125,
                    opacity: 0.30,
                    visible: false
                }
            },
            homeButton: {
                include: true,
                id: 'homeButton',
                type: 'domNode',
                path: 'esri/dijit/HomeButton',
                srcNodeRef: 'homeButton',
                options: {
                    map: true,
                    extent: new Extent({
                        xmin: -180,
                        ymin: -85,
                        xmax: 180,
                        ymax: 85,
                        spatialReference: {
                            wkid: 4326
                        }
                    })
                }
            },
            legend: {
                include: true,
                id: 'legend',
                type: 'titlePane',
                path: 'esri/dijit/Legend',
                title: 'Legend',
                open: false,
                position: 0,
                options: {
                    map: true,
                    legendLayerInfos: true
                }
            },
            layerControl: {
                include: true,
                id: 'layerControl',
                type: 'titlePane',
                path: 'gis/dijit/LayerControl',
                title: 'Layers',
                open: false,
                position: 0,
                options: {
                    map: true,
                    layerControlLayerInfos: true,
                    separated: true,
                    vectorReorder: true,
                    overlayReorder: true
                }
            },
            bookmarks: {
                include: true,
                id: 'bookmarks',
                type: 'titlePane',
                path: 'gis/dijit/Bookmarks',
                title: 'Bookmarks',
                open: false,
                position: 2,
                options: 'config/bookmarks'
            },
            find: {
                include: true,
                id: 'find',
                type: 'titlePane',
                canFloat: true,
                path: 'gis/dijit/Find',
                title: 'Find',
                open: false,
                position: 3,
                options: 'config/find'
            },
            draw: {
                include: true,
                id: 'draw',
                type: 'titlePane',
                canFloat: true,
                path: 'gis/dijit/Draw',
                title: 'Draw',
                open: false,
                position: 4,
                options: {
                    map: true,
                    mapClickMode: true
                }
            },
            measure: {
                include: true,
                id: 'measurement',
                type: 'titlePane',
                canFloat: true,
                path: 'gis/dijit/Measurement',
                title: 'Measurement',
                open: false,
                position: 5,
                options: {
                    map: true,
                    mapClickMode: true,
                    defaultAreaUnit: units.SQUARE_MILES,
                    defaultLengthUnit: units.MILES
                }
            },
            print: {
                include: true,
                id: 'print',
                type: 'titlePane',
                canFloat: true,
                path: 'gis/dijit/Print',
                title: 'Print',
                open: false,
                position: 6,
                options: {
                    map: true,
                    printTaskURL: 'https://utility.arcgisonline.com/arcgis/rest/services/Utilities/PrintingTools/GPServer/Export%20Web%20Map%20Task',
                    copyrightText: 'Copyright 2014',
                    authorText: 'Me',
                    defaultTitle: 'Viewer Map',
                    defaultFormat: 'PDF',
                    defaultLayout: 'Letter ANSI A Landscape'
                }
            },
            directions: {
                include: true,
                id: 'directions',
                type: 'titlePane',
                path: 'gis/dijit/Directions',
                title: 'Directions',
                open: false,
                position: 7,
                options: {
                    map: true,
                    mapRightClickMenu: true,
                    options: {
                        routeTaskUrl: 'http://sampleserver3.arcgisonline.com/ArcGIS/rest/services/Network/USA/NAServer/Route',
                        routeParams: {
                            directionsLanguage: 'en-US',
                            directionsLengthUnits: units.MILES
                        },
                        active: false //for 3.12, starts active by default, which we dont want as it interfears with mapClickMode
                    }
                }
            },
            editor: {
                include: true,
                id: 'editor',
                type: 'titlePane',
                path: 'gis/dijit/Editor',
                title: 'Editor',
                open: false,
                position: 8,
                options: {
                    map: true,
                    mapClickMode: true,
                    editorLayerInfos: true,
                    settings: {
                        toolbarVisible: true,
                        showAttributesOnClick: true,
                        enableUndoRedo: true,
                        createOptions: {
                            polygonDrawTools: ['freehandpolygon', 'autocomplete']
                        },
                        toolbarOptions: {
                            reshapeVisible: true,
                            cutVisible: true,
                            mergeVisible: true
                        }
                    }
                }
            },
            streetview: {
                include: true,
                id: 'streetview',
                type: 'titlePane',
                canFloat: true,
                position: 9,
                path: 'gis/dijit/StreetView',
                title: 'Google Street View',
                paneOptions: {
                    resizable: true,
                    resizeOptions: {
                        minSize: {
                            w: 250,
                            h: 250
                        }
                    }
                },
                options: {
                    map: true,
                    mapClickMode: true,
                    mapRightClickMenu: true
                }
            },
            help: {
                include: true,
                id: 'help',
                type: 'floating',
                path: 'gis/dijit/Help',
                title: 'Help',
                options: {}
            }

        }
    };
});<|endoftext|>


<commit_before>// Revision view for single business process

'use strict';

var renderDocumentsAndData = require('./_business-process-documents-and-data')
, renderMainInfo = require('./_business-process-main-info')
, _       = require('mano').i18n.bind('Official: Revision');

exports._parent = require('./user-base');
exports._match = 'businessProcess';

exports['sub-main'] = {
	class: { content: true, 'user-forms': true },
	content: function () {
		var revisionStep = this.processingStep;

		renderMainInfo(this.businessProcess);

		insert(_if(revisionStep._isPending, section({ class: 'official-submission-toolbar' },
			// show buttons only if step is pending
			_if(eq(revisionStep._revisionProgress, 1),
				// show "approve" or "sent back" buttons only, when revision was finalized
				_if(eq(revisionStep._approvalProgress, 1),
					postButton(
						{ action: url('revision', this.businessProcess.__id__, 'approve'),
							buttonClass: 'button-main button-main-success',
							'data-hint': _("Enables the processing of the application."),
							class: 'hint-optional hint-optional-bottom',
							value: _("Validate revision") }
					),
					postButton(
						{ action: url('revision', this.businessProcess.__id__, 'return'),
							buttonClass: 'button-main',
							class: 'hint-optional hint-optional-bottom',
							'data-hint': _("You can reject the registration when documents and/or " +
								"data that is sent can be determined as not real."),
							value: _("Return for corrections") }
					))),
					// show reject button at all times when revision is pending
			[dialog({ id: 'reject-reason', class: 'dialog-reject dialog-modal' },
				header(
					label({ for: 'revision-reject-reason' }, h3(_("Reason for rejection of the file")))
				),
				section({ class: 'dialog-body' },
					form({ method: 'post', action: '/revision/' + this.businessProcess.__id__ + '/reject/' },
						p({ class: 'input' }, input({ id: 'revision-reject-reason',
								dbjs: this.processingStep._rejectionReason })),
						p(input({ type: 'submit', value: _("Reject") })))),
				footer(p(a({ href: '' }, _("Cancel"))))),
				a({ href: '#reject-reason',
					class: 'button-main button-main-error hint-optional' +
					' hint-optional-right hint-optional-multiline',
					'data-hint': _("When returning the application for corrections, the user will receive" +
						" a notification with information on the changes that must be done in his form or" +
						" in the documents that your request.") },
					_("Reject application"))]
			)));

		renderDocumentsAndData(this.businessProcess,
			{ urlPrefix: '/' + this.businessProcess.__id__ + '/' });
	}
};
<commit_msg>Assure complete sendBack progress<commit_after>// Revision view for single business process

'use strict';

var renderDocumentsAndData = require('./_business-process-documents-and-data')
, renderMainInfo = require('./_business-process-main-info')
, _       = require('mano').i18n.bind('Official: Revision');

exports._parent = require('./user-base');
exports._match = 'businessProcess';

exports['sub-main'] = {
	class: { content: true, 'user-forms': true },
	content: function () {
		var revisionStep = this.processingStep;

		renderMainInfo(this.businessProcess);

		insert(_if(revisionStep._isPending, section({ class: 'official-submission-toolbar' },
			// show buttons only if step is pending
			_if(eq(revisionStep._revisionProgress, 1),
				// show "approve" or "sent back" buttons only, when revision was finalized
				_if(eq(revisionStep._approvalProgress, 1),
					postButton(
						{ action: url('revision', this.businessProcess.__id__, 'approve'),
							buttonClass: 'button-main button-main-success',
							'data-hint': _("Enables the processing of the application."),
							class: 'hint-optional hint-optional-bottom',
							value: _("Validate revision") }
					),
					_if(eq(revisionStep._sendBackProgress, 1), postButton(
						{ action: url('revision', this.businessProcess.__id__, 'return'),
							buttonClass: 'button-main',
							class: 'hint-optional hint-optional-bottom',
							'data-hint': _("You can reject the registration when documents and/or " +
								"data that is sent can be determined as not real."),
							value: _("Return for corrections") }
					)))),
					// show reject button at all times when revision is pending
			[dialog({ id: 'reject-reason', class: 'dialog-reject dialog-modal' },
				header(
					label({ for: 'revision-reject-reason' }, h3(_("Reason for rejection of the file")))
				),
				section({ class: 'dialog-body' },
					form({ method: 'post', action: '/revision/' + this.businessProcess.__id__ + '/reject/' },
						p({ class: 'input' }, input({ id: 'revision-reject-reason',
								dbjs: this.processingStep._rejectionReason })),
						p(input({ type: 'submit', value: _("Reject") })))),
				footer(p(a({ href: '' }, _("Cancel"))))),
				a({ href: '#reject-reason',
					class: 'button-main button-main-error hint-optional' +
					' hint-optional-right hint-optional-multiline',
					'data-hint': _("When returning the application for corrections, the user will receive" +
						" a notification with information on the changes that must be done in his form or" +
						" in the documents that your request.") },
					_("Reject application"))]
			)));

		renderDocumentsAndData(this.businessProcess,
			{ urlPrefix: '/' + this.businessProcess.__id__ + '/' });
	}
};
<|endoftext|>


<commit_before>define([
    'require',
    'jquery',
    'base/js/namespace',
    'base/js/dialog',
    'notebook/js/celltoolbar',
    'base/js/events'

], function (require, $, Jupyter, dialog, celltoolbar, events) {

    var preset_name = 'Cell Testing';

    var CellToolbar = celltoolbar.CellToolbar;

    var prepare_data = function(cell) {
        if (cell.nbcelltesting_data === undefined) {
            cell.nbcelltesting_data = {};
        }
        if (cell.nbcelltesting_data.pending === undefined) {
            cell.nbcelltesting_data.pending = false;
        }
    };

    events.on('execute.CodeCell', function(event, data) {
        cell = data.cell;
        prepare_data(cell);
        cell.nbcelltesting_data.pending = true;
        delete cell.nbcelltesting_data.result_test;
        cell.celltoolbar.rebuild();
    });

    events.on('finished_execute.CodeCell', function(event, data) {
        cell = data.cell;
        prepare_data(cell);
        cell.nbcelltesting_data.pending = false;
        test_output(cell);
        cell.celltoolbar.rebuild();
    });

    var prepare_metadata = function(cell) {
        if (cell.metadata.nbcelltesting === undefined) {
            cell.metadata.nbcelltesting = {};
        }
    };

    var cleanup_metadata = function(cell) {
        if (cell.metadata.nbcelltesting !== undefined &&
            Object.keys(cell.metadata.nbcelltesting).length === 0) {
            delete cell.metadata.nbcelltesting
        }
    };

    var cell_output = function(cell) {
        pre = cell.output_area.element.find('div.output_subarea').last().find('pre');
        return pre.text();
    };

    var save_desired_output = function(cell) {
        prepare_metadata(cell);
        cell.metadata.nbcelltesting.desired_output = cell_output(cell);
    };

    var desired_output = function(cell) {
        if (cell.metadata.nbcelltesting !== undefined &&
            cell.metadata.nbcelltesting.desired_output !== undefined) {
            return cell.metadata.nbcelltesting.desired_output;
        }
        return null;
    };

    var reset_desired_output = function(cell) {
        if (cell.metadata.nbcelltesting !== undefined &&
            cell.metadata.nbcelltesting.hasOwnProperty("desired_output")) {
            delete cell.metadata.nbcelltesting.desired_output;
            cleanup_metadata(cell);
        }
    };

    var edit_nbcelltesting_metadata = function(cell, celltoolbar=null) {
        if (cell.metadata.nbcelltesting === undefined) {
            metadata = {};
        } else {
            metadata = cell.metadata.nbcelltesting;
        }

        notebook = Jupyter.notebook
        dialog.edit_metadata({
            md: metadata,
            callback: function (md) {
                prepare_metadata(cell);
                cell.metadata.nbcelltesting = md;
                test_output(cell);
                if (celltoolbar !== null) {
                    celltoolbar.rebuild();
                }
            },
            name: 'Cell Testing',
            notebook: notebook,
            keyboard_manager: notebook.keyboard_manager});
    };

    var compare_output = function(cell) {
        dout = desired_output(cell);
        if (dout === null) {
            return null;
        }
        cout = cell_output(cell);
        return cout === dout;
    };

    var test_output = function(cell) {
        prepare_data(cell);
        var result;
        var cls;
        if (cell.nbcelltesting_data.pending === true) {
            result = "";
            cls = 'label-info';
        } else {
            comparison_result = compare_output(cell);
            if (comparison_result === null) {
                result = 'no output saved';
                cls = 'label-info';
            } else if (comparison_result === false) {
                result = 'test failed';
                cls = 'label-danger';
            } else if (comparison_result === true) {
                result = 'test passed';
                cls = 'label-success';
            }
        }
        var result_test = $('').addClass('label result-label').addClass(cls);
        result_test.append(result);

        cell.nbcelltesting_data.result_test = result_test;
    };

    var create_result_test = function(div, cell, celltoolbar) {
        if (cell.nbcelltesting_data === undefined ||
            cell.nbcelltesting_data.result_test === undefined) {
            test_output(cell);
        }

        $(div).addClass('ctb-thing result-test')
            .append(cell.nbcelltesting_data.result_test);
    };

    };

    var on_save_desired_output = function(cell, celltoolbar) {
        save_desired_output(cell);
        test_output(cell);
        celltoolbar.rebuild();
    };

    var on_reset_desired_output = function(cell, celltoolbar) {
        reset_desired_output(cell);
        test_output(cell);
        celltoolbar.rebuild();
    };

    var on_test_output = function(cell, celltoolbar) {
        test_output(cell);
        celltoolbar.rebuild();
    };

    var actions = [{name:"Save", callback: on_save_desired_output},
                   {name:"Reset", callback: on_reset_desired_output},
                   {name:"Edit", callback: edit_nbcelltesting_metadata},
                   {name:"Test", callback: on_test_output}];

    var dropdown_factory = function(div, cell, celltoolbar) {
        var dropdownButton = $("<button/>").addClass("btn btn-default btn-xs dropdown-toggle")
            .prop('type', 'button').attr("data-toggle", "dropdown")
            .html("cell testing ");

        var options = $("
").addClass("dropdown-menu");

        for (let action of actions) {
            var action_link = $("").prop("href", "#").html(action.name);
            action_link.on('click', function() { action.callback(cell, celltoolbar) });
            options.append($("
").append(action_link));
        }

        $(div).addClass("dropdown").append(dropdownButton).append(options);
    };

    var load_css = function () {
        var link = document.createElement('link');
        link.type = 'text/css';
        link.rel = 'stylesheet';
        link.href = require.toUrl('./nbcelltesting.css');
        document.getElementsByTagName('head')[0].appendChild(link);
    };

    function load_extension(){
        load_css();

        CellToolbar.register_callback('nbcelltesting.result_test', create_result_test);
        CellToolbar.register_callback('nbcelltesting.dropdown_menu', dropdown_factory);

        var preset = [
            'nbcelltesting.result_test',
            'nbcelltesting.dropdown_menu',
        ];
        CellToolbar.register_preset(preset_name, preset, Jupyter.notebook);
        console.log('nbcelltesting extension loaded.');
    }

    return {
        load_ipython_extension: load_extension
    };
});
<commit_msg>minor rewrite of code/wording<commit_after>define([
    'require',
    'jquery',
    'base/js/namespace',
    'base/js/dialog',
    'notebook/js/celltoolbar',
    'base/js/events'

], function (require, $, Jupyter, dialog, celltoolbar, events) {

    var preset_name = 'Cell Testing';

    var CellToolbar = celltoolbar.CellToolbar;

    var prepare_data = function(cell) {
        if (cell.nbcelltesting_data === undefined) {
            cell.nbcelltesting_data = {};
        }
        if (cell.nbcelltesting_data.pending === undefined) {
            cell.nbcelltesting_data.pending = false;
        }
    };

    events.on('execute.CodeCell', function(event, data) {
        cell = data.cell;
        prepare_data(cell);
        cell.nbcelltesting_data.pending = true;
        delete cell.nbcelltesting_data.result_test;
        cell.celltoolbar.rebuild();
    });

    events.on('finished_execute.CodeCell', function(event, data) {
        cell = data.cell;
        prepare_data(cell);
        cell.nbcelltesting_data.pending = false;
        test_output(cell);
        cell.celltoolbar.rebuild();
    });

    var prepare_metadata = function(cell) {
        if (cell.metadata.nbcelltesting === undefined) {
            cell.metadata.nbcelltesting = {};
        }
    };

    var cleanup_metadata = function(cell) {
        if (cell.metadata.nbcelltesting !== undefined &&
            Object.keys(cell.metadata.nbcelltesting).length === 0) {
            delete cell.metadata.nbcelltesting
        }
    };

    var cell_output = function(cell) {
        pre = cell.output_area.element.find('div.output_subarea').last().find('pre');
        return pre.text();
    };

    var save_desired_output = function(cell) {
        prepare_metadata(cell);
        cell.metadata.nbcelltesting.desired_output = cell_output(cell);
    };

    var desired_output = function(cell) {
        if (cell.metadata.nbcelltesting !== undefined &&
            cell.metadata.nbcelltesting.desired_output !== undefined) {
            return cell.metadata.nbcelltesting.desired_output;
        }
        return null;
    };

    var reset_desired_output = function(cell) {
        if (cell.metadata.nbcelltesting !== undefined &&
            cell.metadata.nbcelltesting.hasOwnProperty("desired_output")) {
            delete cell.metadata.nbcelltesting.desired_output;
            cleanup_metadata(cell);
        }
    };

    var edit_nbcelltesting_metadata = function(cell, celltoolbar=null) {
        if (cell.metadata.nbcelltesting === undefined) {
            metadata = {};
        } else {
            metadata = cell.metadata.nbcelltesting;
        }

        notebook = Jupyter.notebook
        dialog.edit_metadata({
            md: metadata,
            callback: function (md) {
                prepare_metadata(cell);
                cell.metadata.nbcelltesting = md;
                test_output(cell);
                if (celltoolbar !== null) {
                    celltoolbar.rebuild();
                }
            },
            name: 'Cell Testing',
            notebook: notebook,
            keyboard_manager: notebook.keyboard_manager});
    };

    var compare_output = function(cell) {
        dout = desired_output(cell);
        if (dout === null) {
            return null;
        }
        cout = cell_output(cell);
        return cout === dout;
    };

    var test_output = function(cell) {
        prepare_data(cell);
        var result;
        var cls;
        if (cell.nbcelltesting_data.pending === true) {
            result = "";
            cls = 'label-info';
        } else {
            comparison_result = compare_output(cell);
            if (comparison_result === null) {
                result = 'no output saved';
                cls = 'label-info';
            } else if (comparison_result === false) {
                result = 'test failed';
                cls = 'label-danger';
            } else if (comparison_result === true) {
                result = 'test passed';
                cls = 'label-success';
            }
        }
        var result_test = $('').addClass('label result-label').addClass(cls);
        result_test.append(result);

        cell.nbcelltesting_data.result_test = result_test;
    };

    var create_result_test = function(div, cell, celltoolbar) {
        if (cell.nbcelltesting_data === undefined ||
            cell.nbcelltesting_data.result_test === undefined) {
            test_output(cell);
        }

        $(div).addClass('ctb-thing result-test')
            .append(cell.nbcelltesting_data.result_test);
    };

    };

    var on_save_desired_output = function(cell, celltoolbar) {
        save_desired_output(cell);
        test_output(cell);
        celltoolbar.rebuild();
    };

    var on_reset_desired_output = function(cell, celltoolbar) {
        reset_desired_output(cell);
        test_output(cell);
        celltoolbar.rebuild();
    };

    var on_test_output = function(cell, celltoolbar) {
        test_output(cell);
        celltoolbar.rebuild();
    };

    var actions = [{name: 'Reset Output', callback: on_reset_desired_output},
                   {name: 'Edit Output', callback: on_edit_nbcelltesting_metadata},
                   {name: 'Test Output', callback: on_test_output}];

    var dropdown_factory = function(div, cell, celltoolbar) {
        var dropdownButton = $('<button/>').addClass('btn btn-default btn-xs dropdown-toggle')
            .prop('type', 'button').attr('data-toggle', 'dropdown')
            .html('');

        var options = $('
').addClass('dropdown-menu');

        for (let action of actions) {
            var action_link = $('').prop('href', '#').html(action.name);
            action_link.on('click', function() { action.callback(cell, celltoolbar) });
            options.append($('
').append(action_link));
        }

        $(div).addClass('ctb-thing dropdown').append(dropdownButton).append(options);
    };

    var load_css = function () {
        var link = document.createElement('link');
        link.type = 'text/css';
        link.rel = 'stylesheet';
        link.href = require.toUrl('./nbcelltesting.css');
        document.getElementsByTagName('head')[0].appendChild(link);
    };

    function load_extension(){
        load_css();

        CellToolbar.register_callback('nbcelltesting.result_test', create_result_test);
        CellToolbar.register_callback('nbcelltesting.dropdown_menu', dropdown_factory);

        var preset = [
            'nbcelltesting.result_test',
            'nbcelltesting.dropdown_menu',
        ];
        CellToolbar.register_preset(preset_name, preset, Jupyter.notebook);
        console.log('nbcelltesting extension loaded.');
    }

    return {
        load_ipython_extension: load_extension
    };
});
<|endoftext|>