<commit_before>(function(){

  var markletFrame;
  var markletDiv;
  var markletStyle;

  if (!document.getElementById("arena")) {
    initialize()
  }

  function initialize(){
    createFrame();
    createTarget();
    createStyle();

    document.addEventListener('dragstart', startDrag, true);
    document.addEventListener('dragend', stopDrag, true);
    document.addEventListener('mousedown', sendClick, true);

    markletDiv.addEventListener("dragover", dragOver, true);
    markletDiv.addEventListener("dragenter", dragEnter, false);
    markletDiv.addEventListener("dragleave", dragLeave, false);
    markletDiv.addEventListener("drop", drop, false);
  }

  function getURL(msg){
    baseUrl = "https://ervell-staging.herokuapp.com/save/";

    url = baseUrl + encodeURIComponent(msg.url);
    url += "?original_source_url=" + encodeURIComponent(msg.url);
    url += "&original_source_title=" + encodeURIComponent(msg.title);

    return url;
  };

  function createFrame(){
    markletFrame = document.createElement("iframe");
    markletFrame.name = markletFrame.id = "arena_frame";
    markletFrame.src = getURL({
      url: window.location.href,
      title: window.document.title
    });
    document.body.appendChild(markletFrame)
  }

  function createTarget(){
    markletDiv = document.createElement("div");
    markletDiv.id = "arena_div";
    document.body.appendChild(markletDiv);
  }

  function createStyle(){
    markletStyle = document.createElement("style");
    markletStyle.type = "text/css";
    markletCSS = "#arena_frame,#arena_div{overflow:hidden;width:300px;height:420px;position:fixed;top:20px;right:20px;border:none}#arena_frame.is-expanded{height:670px}#arena_frame{z-index:9999999998;background:rgba(255,255,255,0.75);box-shadow: 3px 4px 10px rgba(0, 0, 0, .4);}#arena_frame:hover{background:rgba(255,255,255,0.9); box-shadow: 3px 4px 10px rgba(0, 0, 0, .5);}#arena_div{z-index:9999999999;display:none;opacity:0}";

    if (markletStyle.styleSheet) {
      markletStyle.styleSheet.cssText = markletCSS;
    } else {
      markletStyle.appendChild(document.createTextNode(markletCSS));
    }
    document.body.appendChild(markletStyle);
  }

  document.onkeyup = function(e){
    if (e || window.event) {
      e = window.event;
      if (e.keyCode == 27) closeBookmarklet();
    }
  }

  window.addEventListener("message", function(e){
    switch (e.data.action) {
      case "close":
        closeBookmarklet();
        break;
      case "expand":
        el = document.getElementById('arena_frame');
        addClass(el, 'is-expanded');
        break;
      case "contract":
        el = document.getElementById('arena_frame');
        removeClass(el, 'is-expanded');
        break;
    }
  });

  function closeBookmarklet() {
    if (markletFrame) document.body.removeChild(markletFrame)
    if (markletDiv) document.body.removeNode(markletDiv)
    if (markletStyle) document.body.removeNode(markletStyle)
  }

  function startDrag(e) {
    var targetParent;

    sendMessage({
      action: "drag"
    });

    if (typeof e.dataTransfer.getData("text/html") == "undefined"
      || e.target.tagName == "IMG") {

      targetParent = closest(e.target, "A") || document.createElement('A')

      parentHTML = targetParent.cloneNode(false);
      parentHTML.href = parentHTML.href;

      targetImage = e.target.cloneNode(false);
      targetImage.src = targetImage.src;
      parentHTML.appendChild(targetImage);
      e.dataTransfer.setData("text/html", parentHTML.outerHTML);
    }

    markletDiv.style.display = "block";
  };

  function stopDrag(e) {
    markletDiv.style.display = "none";
  };

  function dragOver(e) {
    e.stopPropagation();
    e.preventDefault();
    return false;
  }

  function dragEnter(e) {
    sendMessage({
      action: "dragenter"
    })
    return false
  }

  function dragLeave(e) {
    sendMessage({
      action: "dragleave"
    })
    return false
  }

  function sendClick() {
    sendMessage({
      action: "click"
    })
  }

  function drop(e) {
    var data = {
      source: location.href,
      title: document.title
    }

    for (var i in e.dataTransfer.types) {
      data[e.dataTransfer.types[i]] = e.dataTransfer.getData(e.dataTransfer.types[i]);
    }

    sendMessage({
      action: "drop",
      value: data
    });

    e.stopPropagation();
    e.preventDefault();
    return false;
  }

  function closest(that, tagname) {
    while (that.parentNode) {
      that = that.parentNode;
      if (that.tagName == tagname) return that
    }
    return false;
  }

  function sendMessage(data) {
    markletFrame.contentWindow.postMessage(data, "*")
  }

  function hasClass(el, className) {
    if (el.classList)
      return el.classList.contains(className)
    else
      return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'))
  }

  function addClass(el, className) {
    if (el.classList)
      el.classList.add(className)
    else if (!hasClass(el, className)) el.className += " " + className
  }

  function removeClass(el, className) {
    if (el.classList)
      el.classList.remove(className)
    else if (hasClass(el, className)) {
      var reg = new RegExp('(\\s|^)' + className + '(\\s|$)')
      el.className=el.className.replace(reg, ' ')
    }
  }

})();
<commit_msg>use normal are.na<commit_after>(function(){

  var markletFrame;
  var markletDiv;
  var markletStyle;

  if (!document.getElementById("arena")) {
    initialize()
  }

  function initialize(){
    createFrame();
    createTarget();
    createStyle();

    document.addEventListener('dragstart', startDrag, true);
    document.addEventListener('dragend', stopDrag, true);
    document.addEventListener('mousedown', sendClick, true);

    markletDiv.addEventListener("dragover", dragOver, true);
    markletDiv.addEventListener("dragenter", dragEnter, false);
    markletDiv.addEventListener("dragleave", dragLeave, false);
    markletDiv.addEventListener("drop", drop, false);
  }

  function getURL(msg){
    baseUrl = "https://www.are.na/save/";

    url = baseUrl + encodeURIComponent(msg.url);
    url += "?original_source_url=" + encodeURIComponent(msg.url);
    url += "&original_source_title=" + encodeURIComponent(msg.title);

    return url;
  };

  function createFrame(){
    markletFrame = document.createElement("iframe");
    markletFrame.name = markletFrame.id = "arena_frame";
    markletFrame.src = getURL({
      url: window.location.href,
      title: window.document.title
    });
    document.body.appendChild(markletFrame)
  }

  function createTarget(){
    markletDiv = document.createElement("div");
    markletDiv.id = "arena_div";
    document.body.appendChild(markletDiv);
  }

  function createStyle(){
    markletStyle = document.createElement("style");
    markletStyle.type = "text/css";
    markletCSS = "#arena_frame,#arena_div{overflow:hidden;width:300px;height:420px;position:fixed;top:20px;right:20px;border:none}#arena_frame.is-expanded{height:670px}#arena_frame{z-index:9999999998;background:rgba(255,255,255,0.75);box-shadow: 3px 4px 10px rgba(0, 0, 0, .4);}#arena_frame:hover{background:rgba(255,255,255,0.9); box-shadow: 3px 4px 10px rgba(0, 0, 0, .5);}#arena_div{z-index:9999999999;display:none;opacity:0}";

    if (markletStyle.styleSheet) {
      markletStyle.styleSheet.cssText = markletCSS;
    } else {
      markletStyle.appendChild(document.createTextNode(markletCSS));
    }
    document.body.appendChild(markletStyle);
  }

  document.onkeyup = function(e){
    if (e || window.event) {
      e = window.event;
      if (e.keyCode == 27) closeBookmarklet();
    }
  }

  window.addEventListener("message", function(e){
    switch (e.data.action) {
      case "close":
        closeBookmarklet();
        break;
      case "expand":
        el = document.getElementById('arena_frame');
        addClass(el, 'is-expanded');
        break;
      case "contract":
        el = document.getElementById('arena_frame');
        removeClass(el, 'is-expanded');
        break;
    }
  });

  function closeBookmarklet() {
    if (markletFrame) document.body.removeChild(markletFrame)
    if (markletDiv) document.body.removeNode(markletDiv)
    if (markletStyle) document.body.removeNode(markletStyle)
  }

  function startDrag(e) {
    var targetParent;

    sendMessage({
      action: "drag"
    });

    if (typeof e.dataTransfer.getData("text/html") == "undefined"
      || e.target.tagName == "IMG") {

      targetParent = closest(e.target, "A") || document.createElement('A')

      parentHTML = targetParent.cloneNode(false);
      parentHTML.href = parentHTML.href;

      targetImage = e.target.cloneNode(false);
      targetImage.src = targetImage.src;
      parentHTML.appendChild(targetImage);
      e.dataTransfer.setData("text/html", parentHTML.outerHTML);
    }

    markletDiv.style.display = "block";
  };

  function stopDrag(e) {
    markletDiv.style.display = "none";
  };

  function dragOver(e) {
    e.stopPropagation();
    e.preventDefault();
    return false;
  }

  function dragEnter(e) {
    sendMessage({
      action: "dragenter"
    })
    return false
  }

  function dragLeave(e) {
    sendMessage({
      action: "dragleave"
    })
    return false
  }

  function sendClick() {
    sendMessage({
      action: "click"
    })
  }

  function drop(e) {
    var data = {
      source: location.href,
      title: document.title
    }

    for (var i in e.dataTransfer.types) {
      data[e.dataTransfer.types[i]] = e.dataTransfer.getData(e.dataTransfer.types[i]);
    }

    sendMessage({
      action: "drop",
      value: data
    });

    e.stopPropagation();
    e.preventDefault();
    return false;
  }

  function closest(that, tagname) {
    while (that.parentNode) {
      that = that.parentNode;
      if (that.tagName == tagname) return that
    }
    return false;
  }

  function sendMessage(data) {
    markletFrame.contentWindow.postMessage(data, "*")
  }

  function hasClass(el, className) {
    if (el.classList)
      return el.classList.contains(className)
    else
      return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'))
  }

  function addClass(el, className) {
    if (el.classList)
      el.classList.add(className)
    else if (!hasClass(el, className)) el.className += " " + className
  }

  function removeClass(el, className) {
    if (el.classList)
      el.classList.remove(className)
    else if (hasClass(el, className)) {
      var reg = new RegExp('(\\s|^)' + className + '(\\s|$)')
      el.className=el.className.replace(reg, ' ')
    }
  }

})();
<|endoftext|>


<commit_before>exports.cartridge = {
	commandCounter : 0;
	introText	:
};<commit_msg>Remove Test Game<commit_after><|endoftext|>


<commit_before><commit_msg>fix test (again)<commit_after><|endoftext|>


<commit_before>const {forkJoin, of, map, switchMap} = require('rxjs')
const ee = require('sepal/ee')
const imageFactory = require('sepal/ee/imageFactory')
const {getCollection$} = require('sepal/ee/timeSeries/collection')
const temporalSegmentation = require('./temporalSegmentation')
const moment = require('moment')
const _ = require('lodash')

const DATE_FORMAT = 'YYYY-MM-DD'

const changeAlerts = (recipe, {selection: selectedBands, baseBands} = {selection: [], baseBands: []}) => {
    const model = recipe.model
    const band = model.sources.band
    const monitoringEnd = model.date.monitoringEnd
    const monitoringStart = moment(monitoringEnd, DATE_FORMAT).subtract(model.date.monitoringDuration, model.date.monitoringDurationUnit).format(DATE_FORMAT)
    const calibrationStart = moment(monitoringStart, DATE_FORMAT).subtract(model.date.calibrationDuration, model.date.calibrationDurationUnit).format(DATE_FORMAT)

    const selectedBands$ = selectedBands && selectedBands.length
        ? of(selectedBands)
        : imageFactory(model.reference).getBands$()

    const bands$ = selectedBands$.pipe(
        map(selectedBands => ({selectedBands, baseBands}))
    )
    const ccdc$ = bands$.pipe(
        map(({selectedBands, baseBands}) =>
            ({
                selectedBands,
                baseBands,
                ccdc: imageFactory(model.reference, {selection: baseBands})
            }))
    )

    const getObservations$ = geometry => {
        const collectionRecipe = {model: {
            dates: {
                startDate: calibrationStart,
                endDate: monitoringEnd
            },
            sources: model.sources,
            options: model.options
        }}
        return getCollection$({
            recipe: collectionRecipe,
            geometry,
            bands: [band]
        })
    }

    return {
        getImage$: function () {
            return ccdc$.pipe(
                switchMap(({ccdc}) => {
                    return forkJoin({
                        segmentsImage: ccdc.getImage$(),
                        geometry: ccdc.getGeometry$()
                    }).pipe(
                        switchMap(({segmentsImage, geometry}) => {
                            return getObservations$(geometry).pipe(
                                map(collection => analyzeChanges({
                                    segmentsImage,
                                    collection,
                                    monitoringStart,
                                    band,
                                    minConfidence: 5,  // TODO Pick the rest from ops
                                    minNumberOfChanges: 3,
                                    numberOfObservations: 3,
                                    // mmu: 1800, // In square meters. A 5 Landsat pixels: 5 x 30 x 30 = 4500
                                    aggressive: false,
                                }))
                            )
                        })
                    )
                })
            )
        },
        getBands$() {
            return of([
                'confidence',
                'difference',
                'detection_count',
                'monitoring_observation_count',
                'calibration_observation_count',
                'last_stable_date',
                'first_detection_date',
                'last_detection_date',
            ])
        },
        getGeometry$() {
            return imageFactory(model.reference).getGeometry$()
        },

        histogramMaxPixels: model.source === 'RECIPE_REF' ? 1e3 : null
    }
}

function analyzeChanges(args) {
    var segmentsImage = args.segmentsImage
    var collection = args.collection
    var monitoringStart = args.monitoringStart
    var band = args.band
    var minConfidence = args.minConfidence || 2.5
    var numberOfObservations = args.numberOfObservations || 3
    var minNumberOfChanges = args.minNumberOfChanges || 3
    var mmu = args.mmu
    var aggressive = args.aggressive
    var debug = args.debug

    if (!segmentsImage)
        throw Error('A segmentsImage argument is required')
    if (!collection)
        throw Error('A collection argument is required')
    if (!monitoringStart)
        throw Error('A monitoringStart argument is required')
    if (!band)
        throw Error('A band argument is required')

    var segments = temporalSegmentation.Segments(segmentsImage, 1)

    var changesAndMasked = collection
        .map(function(image) {
            var date = image.date()

            var dateYearAgo = image.date().advance(-1, 'year')
            var segment = segments.findByDate(dateYearAgo, 'previous')
            var slice = segment.slice({strategy: 'closest'})
                .addBands(segment.toImage(`${band}_rmse`))

            var fractionalYear = toFractionalYear(date)
            var difference = slice.select(band).subtract(image.select(band))
            var confidence = difference.abs().divide(slice.select(`${band}_rmse`))
            var change = confidence
                .gt(minConfidence)
                .rename('change')
            return change
                .addBands(
                    difference.rename('difference')
                )
                .addBands(
                    confidence.rename('confidence')
                )
                .addBands(
                    ee.Image(fractionalYear).rename('date').float()
                )
                .unmask(-1)
        })
        .toArrayPerBand(0)

    var changes1d = changesAndMasked
        .arrayMask(changesAndMasked.select('change').neq(-1))

    var changes2d = ee.ImageCollection(
        ee.List.sequence(0, ee.Number(numberOfObservations).subtract(1))
            .map(function (offset) {
                offset = ee.Number(offset)
                var start = ee.Image(offset).int8()
                var end = start.subtract(numberOfObservations).add(1)
                    .where( // End for last offset is 0, make sure it's the last index
                        offset.add(1).eq(numberOfObservations),
                        changes1d.select('change').arrayLength(0)
                    )
                return changes1d.arraySlice(0, start, end)
            })
    ).toArrayPerBand(1)

    if (debug) {
        Map.addLayer(changes1d, null, 'changes1d', false)
        Map.addLayer(changes2d, null, 'changes2d', false)
    }

    var monitoringObservations = changes1d
        .select(0)
        .arrayMask(changes1d.select('date').gte(toFractionalYear(monitoringStart)))

    var monitoringObservationCount = monitoringObservations
        .updateMask(monitoringObservations.select(0).arrayLength(0))
        .arrayReduce(ee.Reducer.count(), [0])
        .arrayGet([0])
        .unmask(0)
        .rename('monitoring_observation_count')

    var calibrationObservations = changes1d
        .select(0)
        .arrayMask(changes1d.select('date').lt(toFractionalYear(monitoringStart)))

    var calibrationObservationCount = calibrationObservations
        .updateMask(calibrationObservations.select(0).arrayLength(0))
        .arrayReduce(ee.Reducer.count(), [0])
        .arrayGet([0])
        .unmask(0)
        .rename('calibration_observation_count')

    var stableCount = changes2d
        .select('change')
        .not()
        .arrayAccum(1, ee.Reducer.sum())
        .arraySlice(1, -1)

    var lastStable1d = changes2d
        .arrayMask(stableCount.gte(minNumberOfChanges))
        .arraySlice(0, -1) // Last row
        .arrayProject([1]) // 2d to 1d

    var lastStableDate = lastStable1d
        .select('date')
        .arrayMask(lastStable1d.select('change').not()) // Mask out any eventual changes
        .arrayGet([-1]) // Last date
        .rename('last_stable_date')

    var changeCount = changes2d
        .select('change')
        .arrayAccum(1, ee.Reducer.sum())
        .arraySlice(1, -1)

    var changeCountMask = changeCount.gte(minNumberOfChanges)
    var changeDateMask = changes2d
        .select('date')
        .gt(aggressive ? -1 : lastStableDate)
        .arraySlice(1, -1) // Last column in changes2d is after lastStableDate

    var changes = changes2d.arrayMask(
        changeCountMask
            .and(changeDateMask)
    )
    changes = changes
        .updateMask(changes.select(0).arrayLength(0))
    var firstChange1d = changes
        .arraySlice(0, 0, 1) // First row
        .arrayProject([1]) // 2d to 1d

    var firstDetectionDate = firstChange1d
        .select('date')
        .arrayMask(firstChange1d.select('change')) // Mask out any eventual stable observations
        .arrayGet([0]) // First date
        .rename('first_detection_date')

    var confirmationDate = firstChange1d
        .select('date')
        .arrayMask(firstChange1d.select('change')) // Mask out any eventual stable observations
        .arrayGet([-1]) // Last date
        .rename('confirmation_date')

    var first = changes2d // If first confirmation date altogether is different from first actual confirmation
        .arrayMask(changeCountMask)
        .arraySlice(0, 0, 1) // First row
        .arrayProject([1]) // 2d to 1d
    var unstable = first
        .select('date')
        .arrayMask(first.select('change')) // Mask out any eventual stable observations
        .arrayGet([-1]) // Last date
        .neq(confirmationDate)
        .rename('unstable')

    var lastChange1d = changes
        .arraySlice(0, -1) // Last row
        .arrayProject([1]) // 2d to 1d

    var lastDetectionDate = lastChange1d
        .select('date')
        .arrayMask(lastChange1d.select('change')) // Mask out any eventual stable observations
        .arrayGet([-1]) // Last date
        .rename('last_detection_date')

    var changeMask = changes1d.select('date').gte(firstDetectionDate).and(
        changes1d.select('date').lte(lastDetectionDate)
    ).or(aggressive ? 1 : 0)

    var detectionCount = changes1d
        .select('date')
        .arrayMask(changeMask)
        .arrayReduce(ee.Reducer.count(), [0])
        .arrayGet([0])
        .unmask(0)
        .rename('detection_count')

    var mean = changes1d
        .select(['confidence', 'difference'])
        .arrayMask(changeMask)
        .arrayReduce(ee.Reducer.mean(), [0])
        .arrayGet([0])
        .rename(['confidence', 'difference'])

    var changeBands = firstDetectionDate
        .addBands(confirmationDate)
        .addBands(lastDetectionDate)
        .addBands(mean)
        .addBands(detectionCount)
        .updateMask(
            confirmationDate.gte(toFractionalYear(monitoringStart))
                .and(unstable.not())
                .or(aggressive ? 1 : 0)
        )
    var mmuMask = mmu
        ? changeBands.select(0).mask().byte()
            .addBands(ee.Image.pixelArea())
            .updateMask(changeBands.select(0).mask())
            .reduceConnectedComponents({
                reducer: ee.Reducer.sum(),
                maxSize: 128
            })
            .gte(mmu)
        : ee.Image(1)
    var filteredChangeBands = changeBands
        .updateMask(mmuMask)
        .unmask(0)

    return lastStableDate
        .addBands(filteredChangeBands)
        .addBands(monitoringObservationCount)
        .addBands(calibrationObservationCount)
        .updateMask(segmentsImage.select(0).mask())
}

function toFractionalYear(date) {
    date = ee.Date(date)
    return date.get('year').add(date.getFraction('year'))
}

module.exports = changeAlerts
<commit_msg>Protected against empty arrays.<commit_after>const {forkJoin, of, map, switchMap} = require('rxjs')
const ee = require('sepal/ee')
const imageFactory = require('sepal/ee/imageFactory')
const {getCollection$} = require('sepal/ee/timeSeries/collection')
const temporalSegmentation = require('./temporalSegmentation')
const moment = require('moment')
const _ = require('lodash')

const DATE_FORMAT = 'YYYY-MM-DD'

const changeAlerts = (recipe, {selection: selectedBands, baseBands} = {selection: [], baseBands: []}) => {
    const model = recipe.model
    const band = model.sources.band
    const monitoringEnd = model.date.monitoringEnd
    const monitoringStart = moment(monitoringEnd, DATE_FORMAT).subtract(model.date.monitoringDuration, model.date.monitoringDurationUnit).format(DATE_FORMAT)
    const calibrationStart = moment(monitoringStart, DATE_FORMAT).subtract(model.date.calibrationDuration, model.date.calibrationDurationUnit).format(DATE_FORMAT)

    const selectedBands$ = selectedBands && selectedBands.length
        ? of(selectedBands)
        : imageFactory(model.reference).getBands$()

    const bands$ = selectedBands$.pipe(
        map(selectedBands => ({selectedBands, baseBands}))
    )
    const ccdc$ = bands$.pipe(
        map(({selectedBands, baseBands}) =>
            ({
                selectedBands,
                baseBands,
                ccdc: imageFactory(model.reference, {selection: baseBands})
            }))
    )

    const getObservations$ = geometry => {
        const collectionRecipe = {model: {
            dates: {
                startDate: calibrationStart,
                endDate: monitoringEnd
            },
            sources: model.sources,
            options: model.options
        }}
        return getCollection$({
            recipe: collectionRecipe,
            geometry,
            bands: [band]
        })
    }

    return {
        getImage$: function () {
            return ccdc$.pipe(
                switchMap(({ccdc}) => {
                    return forkJoin({
                        segmentsImage: ccdc.getImage$(),
                        geometry: ccdc.getGeometry$()
                    }).pipe(
                        switchMap(({segmentsImage, geometry}) => {
                            return getObservations$(geometry).pipe(
                                map(collection => analyzeChanges({
                                    segmentsImage,
                                    collection,
                                    monitoringStart,
                                    band,
                                    minConfidence: 5,  // TODO Pick the rest from ops
                                    minNumberOfChanges: 3,
                                    numberOfObservations: 3,
                                    // mmu: 1800, // In square meters. A 5 Landsat pixels: 5 x 30 x 30 = 4500
                                    aggressive: false,
                                }))
                            )
                        })
                    )
                })
            )
        },
        getBands$() {
            return of([
                'confidence',
                'difference',
                'detection_count',
                'monitoring_observation_count',
                'calibration_observation_count',
                'last_stable_date',
                'first_detection_date',
                'last_detection_date',
            ])
        },
        getGeometry$() {
            return imageFactory(model.reference).getGeometry$()
        },

        histogramMaxPixels: model.source === 'RECIPE_REF' ? 1e3 : null
    }
}

function analyzeChanges(args) {
    var segmentsImage = args.segmentsImage
    var collection = args.collection
    var monitoringStart = args.monitoringStart
    var band = args.band
    var minConfidence = args.minConfidence || 2.5
    var numberOfObservations = args.numberOfObservations || 3
    var minNumberOfChanges = args.minNumberOfChanges || 3
    var mmu = args.mmu
    var aggressive = args.aggressive
    var debug = args.debug

    if (!segmentsImage)
        throw Error('A segmentsImage argument is required')
    if (!collection)
        throw Error('A collection argument is required')
    if (!monitoringStart)
        throw Error('A monitoringStart argument is required')
    if (!band)
        throw Error('A band argument is required')

    var segments = temporalSegmentation.Segments(segmentsImage, 1)

    var changesAndMasked = collection
        .map(function(image) {
            var date = image.date()

            var dateYearAgo = image.date().advance(-1, 'year')
            var segment = segments.findByDate(dateYearAgo, 'previous')
            var slice = segment.slice({strategy: 'closest'})
                .addBands(segment.toImage(`${band}_rmse`))

            var fractionalYear = toFractionalYear(date)
            var difference = slice.select(band).subtract(image.select(band))
            var confidence = difference.abs().divide(slice.select(`${band}_rmse`))
            var change = confidence
                .gt(minConfidence)
                .rename('change')
            return change
                .addBands(
                    difference.rename('difference')
                )
                .addBands(
                    confidence.rename('confidence')
                )
                .addBands(
                    ee.Image(fractionalYear).rename('date').float()
                )
                .unmask(-1)
        })
        .toArrayPerBand(0)

    var changes1d = changesAndMasked
        .arrayMask(changesAndMasked.select('change').neq(-1))

    var changes2d = ee.ImageCollection(
        ee.List.sequence(0, ee.Number(numberOfObservations).subtract(1))
            .map(function (offset) {
                offset = ee.Number(offset)
                var start = ee.Image(offset).int8()
                var end = start.subtract(numberOfObservations).add(1)
                    .where( // End for last offset is 0, make sure it's the last index
                        offset.add(1).eq(numberOfObservations),
                        changes1d.select('change').arrayLength(0)
                    )
                return changes1d.arraySlice(0, start, end)
            })
    ).toArrayPerBand(1)

    if (debug) {
        Map.addLayer(changes1d, null, 'changes1d', false)
        Map.addLayer(changes2d, null, 'changes2d', false)
    }

    var monitoringObservationCount = arrayFirst(
        changes1d
            .select(0)
            .arrayMask(changes1d.select('date').gte(toFractionalYear(monitoringStart)))
            .arrayReduce(ee.Reducer.count(), [0]),
        'monitoring_observation_count'
    ).unmask(0)

    var calibrationObservationCount = arrayFirst(
        changes1d
            .select(0)
            .arrayMask(changes1d.select('date').lt(toFractionalYear(monitoringStart)))
            .arrayReduce(ee.Reducer.count(), [0]),
        'calibration_observation_count'
    ).unmask(0)

    var stableCount = changes2d
        .select('change')
        .not()
        .arrayAccum(1, ee.Reducer.sum())
        .arraySlice(1, -1)

    var lastStable1d = changes2d
        .arrayMask(stableCount.gte(minNumberOfChanges))
        .arraySlice(0, -1) // Last row
        .arrayProject([1]) // 2d to 1d

    var lastStableDate = arrayLast(
        lastStable1d
            .select('date')
            .arrayMask(lastStable1d.select('change').not()), // Mask out any eventual changes
        'last_stable_date'
    )

    var changeCount = changes2d
        .select('change')
        .arrayAccum(1, ee.Reducer.sum())
        .arraySlice(1, -1)

    var changeCountMask = changeCount.gte(minNumberOfChanges)
    var changeDateMask = changes2d
        .select('date')
        .gt(aggressive ? -1 : lastStableDate)
        .arraySlice(1, -1) // Last column in changes2d is after lastStableDate

    var changes = changes2d.arrayMask(
        changeCountMask
            .and(changeDateMask)
    )
    changes = changes
        .updateMask(changes.select(0).arrayLength(0))
    var firstChange1d = changes
        .arraySlice(0, 0, 1) // First row
        .arrayProject([1]) // 2d to 1d

    var firstDetectionDate = arrayFirst(
        firstChange1d
            .select('date')
            .arrayMask(firstChange1d.select('change')), // Mask out any eventual stable observations
        'first_detection_date'
    )

    var confirmationDate = arrayLast(
        firstChange1d
            .select('date')
            .arrayMask(firstChange1d.select('change')), // Mask out any eventual stable observations
        'confirmation_date'
    )

    var first = changes2d // If first confirmation date altogether is different from first actual confirmation
        .arrayMask(changeCountMask)
        .arraySlice(0, 0, 1) // First row
        .arrayProject([1]) // 2d to 1d
    var unstable = arrayLast(
        first
            .select('date')
            .arrayMask(first.select('change')) // Mask out any eventual stable observations
            .neq(confirmationDate),
        'unstable'
    )

    var lastChange1d = changes
        .arraySlice(0, -1) // Last row
        .arrayProject([1]) // 2d to 1d

    var lastDetectionDate = arrayLast(
        lastChange1d
            .select('date')
            .arrayMask(lastChange1d.select('change')), // Mask out any eventual stable observations
        'last_detection_date'
    )

    var changeMask = changes1d.select('date').gte(firstDetectionDate).and(
        changes1d.select('date').lte(lastDetectionDate)
    ).or(aggressive ? 1 : 0)

    var detectionCount = arrayFirst(
        changes1d
            .select('date')
            .arrayMask(changeMask)
            .arrayReduce(ee.Reducer.count(), [0]),
        'detection_count'
    ).unmask(0)

    var mean = arrayFirst(
        changes1d
            .select(['confidence', 'difference'])
            .arrayMask(changeMask)
            .arrayReduce(ee.Reducer.mean(), [0]),
        ['confidence', 'difference']
    )

    var changeBands = firstDetectionDate
        .addBands(confirmationDate)
        .addBands(lastDetectionDate)
        .addBands(mean)
        .addBands(detectionCount)
        .updateMask(
            confirmationDate.gte(toFractionalYear(monitoringStart))
                .and(unstable.not())
                .or(aggressive ? 1 : 0)
        )
    var mmuMask = mmu
        ? changeBands.select(0).mask().byte()
            .addBands(ee.Image.pixelArea())
            .updateMask(changeBands.select(0).mask())
            .reduceConnectedComponents({
                reducer: ee.Reducer.sum(),
                maxSize: 128
            })
            .gte(mmu)
        : ee.Image(1)
    var filteredChangeBands = changeBands
        .updateMask(mmuMask)
        .unmask(0)

    return lastStableDate
        .addBands(filteredChangeBands)
        .addBands(monitoringObservationCount)
        .addBands(calibrationObservationCount)
        .updateMask(segmentsImage.select(0).mask())
}

function arrayFirst(image, name) {
    return image
        .updateMask(image.select(0).arrayLength(0))
        .arrayGet([0])
        .rename(name)

}

function arrayLast(image, name) {
    return image
        .updateMask(image.select(0).arrayLength(0))
        .arrayGet([-1])
        .rename(name)
}

function toFractionalYear(date) {
    date = ee.Date(date)
    return date.get('year').add(date.getFraction('year'))
}

module.exports = changeAlerts
<|endoftext|>


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

'use strict';

/*
* When adding modules to the namespace, ensure that they are added in alphabetical order according to module name.
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );

// MAIN //

/**
* Top-level namespace.
*
* @namespace blas
*/
var blas = {};

/**
* @name dasum
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/dasum}
*/
setReadOnly( blas, 'dasum', require( '@stdlib/blas/base/dasum' ) );

/**
* @name daxpy
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/daxpy}
*/
setReadOnly( blas, 'daxpy', require( '@stdlib/blas/base/daxpy' ) );

/**
* @name dcopy
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/dcopy}
*/
setReadOnly( blas, 'dcopy', require( '@stdlib/blas/base/dcopy' ) );

/**
* @name ddot
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/ddot}
*/
setReadOnly( blas, 'ddot', require( '@stdlib/blas/base/ddot' ) );

/**
* @name dscal
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/dscal}
*/
setReadOnly( blas, 'dscal', require( '@stdlib/blas/base/dscal' ) );

/**
* @name dswap
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/dswap}
*/
setReadOnly( blas, 'dswap', require( '@stdlib/blas/base/dswap' ) );

/**
* @name gasum
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/gasum}
*/
setReadOnly( blas, 'gasum', require( '@stdlib/blas/base/gasum' ) );

/**
* @name gaxpy
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/gaxpy}
*/
setReadOnly( blas, 'gaxpy', require( '@stdlib/blas/base/gaxpy' ) );

/**
* @name gcopy
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/gcopy}
*/
setReadOnly( blas, 'gcopy', require( '@stdlib/blas/base/gcopy' ) );

/**
* @name gdot
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/gdot}
*/
setReadOnly( blas, 'gdot', require( '@stdlib/blas/base/gdot' ) );

/**
* @name gscal
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/gscal}
*/
setReadOnly( blas, 'gscal', require( '@stdlib/blas/base/gscal' ) );

/**
* @name gswap
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/gswap}
*/
setReadOnly( blas, 'gswap', require( '@stdlib/blas/base/gswap' ) );

/**
* @name sasum
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/sasum}
*/
setReadOnly( blas, 'sasum', require( '@stdlib/blas/base/sasum' ) );

/**
* @name saxpy
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/saxpy}
*/
setReadOnly( blas, 'saxpy', require( '@stdlib/blas/base/saxpy' ) );

/**
* @name scopy
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/scopy}
*/
setReadOnly( blas, 'scopy', require( '@stdlib/blas/base/scopy' ) );

/**
* @name sdot
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/sdot}
*/
setReadOnly( blas, 'sdot', require( '@stdlib/blas/base/sdot' ) );

/**
* @name sscal
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/sscal}
*/
setReadOnly( blas, 'sscal', require( '@stdlib/blas/base/sscal' ) );

/**
* @name sswap
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/sswap}
*/
setReadOnly( blas, 'sswap', require( '@stdlib/blas/base/sswap' ) );

// EXPORTS //

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

'use strict';

/*
* When adding modules to the namespace, ensure that they are added in alphabetical order according to module name.
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );

// MAIN //

/**
* Top-level namespace.
*
* @namespace blas
*/
var blas = {};

/**
* @name dasum
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/dasum}
*/
setReadOnly( blas, 'dasum', require( '@stdlib/blas/base/dasum' ) );

/**
* @name daxpy
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/daxpy}
*/
setReadOnly( blas, 'daxpy', require( '@stdlib/blas/base/daxpy' ) );

/**
* @name dcopy
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/dcopy}
*/
setReadOnly( blas, 'dcopy', require( '@stdlib/blas/base/dcopy' ) );

/**
* @name ddot
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/ddot}
*/
setReadOnly( blas, 'ddot', require( '@stdlib/blas/base/ddot' ) );

/**
* @name dscal
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/dscal}
*/
setReadOnly( blas, 'dscal', require( '@stdlib/blas/base/dscal' ) );

/**
* @name dsdot
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/dsdot}
*/
setReadOnly( blas, 'dsdot', require( '@stdlib/blas/base/dsdot' ) );

/**
* @name dswap
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/dswap}
*/
setReadOnly( blas, 'dswap', require( '@stdlib/blas/base/dswap' ) );

/**
* @name gasum
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/gasum}
*/
setReadOnly( blas, 'gasum', require( '@stdlib/blas/base/gasum' ) );

/**
* @name gaxpy
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/gaxpy}
*/
setReadOnly( blas, 'gaxpy', require( '@stdlib/blas/base/gaxpy' ) );

/**
* @name gcopy
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/gcopy}
*/
setReadOnly( blas, 'gcopy', require( '@stdlib/blas/base/gcopy' ) );

/**
* @name gdot
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/gdot}
*/
setReadOnly( blas, 'gdot', require( '@stdlib/blas/base/gdot' ) );

/**
* @name gscal
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/gscal}
*/
setReadOnly( blas, 'gscal', require( '@stdlib/blas/base/gscal' ) );

/**
* @name gswap
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/gswap}
*/
setReadOnly( blas, 'gswap', require( '@stdlib/blas/base/gswap' ) );

/**
* @name sasum
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/sasum}
*/
setReadOnly( blas, 'sasum', require( '@stdlib/blas/base/sasum' ) );

/**
* @name saxpy
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/saxpy}
*/
setReadOnly( blas, 'saxpy', require( '@stdlib/blas/base/saxpy' ) );

/**
* @name scopy
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/scopy}
*/
setReadOnly( blas, 'scopy', require( '@stdlib/blas/base/scopy' ) );

/**
* @name sdot
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/sdot}
*/
setReadOnly( blas, 'sdot', require( '@stdlib/blas/base/sdot' ) );

/**
* @name sscal
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/sscal}
*/
setReadOnly( blas, 'sscal', require( '@stdlib/blas/base/sscal' ) );

/**
* @name sswap
* @memberof blas
* @readonly
* @type {Function}
* @see {@link module:@stdlib/blas/base/sswap}
*/
setReadOnly( blas, 'sswap', require( '@stdlib/blas/base/sswap' ) );

// EXPORTS //

module.exports = blas;
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React from 'react'
import Dialog from 'part:@sanity/components/dialogs/fullscreen'
import Spinner from 'part:@sanity/components/loading/spinner'

import enhanceWithReferringDocuments from './enhanceWithReferringDocuments'
import DocTitle from './DocTitle'
import ReferringDocumentsList from './ReferringDocumentsList'

export default enhanceWithReferringDocuments(
  class ConfirmDelete extends React.PureComponent {
    static propTypes = {
      onCancel: PropTypes.func.isRequired,
      onConfirm: PropTypes.func.isRequired,
      published: PropTypes.object,
      draft: PropTypes.object,
      referringDocuments: PropTypes.array,
      isCheckingReferringDocuments: PropTypes.bool
    }

    handleAction = action => {
      const {onCancel, onConfirm} = this.props
      if (action.name === 'confirm') {
        onConfirm()
      }
      if (action.name === 'cancel') {
        onCancel()
      }
    }

    render() {
      const {
        isCheckingReferringDocuments,
        referringDocuments,
        draft,
        published,
        onCancel
      } = this.props

      const hasReferringDocuments = referringDocuments.length > 0

      const canContinue = !isCheckingReferringDocuments

      const actions = [
        canContinue && {
          name: 'confirm',
          title: hasReferringDocuments ? 'Try to delete anyway' : 'Delete now'
        },
        {name: 'cancel', title: 'Cancel', kind: 'simple'}
      ].filter(Boolean)

      const title = isCheckingReferringDocuments ? 'Checking…' : 'Confirm delete'

      return (
        <Dialog
          isOpen
          showHeader
          color="danger"
          centered
          title={title}
          onClose={onCancel}
          onAction={this.handleAction}
          actions={actions}
        >
          {isCheckingReferringDocuments && <Spinner message="Looking for referring documents…" />}
          {hasReferringDocuments && (

                Warning: Found{' '}
                {referringDocuments.length === 1
                  ? 'a document'
                  : `${referringDocuments.length} documents`}{' '}
                that refers to {'"'}
                <DocTitle document={draft || published} />
                {'"'}

                You may not be allowed to delete
                {' "'}
                <DocTitle document={draft || published} />
                {'" '}
                as the following document{referringDocuments.length === 1 ? '' : 's'} refers to it:

              <ReferringDocumentsList documents={referringDocuments} />

          )}
          {!isCheckingReferringDocuments &&
            !hasReferringDocuments && (

                  Are you sure you would like to delete the document{' '}

                    <DocTitle document={draft || published} />
                  ?

Careful!

                  If you continue, this document will be permanently gone and not be available to
                  anyone anymore.

            )}
        </Dialog>
      )
    }
  }
)
<commit_msg>[desk-tool] Cleanup delete dialog<commit_after>/* eslint-disable complexity */
import PropTypes from 'prop-types'
import React, {Fragment} from 'react'
import Dialog from 'part:@sanity/components/dialogs/fullscreen'
import Spinner from 'part:@sanity/components/loading/spinner'

import enhanceWithReferringDocuments from './enhanceWithReferringDocuments'
import DocTitle from './DocTitle'
import ReferringDocumentsList from './ReferringDocumentsList'

export default enhanceWithReferringDocuments(
  class ConfirmDelete extends React.PureComponent {
    static propTypes = {
      onCancel: PropTypes.func.isRequired,
      onConfirm: PropTypes.func.isRequired,
      published: PropTypes.object,
      draft: PropTypes.object,
      referringDocuments: PropTypes.array,
      isCheckingReferringDocuments: PropTypes.bool
    }

    handleAction = action => {
      const {onCancel, onConfirm} = this.props
      if (action.name === 'confirm') {
        onConfirm()
      }
      if (action.name === 'cancel') {
        onCancel()
      }
    }

    render() {
      const {
        isCheckingReferringDocuments,
        referringDocuments,
        draft,
        published,
        onCancel
      } = this.props

      const hasReferringDocuments = referringDocuments.length > 0

      const canContinue = !isCheckingReferringDocuments

      const actions = [
        canContinue && {
          name: 'confirm',
          title: hasReferringDocuments ? 'Try to delete anyway' : 'Delete now',
          color: 'danger'
        },
        {name: 'cancel', title: 'Cancel', kind: 'simple'}
      ].filter(Boolean)

      const title = isCheckingReferringDocuments ? 'Checking…' : 'Confirm delete'

      return (
        <Dialog
          isOpen
          showHeader
          color="danger"
          centered
          title={title}
          onClose={onCancel}
          onAction={this.handleAction}
          actions={actions}
        >
          {isCheckingReferringDocuments && <Spinner message="Looking for referring documents…" />}
          {hasReferringDocuments && (

                Warning: Found{' '}
                {referringDocuments.length === 1
                  ? 'a document'
                  : `${referringDocuments.length} documents`}{' '}
                that refers to {'"'}
                <DocTitle document={draft || published} />
                {'"'}

                You may not be allowed to delete
                {' "'}
                <DocTitle document={draft || published} />
                {'" '}
                as the following document{referringDocuments.length === 1 ? '' : 's'} refers to it:

              <ReferringDocumentsList documents={referringDocuments} />

          )}
          {!isCheckingReferringDocuments &&
            !hasReferringDocuments && (
              <Fragment>

                  Are you sure you would like to permanently delete the document

                     “<DocTitle document={draft || published} />”
                  ?

              </Fragment>
            )}
        </Dialog>
      )
    }
  }
)
<|endoftext|>


<commit_before>// @flow

import type {
  Package,
  BuildPlugin,
  DevelopPlugin,
  DeployPlugin,
} from '../../types'

const R = require('ramda')
const readPkg = require('read-pkg')
const TerminalUtils = require('../../terminal')
const ChildProcessUtils = require('../../childProcess')
const DevelopPluginUtils = require('../utils')

type Options = {
  scriptName?: string,
  scriptRunOnce?: boolean,
}

module.exports = function scriptPlugin(
  pkg: Package,
  options: Options,
): BuildPlugin & DevelopPlugin & DeployPlugin {
  if (!options.scriptName || typeof options.scriptName !== 'string') {
    throw new Error(
      `No scriptName was provided for the develop configuration of ${
        pkg.name
      }.`,
    )
  }
  const childProcessMap = {}
  const addChildProcess = (task, processInstance) => {
    childProcessMap[task] = processInstance
  }
  const killChildProcessFor = task => {
    const childProcess = childProcessMap[task]
    if (!childProcess) {
      TerminalUtils.verbose(
        `No running "${task}" script process for ${pkg.name} to kill`,
      )
      return Promise.resolve()
    }
    return DevelopPluginUtils.killChildProcess(pkg, childProcess).then(() => {
      TerminalUtils.verbose(
        `Killed "${task}" script process for ${pkg.name} successfully`,
      )
      if (childProcessMap[task]) {
        delete childProcessMap[task]
      }
    })
  }

  const pkgJson = readPkg.sync(pkg.paths.packageJson)

  const returnAPI = {
    kill: () => killChildProcessFor(pkg),
  }

  const runScript = task => async () => {
    const existingProcess = childProcessMap[pkg.name]
    if (options.scriptRunOnce && existingProcess) {
      return returnAPI
    }

    if (existingProcess) {
      await killChildProcessFor(pkg)
    }

    await new Promise((resolve, reject) => {
      const scriptCmd = R.path(['scripts', options.scriptName], pkgJson)
      if (!scriptCmd || R.isEmpty(scriptCmd)) {
        throw new Error(
          `Could not resolve script named "${options.scriptName}" on ${
            pkg.name
          }`,
        )
      }

      TerminalUtils.info(
        `Executing script "${options.scriptName}" for ${pkg.name}`,
      )

      const childProcess = ChildProcessUtils.spawn(
        'npm',
        ['run', options.scriptName],
        {
          stdio: 'inherit',
          cwd: pkg.paths.packageRoot,
        },
      )
      childProcess.catch(err => {
        TerminalUtils.verbose(
          `Error executing script "${options.scriptName}" for ${pkg.name}`,
        )
        reject(err)
      })

      // Give the catch above a tick of space, so that it can resolve any
      // error that may have occurred
      process.nextTick(() => {
        childProcess.on('close', () => {
          TerminalUtils.verbose(
            `Stopped script "${options.scriptName}" process for ${pkg.name}`,
          )
        })
        addChildProcess(task, childProcess)
        resolve()
      })
    })

    return returnAPI
  }

  return {
    name: 'constellate-core-plugin/script',
    build: runScript('build'),
    clean: runScript('clean'),
    develop: runScript('develop'),
    deploy: runScript('deploy'),
  }
}
<commit_msg>Fixes script plugin.<commit_after>// @flow

import type { ChildProcess } from 'child_process'
import type {
  Package,
  BuildPlugin,
  DevelopPlugin,
  DeployPlugin,
} from '../../types'

const R = require('ramda')
const readPkg = require('read-pkg')
const TerminalUtils = require('../../terminal')
const ChildProcessUtils = require('../../childProcess')
const DevelopPluginUtils = require('../utils')

type Options = {
  scriptName?: string,
  scriptRunOnce?: boolean,
}

type TaskName = 'build' | 'develop' | 'deploy' | 'clean'

type ChildProcessMap = {
  [key: TaskName]: {
    [key: Package]: ChildProcess,
  },
}

module.exports = function scriptPlugin(
  pkg: Package,
  options: Options,
): BuildPlugin & DevelopPlugin & DeployPlugin {
  if (!options.scriptName || typeof options.scriptName !== 'string') {
    throw new Error(
      `No scriptName was provided for the develop configuration of ${
        pkg.name
      }.`,
    )
  }
  const childProcessMap: ChildProcessMap = {
    build: {},
    clean: {},
    develop: {},
    deploy: {},
  }

  const addChildProcess = (task: TaskName, processInstance: ChildProcess) => {
    childProcessMap[task][pkg] = processInstance
  }

  const getChildProcess = (task: TaskName) => childProcessMap[task][pkg]

  const killChildProcessFor = (task: TaskName) => {
    const childProcess = getChildProcess(task)
    if (!childProcess) {
      TerminalUtils.verbose(
        `No running "${task}" script process for ${pkg.name} to kill`,
      )
      return Promise.resolve()
    }
    return DevelopPluginUtils.killChildProcess(pkg, childProcess).then(() => {
      TerminalUtils.verbose(
        `Killed "${task}" script process for ${pkg.name} successfully`,
      )
      if (childProcessMap[task]) {
        delete childProcessMap[task]
      }
    })
  }

  const pkgJson = readPkg.sync(pkg.paths.packageJson)

  const runScript = (task: TaskName) => async () => {
    const returnAPI = {
      kill: () => killChildProcessFor(task),
    }

    const existingProcess = getChildProcess(task)
    if (options.scriptRunOnce && existingProcess) {
      return returnAPI
    }

    if (existingProcess) {
      await killChildProcessFor(task)
    }

    await new Promise((resolve, reject) => {
      const scriptCmd = R.path(['scripts', options.scriptName], pkgJson)
      if (!scriptCmd || R.isEmpty(scriptCmd)) {
        throw new Error(
          `Could not resolve script named "${options.scriptName}" on ${
            pkg.name
          }`,
        )
      }

      TerminalUtils.info(
        `Executing script "${options.scriptName}" for ${pkg.name}`,
      )

      const childProcess = ChildProcessUtils.spawn(
        'npm',
        ['run', options.scriptName],
        {
          stdio: 'inherit',
          cwd: pkg.paths.packageRoot,
        },
      )
      childProcess.catch(err => {
        TerminalUtils.verbose(
          `Error executing script "${options.scriptName}" for ${pkg.name}`,
        )
        reject(err)
      })

      // Give the catch above a tick of space, so that it can resolve any
      // error that may have occurred
      process.nextTick(() => {
        childProcess.on('close', () => {
          TerminalUtils.verbose(
            `Stopped script "${options.scriptName}" process for ${pkg.name}`,
          )
        })
        addChildProcess(task, childProcess)
        resolve()
      })
    })

    return returnAPI
  }

  return {
    name: 'constellate-core-plugin/script',
    build: runScript('build'),
    clean: runScript('clean'),
    develop: runScript('develop'),
    deploy: runScript('deploy'),
  }
}
<|endoftext|>


<commit_before><commit_msg>put videos onto dashboard namespace for embed case<commit_after><|endoftext|>


<commit_before>'use strict';
/* global describe, it, beforeEach */
/* eslint react/jsx-boolean-value: 0 */

// Copied from Async-test verbatim; may need to be reevaluated later.
var jsdomHelper = require('../testHelpers/jsdomHelper');
jsdomHelper();
var unexpected = require('unexpected');
var unexpectedDom = require('unexpected-dom');
var unexpectedReact = require('unexpected-react');
var expect = unexpected
	.clone()
	.installPlugin(unexpectedDom)
	.installPlugin(unexpectedReact);

var React = require('react');
var ReactDOM = require('react-dom');
var TestUtils = require('react-addons-test-utils');
var Select = require('../src/Select');

describe('Creatable', () => {
	let creatableInstance, creatableNode, filterInputNode, innserSelectInstance, renderer;

	beforeEach(() => renderer = TestUtils.createRenderer());

	const defaultOptions = [
		{ value: 'one', label: 'One' },
		{ value: 'two', label: '222' },
		{ value: 'three', label: 'Three' },
		{ value: 'four', label: 'AbcDef' }
	];

	function createControl (props = {}) {
		props.options = props.options || defaultOptions;
		creatableInstance = TestUtils.renderIntoDocument(
			<Select.Creatable {...props} />
		);
		creatableNode = ReactDOM.findDOMNode(creatableInstance);
		innserSelectInstance = creatableInstance.select;
		findAndFocusInputControl();
	};

	function findAndFocusInputControl () {
		filterInputNode = creatableNode.querySelector('input');
		if (filterInputNode) {
			TestUtils.Simulate.focus(filterInputNode);
		}
	};

	function typeSearchText (text) {
		TestUtils.Simulate.change(filterInputNode, { target: { value: text } });
	};

	it('should render a decorated Select (with passed through properties)', () => {
		createControl({
			inputProps: {
				className: 'foo'
			}
		});
		expect(creatableNode.querySelector('.Select-input'), 'to have attributes', {
			class: ['foo']
		});
	});

	it('should add a placeholder "create..." prompt when filter text is entered that does not match any existing options', () => {
		createControl();
		typeSearchText('foo');
		expect(creatableNode.querySelector('.Select-create-option-placeholder'), 'to have text', Select.Creatable.promptTextCreator('foo'));
	});

	it('should not show a "create..." prompt if current filter text is an exact match for an existing option', () => {
		createControl({
			isOptionUnique: () => false
		});
		typeSearchText('existing');
		expect(creatableNode.querySelector('.Select-menu-outer').textContent, 'not to equal', Select.Creatable.promptTextCreator('existing'));
	});

	it('should filter the "create..." prompt using both filtered options and currently-selected options', () => {
		let isOptionUniqueParams;
		createControl({
			filterOptions: () => [
				{ value: 'one', label: 'One' }
			],
			isOptionUnique: (params) => {
				isOptionUniqueParams = params;
			},
			multi: true,
			options: [
				{ value: 'one', label: 'One' },
				{ value: 'two', label: 'Two' }
			],
			value: [
				{ value: 'three', label: 'Three' }
			]
		});
		typeSearchText('test');
		const { options } = isOptionUniqueParams;
		const values = options.map(option => option.value);
		expect(values, 'to have length', 2);
		expect(values, 'to contain', 'one');
		expect(values, 'to contain', 'three');
	});

	it('should guard against invalid values returned by filterOptions', () => {
		createControl({
			filterOptions: () => null
		});
		typeSearchText('test');;
	});

	it('should not show a "create..." prompt if current filter text is not a valid option (as determined by :isValidNewOption prop)', () => {
		createControl({
			isValidNewOption: () => false
		});
		typeSearchText('invalid');
		expect(creatableNode.querySelector('.Select-menu-outer').textContent, 'not to equal', Select.Creatable.promptTextCreator('invalid'));
	});

	it('should create (and auto-select) a new option when placeholder option is clicked', () => {
		let selectedOption;
		const options = [];
		createControl({
			onChange: (option) => selectedOption = option,
			options
		});
		typeSearchText('foo');
		TestUtils.Simulate.mouseDown(creatableNode.querySelector('.Select-create-option-placeholder'));
		expect(options, 'to have length', 1);
		expect(options[0].label, 'to equal', 'foo');
		expect(selectedOption, 'to be', options[0]);
	});

	it('should create (and auto-select) a new option when ENTER is pressed while placeholder option is selected', () => {
		let selectedOption;
		const options = [];
		createControl({
			onChange: (option) => selectedOption = option,
			options,
			shouldKeyDownEventCreateNewOption: () => true
		});
		typeSearchText('foo');
		TestUtils.Simulate.keyDown(filterInputNode, { keyCode: 13 });
		expect(options, 'to have length', 1);
		expect(options[0].label, 'to equal', 'foo');
		expect(selectedOption, 'to be', options[0]);
	});

	it('should not create a new option if the placeholder option is not selected but should select the focused option', () => {
		const options = [{ label: 'One', value: 1 }];
		createControl({
			options,
			shouldKeyDownEventCreateNewOption: ({ keyCode }) => keyCode === 13
		});
		typeSearchText('on'); // ['Create option "on"', 'One']
		TestUtils.Simulate.keyDown(filterInputNode, { keyCode: 40, key: 'ArrowDown' }); // Select 'One'
		TestUtils.Simulate.keyDown(filterInputNode, { keyCode: 13 });
		expect(options, 'to have length', 1);
	});

	it('should allow a custom select type to be rendered via the :children property', () => {
		let childProps;
		createControl({
			children: (props) => {
				childProps = props;
				return
faux select
;
			}
		});
		expect(creatableNode.textContent, 'to equal', 'faux select');
		expect(childProps.allowCreate, 'to equal', true);
	});

	it('default :children function renders a Select component', () => {
		createControl();
		expect(creatableNode.className, 'to contain', 'Select');
	});

	it('default :isOptionUnique function should do a simple equality check for value and label', () => {
		const options = [
			newOption('foo', 1),
			newOption('bar', 2),
			newOption('baz', 3)
		];

		function newOption (label, value) {
			return { label, value };
		};

		function test (option) {
			return Select.Creatable.isOptionUnique({
				labelKey: 'label',
				option,
				options,
				valueKey: 'value'
			});
		};

		expect(test(newOption('foo', 0)), 'to be', false);
		expect(test(newOption('qux', 1)), 'to be', false);
		expect(test(newOption('qux', 4)), 'to be', true);
		expect(test(newOption('Foo', 11)), 'to be', true);
	});

	it('default :isValidNewOption function should just ensure a non-empty string is provided', () => {
		function test (label) {
			return Select.Creatable.isValidNewOption({ label });
		};

		expect(test(''), 'to be', false);
		expect(test('a'), 'to be', true);
		expect(test(' '), 'to be', true);
	});

	it('default :newOptionCreator function should create an option with a :label and :value equal to the label string', () => {
		const option = Select.Creatable.newOptionCreator({
			label: 'foo',
			labelKey: 'label',
			valueKey: 'value'
		});
		expect(option.className, 'to equal', 'Select-create-option-placeholder');
		expect(option.label, 'to equal', 'foo');
		expect(option.value, 'to equal', 'foo');
	});

	it('default :shouldKeyDownEventCreateNewOption function should accept TAB, ENTER, and comma keys', () => {
		function test (keyCode) {
			return Select.Creatable.shouldKeyDownEventCreateNewOption({ keyCode });
		};

		expect(test(9), 'to be', true);
		expect(test(13), 'to be', true);
		expect(test(188), 'to be', true);
		expect(test(1), 'to be', false);
	});
});
<commit_msg>Add unit test for fix - Creatable doesn't allow input key down handling. #1247<commit_after>'use strict';
/* global describe, it, beforeEach */
/* eslint react/jsx-boolean-value: 0 */

// Copied from Async-test verbatim; may need to be reevaluated later.
var jsdomHelper = require('../testHelpers/jsdomHelper');
jsdomHelper();
var unexpected = require('unexpected');
var unexpectedDom = require('unexpected-dom');
var unexpectedReact = require('unexpected-react');
var expect = unexpected
	.clone()
	.installPlugin(unexpectedDom)
	.installPlugin(unexpectedReact);

var React = require('react');
var ReactDOM = require('react-dom');
var TestUtils = require('react-addons-test-utils');
var Select = require('../src/Select');

describe('Creatable', () => {
	let creatableInstance, creatableNode, filterInputNode, innserSelectInstance, renderer;

	beforeEach(() => renderer = TestUtils.createRenderer());

	const defaultOptions = [
		{ value: 'one', label: 'One' },
		{ value: 'two', label: '222' },
		{ value: 'three', label: 'Three' },
		{ value: 'four', label: 'AbcDef' }
	];

	function createControl (props = {}) {
		props.options = props.options || defaultOptions;
		creatableInstance = TestUtils.renderIntoDocument(
			<Select.Creatable {...props} />
		);
		creatableNode = ReactDOM.findDOMNode(creatableInstance);
		innserSelectInstance = creatableInstance.select;
		findAndFocusInputControl();
	};

	function findAndFocusInputControl () {
		filterInputNode = creatableNode.querySelector('input');
		if (filterInputNode) {
			TestUtils.Simulate.focus(filterInputNode);
		}
	};

	function typeSearchText (text) {
		TestUtils.Simulate.change(filterInputNode, { target: { value: text } });
	};

	it('should render a decorated Select (with passed through properties)', () => {
		createControl({
			inputProps: {
				className: 'foo'
			}
		});
		expect(creatableNode.querySelector('.Select-input'), 'to have attributes', {
			class: ['foo']
		});
	});

	it('should add a placeholder "create..." prompt when filter text is entered that does not match any existing options', () => {
		createControl();
		typeSearchText('foo');
		expect(creatableNode.querySelector('.Select-create-option-placeholder'), 'to have text', Select.Creatable.promptTextCreator('foo'));
	});

	it('should not show a "create..." prompt if current filter text is an exact match for an existing option', () => {
		createControl({
			isOptionUnique: () => false
		});
		typeSearchText('existing');
		expect(creatableNode.querySelector('.Select-menu-outer').textContent, 'not to equal', Select.Creatable.promptTextCreator('existing'));
	});

	it('should filter the "create..." prompt using both filtered options and currently-selected options', () => {
		let isOptionUniqueParams;
		createControl({
			filterOptions: () => [
				{ value: 'one', label: 'One' }
			],
			isOptionUnique: (params) => {
				isOptionUniqueParams = params;
			},
			multi: true,
			options: [
				{ value: 'one', label: 'One' },
				{ value: 'two', label: 'Two' }
			],
			value: [
				{ value: 'three', label: 'Three' }
			]
		});
		typeSearchText('test');
		const { options } = isOptionUniqueParams;
		const values = options.map(option => option.value);
		expect(values, 'to have length', 2);
		expect(values, 'to contain', 'one');
		expect(values, 'to contain', 'three');
	});

	it('should guard against invalid values returned by filterOptions', () => {
		createControl({
			filterOptions: () => null
		});
		typeSearchText('test');;
	});

	it('should not show a "create..." prompt if current filter text is not a valid option (as determined by :isValidNewOption prop)', () => {
		createControl({
			isValidNewOption: () => false
		});
		typeSearchText('invalid');
		expect(creatableNode.querySelector('.Select-menu-outer').textContent, 'not to equal', Select.Creatable.promptTextCreator('invalid'));
	});

	it('should create (and auto-select) a new option when placeholder option is clicked', () => {
		let selectedOption;
		const options = [];
		createControl({
			onChange: (option) => selectedOption = option,
			options
		});
		typeSearchText('foo');
		TestUtils.Simulate.mouseDown(creatableNode.querySelector('.Select-create-option-placeholder'));
		expect(options, 'to have length', 1);
		expect(options[0].label, 'to equal', 'foo');
		expect(selectedOption, 'to be', options[0]);
	});

	it('should create (and auto-select) a new option when ENTER is pressed while placeholder option is selected', () => {
		let selectedOption;
		const options = [];
		createControl({
			onChange: (option) => selectedOption = option,
			options,
			shouldKeyDownEventCreateNewOption: () => true
		});
		typeSearchText('foo');
		TestUtils.Simulate.keyDown(filterInputNode, { keyCode: 13 });
		expect(options, 'to have length', 1);
		expect(options[0].label, 'to equal', 'foo');
		expect(selectedOption, 'to be', options[0]);
	});

	it('should not create a new option if the placeholder option is not selected but should select the focused option', () => {
		const options = [{ label: 'One', value: 1 }];
		createControl({
			options,
			shouldKeyDownEventCreateNewOption: ({ keyCode }) => keyCode === 13
		});
		typeSearchText('on'); // ['Create option "on"', 'One']
		TestUtils.Simulate.keyDown(filterInputNode, { keyCode: 40, key: 'ArrowDown' }); // Select 'One'
		TestUtils.Simulate.keyDown(filterInputNode, { keyCode: 13 });
		expect(options, 'to have length', 1);
	});

	it('should allow a custom select type to be rendered via the :children property', () => {
		let childProps;
		createControl({
			children: (props) => {
				childProps = props;
				return
faux select
;
			}
		});
		expect(creatableNode.textContent, 'to equal', 'faux select');
		expect(childProps.allowCreate, 'to equal', true);
	});

	it('default :children function renders a Select component', () => {
		createControl();
		expect(creatableNode.className, 'to contain', 'Select');
	});

	it('default :isOptionUnique function should do a simple equality check for value and label', () => {
		const options = [
			newOption('foo', 1),
			newOption('bar', 2),
			newOption('baz', 3)
		];

		function newOption (label, value) {
			return { label, value };
		};

		function test (option) {
			return Select.Creatable.isOptionUnique({
				labelKey: 'label',
				option,
				options,
				valueKey: 'value'
			});
		};

		expect(test(newOption('foo', 0)), 'to be', false);
		expect(test(newOption('qux', 1)), 'to be', false);
		expect(test(newOption('qux', 4)), 'to be', true);
		expect(test(newOption('Foo', 11)), 'to be', true);
	});

	it('default :isValidNewOption function should just ensure a non-empty string is provided', () => {
		function test (label) {
			return Select.Creatable.isValidNewOption({ label });
		};

		expect(test(''), 'to be', false);
		expect(test('a'), 'to be', true);
		expect(test(' '), 'to be', true);
	});

	it('default :newOptionCreator function should create an option with a :label and :value equal to the label string', () => {
		const option = Select.Creatable.newOptionCreator({
			label: 'foo',
			labelKey: 'label',
			valueKey: 'value'
		});
		expect(option.className, 'to equal', 'Select-create-option-placeholder');
		expect(option.label, 'to equal', 'foo');
		expect(option.value, 'to equal', 'foo');
	});

	it('default :shouldKeyDownEventCreateNewOption function should accept TAB, ENTER, and comma keys', () => {
		function test (keyCode) {
			return Select.Creatable.shouldKeyDownEventCreateNewOption({ keyCode });
		};

		expect(test(9), 'to be', true);
		expect(test(13), 'to be', true);
		expect(test(188), 'to be', true);
		expect(test(1), 'to be', false);
	});

	it('default :onInputKeyDown should run user provided handler.', (done) => {
		createControl({ onInputKeyDown: event => done() });
		return creatableInstance.onInputKeyDown({ keyCode: 97 });
	});
});
<|endoftext|>


<commit_before>/*

    maps.js

    a slippy maps tile client for morphic.js and Snap!

    written by Jens Mönig
    jens@moenig.org

    Copyright (C) 2019 by Jens Mönig

    This file is part of Snap!.

    Snap! is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of
    the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    prerequisites:
    --------------
    needs morphic.js

    credits:
    --------
    https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
*/

/*global modules, Point, newCanvas, radians, StringMorph, normalizeCanvas*/

// Global stuff ////////////////////////////////////////////////////////

modules.maps = '2019-May-28';

// WorldMap /////////////////////////////////////////////////////////////

function WorldMap(host) {
    this.tileServers = {
        OpenStreetMap: {
            url: 'tile.openstreetmap.org',
            type: 'zxy',
            subdomains: ['a', 'b', 'c'],
            key: null,
            min: 0,
            max: 19,
            credits: 'Map data \u00A9 OpenStreetMap contributors' +
                'CC-BY-SA, Imagery \u00A9 Mapnik'
        },
        Wikimedia: {
            url: 'maps.wikimedia.org/osm-intl',
            type: 'zxy',
            subdomains: null,
            key: null,
            min: 0,
            max: 19,
            credits: 'Map data \u00A9 OpenStreetMap contributors, ' +
                'CC-BY-SA, Imagery \u00A9 Wikimedia'
        },
        Watercolor: {
            url: 'stamen-tiles.a.ssl.fastly.net/watercolor',
            type: 'zxy',
            subdomains: null,
            key: null,
            min: 0,
            max: 20,
            credits: 'Map data \u00A9 OpenStreetMap contributors, ' +
                'CC-BY-SA, Imagery \u00A9 Stamen, CC-BY-3.0.'
        },
        'Toner': {
            url: 'stamen-tiles.a.ssl.fastly.net/toner',
            type: 'zxy',
            subdomains: null,
            key: null,
            min: 0,
            max: 20,
            credits: 'Map data \u00A9 OpenStreetMap contributors, ' +
                'CC-BY-SA, Imagery \u00A9 Stamen, CC-BY-3.0.'
        },
        'Terrain': {
            url: 'stamen-tiles.a.ssl.fastly.net/terrain',
            type: 'zxy',
            subdomains: null,
            key: null,
            min: 0,
            max: 16,
            credits: 'Map data \u00A9 OpenStreetMap contributors, ' +
                'CC-BY-SA, Imagery \u00A9 Stamen, CC-BY-3.0.'
        },
        Topographic: {
            url: 'tile.opentopomap.org',
            type: 'zxy',
            subdomains: null,
            key: null,
            min: 0,
            max: 17,
            credits: 'Map data \u00A9 OpenStreetMap contributors, ' +
                'CC-BY-SA, Imagery \u00A9 Opentopomaps'
        },
        Satellite: {
            url: 'services.arcgisonline.com/ArcGIS/rest/services/' +
                'World_Imagery/MapServer/tile',
            type: 'zyx',
            subdomains: null,
            key: null,
            min: 0,
            max: 19,
            credits: 'Imagery \u00A9 ArcGIS'
        },
        Streets: {
            url: 'services.arcgisonline.com/ArcGIS/rest/services/' +
                'World_Street_Map/MapServer/tile',
            type: 'zyx',
            subdomains: null,
            key: null,
            min: 0,
            max: 19,
            credits: 'Imagery \u00A9 ArcGIS'
        },
        Shading: {
            url: 'services.arcgisonline.com/ArcGIS/rest/services/' +
                'World_Topo_Map/MapServer/tile',
            type: 'zyx',
            subdomains: null,
            key: null,
            min: 0,
            max: 19,
            credits: 'Imagery \u00A9 ArcGIS'
        },
        'Mapbox (experimental)': {
            url: 'api.tiles.mapbox.com/v4/mapbox.streets',
            type: 'zxy',
            subdomains: null,
            key: '?access_token=' +
                'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycX' +
                'BndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw',
            min: 0,
            max: 20,
            credits: 'Map data \u00A9 OpenStreetMap contributors, ' +
                'CC-BY-SA, Imagery \u00A9 Mapbox'
        }
    };
    this.api = this.tileServers[host || 'Wikimedia'];
    this.lon = -122.257852;
    this.lat = 37.872099;
    this.zoom = 13;
    this.position = new Point(
        this.tileXfromLon(this.lon),
        this.tileYfromLat(this.lat)
    );
    this.extent = new Point(480, 360);
    this.tileSize = 256;
    this.canvas = null;
    this.creditsTxt = null;
    this.creditsBG = null;
    this.initializeCredits();
    this.loading = 0;
}

WorldMap.prototype.setHost = function (host) {
    this.api = this.tileServers[host || 'Wikimedia'];
    this.initializeCredits();
    this.setZoom(this.zoom);
};

WorldMap.prototype.setView = function (lon, lat) {
    this.lat = lat;
    this.lon = lon;
    this.refresh();
};

WorldMap.prototype.setZoom = function (num) {
    this.zoom = Math.max(Math.min(this.api.max, Math.floor(num)), this.api.min);
    this.refresh();
};

WorldMap.prototype.panBy = function (x, y) {
    this.lon = this.lonFromTileX(this.position.x + (x / this.tileSize));
    this.lat = this.latFromTileY(this.position.y + (y / this.tileSize));
    this.refresh();
};

WorldMap.prototype.refresh = function () {
    this.position = new Point(
        this.wrapTile(this.tileXfromLon(this.lon)),
        this.tileYfromLat(this.lat)
    );
};

WorldMap.prototype.wrapTile = function (n) {
    var max = Math.pow(2, this.zoom);
    return n < 0 ? max + n : n % max;
};

WorldMap.prototype.tileXfromLon = function (lon) {
    return (lon + 180) / 360 * Math.pow(2, this.zoom);
};

WorldMap.prototype.tileYfromLat = function (lat) {
    return (1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 /
        Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 *
        Math.pow(2, this.zoom);
};

WorldMap.prototype.lonFromTileX = function (x) {
    return x / Math.pow(2, this.zoom) * 360 - 180;
};

WorldMap.prototype.latFromTileY = function (y) {
    var n = Math.PI - 2 * Math.PI * y / Math.pow(2, this.zoom);
    return 180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
};

WorldMap.prototype.lonFromSnapX = function (x) {
    return this.lonFromTileX(this.position.x + (x / this.tileSize));
};

WorldMap.prototype.latFromSnapY = function (y) {
    return this.latFromTileY(this.position.y - (y / this.tileSize));
};

WorldMap.prototype.snapXfromLon = function (lon) {
    return (this.tileXfromLon(lon) - this.position.x) * this.tileSize;
};

WorldMap.prototype.snapYfromLat = function (lat) {
    return (this.tileYfromLat(lat) - this.position.y) * -this.tileSize;
};

WorldMap.prototype.distanceInKm = function(lat1, lon1, lat2, lon2) {
    // haversine formula:
    var R = 6371, // radius of the earth in km
        dLat = radians(lat2 - lat1),
        dLon = radians(lon2 - lon1),
        a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.cos(radians(lat1)) * Math.cos(radians(lat2)) *
            Math.sin(dLon/2) * Math.sin(dLon/2),
        c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    return R * c;
};

WorldMap.prototype.render = function () {
    var cntr = this.extent.divideBy(2),
        size = this.tileSize,
        tile = this.position.floor(),
        off = new Point(
            this.position.x % 1,
            this.position.y % 1
        ).multiplyBy(size),
        tileOrigin = cntr.subtract(off),
        tileDistance = tileOrigin.floorDivideBy(size).add(1),
        tileGrid = this.extent.floorDivideBy(size).add(2),
        originTile = tile.subtract(tileDistance),
        mapOrigin = tileOrigin.subtract(
            tileDistance.multiplyBy(size)
        ),
        sub = 0,
        myself = this,
        max = Math.pow(2, this.zoom),
        x, y, img, ctx, tileX, tileY, coords;

    function ok() {
        myself.loading -= 1;
        ctx.drawImage(
            this,
            mapOrigin.x + (this.cx * size),
            mapOrigin.y + (this.cy * size)
        );
        crd();
    }

    function err() {
        myself.loading -= 1;
        crd();
    }

    function crd() {
        if (!myself.loading) {
            myself.addCredits();
        }
    }

    this.canvas = newCanvas(this.extent, true);
    ctx = this.canvas.getContext('2d');
    for (x = 0; x < tileGrid.x; x += 1) {
        for (y = 0; y < tileGrid.y; y += 1) {
            tileX = this.wrapTile(originTile.x + x);
            tileY = originTile.y + y;
            if ((tileX >= 0 && tileX < max) && (tileY >= 0 && tileY < max)) {
                img = new Image();
                img.cx = x;
                img.cy = y;
                img.crossOrigin = ''; // anonymous
                img.onload = ok;
                img.onerror = err;
                myself.loading += 1;
                switch (this.api.type) {
                    case 'zxy':
                        coords = '' + this.zoom + '/' + tileX + '/' + tileY;
                        break;
                    case 'zyx':
                        coords = '' + this.zoom + '/' + tileY + '/' + tileX;
                        break;
                    case 'xyz':
                        coords = '' + tileX + '/' + tileY + '/' + this.zoom ;
                        break;
                }
                img.src = 'https://' +
                    (this.api.subdomains ?
                        this.api.subdomains[sub] + '.'
                            : '') +
                    this.api.url + '/' + coords + '.png' + (this.api.key || '');
                if (this.api.subdomains) {
                    sub += 1;
                    if  (sub === this.api.subdomains.length) {
                        sub = 0;
                    }
                }
            }
        }
    }
};

WorldMap.prototype.initializeCredits = function () {
    var ctx;
    this.creditsTxt = new StringMorph(
        ' ' + this.api.credits + ' ',
        8
    );
    normalizeCanvas(this.creditsTxt.image);
    this.creditsBG = newCanvas(this.creditsTxt.extent(), true);
    ctx = this.creditsBG.getContext('2d');
    ctx.fillStyle = 'white';
    ctx.fillRect(0, 0, this.creditsBG.width, this.creditsBG.height);
};

WorldMap.prototype.addCredits = function () {
    var ctx = this.canvas.getContext('2d'),
        crd = this.creditsTxt.image;
    ctx.globalAlpha = 0.5;
    ctx.drawImage(
        this.creditsBG,
        this.canvas.width - crd.width,
        this.canvas.height - crd.height
    );
    ctx.globalAlpha = 1;
    ctx.drawImage(
        crd,
        this.canvas.width - crd.width,
        this.canvas.height - crd.height
    );
};
<commit_msg>added parseFloat to maps module so it properly parses lat and long from strings<commit_after>/*

    maps.js

    a slippy maps tile client for morphic.js and Snap!

    written by Jens Mönig
    jens@moenig.org

    Copyright (C) 2019 by Jens Mönig

    This file is part of Snap!.

    Snap! is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of
    the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    prerequisites:
    --------------
    needs morphic.js

    credits:
    --------
    https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
*/

/*global modules, Point, newCanvas, radians, StringMorph, normalizeCanvas*/

// Global stuff ////////////////////////////////////////////////////////

modules.maps = '2019-May-28';

// WorldMap /////////////////////////////////////////////////////////////

function WorldMap(host) {
    this.tileServers = {
        OpenStreetMap: {
            url: 'tile.openstreetmap.org',
            type: 'zxy',
            subdomains: ['a', 'b', 'c'],
            key: null,
            min: 0,
            max: 19,
            credits: 'Map data \u00A9 OpenStreetMap contributors' +
                'CC-BY-SA, Imagery \u00A9 Mapnik'
        },
        Wikimedia: {
            url: 'maps.wikimedia.org/osm-intl',
            type: 'zxy',
            subdomains: null,
            key: null,
            min: 0,
            max: 19,
            credits: 'Map data \u00A9 OpenStreetMap contributors, ' +
                'CC-BY-SA, Imagery \u00A9 Wikimedia'
        },
        Watercolor: {
            url: 'stamen-tiles.a.ssl.fastly.net/watercolor',
            type: 'zxy',
            subdomains: null,
            key: null,
            min: 0,
            max: 20,
            credits: 'Map data \u00A9 OpenStreetMap contributors, ' +
                'CC-BY-SA, Imagery \u00A9 Stamen, CC-BY-3.0.'
        },
        'Toner': {
            url: 'stamen-tiles.a.ssl.fastly.net/toner',
            type: 'zxy',
            subdomains: null,
            key: null,
            min: 0,
            max: 20,
            credits: 'Map data \u00A9 OpenStreetMap contributors, ' +
                'CC-BY-SA, Imagery \u00A9 Stamen, CC-BY-3.0.'
        },
        'Terrain': {
            url: 'stamen-tiles.a.ssl.fastly.net/terrain',
            type: 'zxy',
            subdomains: null,
            key: null,
            min: 0,
            max: 16,
            credits: 'Map data \u00A9 OpenStreetMap contributors, ' +
                'CC-BY-SA, Imagery \u00A9 Stamen, CC-BY-3.0.'
        },
        Topographic: {
            url: 'tile.opentopomap.org',
            type: 'zxy',
            subdomains: null,
            key: null,
            min: 0,
            max: 17,
            credits: 'Map data \u00A9 OpenStreetMap contributors, ' +
                'CC-BY-SA, Imagery \u00A9 Opentopomaps'
        },
        Satellite: {
            url: 'services.arcgisonline.com/ArcGIS/rest/services/' +
                'World_Imagery/MapServer/tile',
            type: 'zyx',
            subdomains: null,
            key: null,
            min: 0,
            max: 19,
            credits: 'Imagery \u00A9 ArcGIS'
        },
        Streets: {
            url: 'services.arcgisonline.com/ArcGIS/rest/services/' +
                'World_Street_Map/MapServer/tile',
            type: 'zyx',
            subdomains: null,
            key: null,
            min: 0,
            max: 19,
            credits: 'Imagery \u00A9 ArcGIS'
        },
        Shading: {
            url: 'services.arcgisonline.com/ArcGIS/rest/services/' +
                'World_Topo_Map/MapServer/tile',
            type: 'zyx',
            subdomains: null,
            key: null,
            min: 0,
            max: 19,
            credits: 'Imagery \u00A9 ArcGIS'
        },
        'Mapbox (experimental)': {
            url: 'api.tiles.mapbox.com/v4/mapbox.streets',
            type: 'zxy',
            subdomains: null,
            key: '?access_token=' +
                'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycX' +
                'BndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw',
            min: 0,
            max: 20,
            credits: 'Map data \u00A9 OpenStreetMap contributors, ' +
                'CC-BY-SA, Imagery \u00A9 Mapbox'
        }
    };
    this.api = this.tileServers[host || 'Wikimedia'];
    this.lon = -122.257852;
    this.lat = 37.872099;
    this.zoom = 13;
    this.position = new Point(
        this.tileXfromLon(this.lon),
        this.tileYfromLat(this.lat)
    );
    this.extent = new Point(480, 360);
    this.tileSize = 256;
    this.canvas = null;
    this.creditsTxt = null;
    this.creditsBG = null;
    this.initializeCredits();
    this.loading = 0;
}

WorldMap.prototype.setHost = function (host) {
    this.api = this.tileServers[host || 'Wikimedia'];
    this.initializeCredits();
    this.setZoom(this.zoom);
};

WorldMap.prototype.setView = function (lon, lat) {
    this.lat = lat;
    this.lon = lon;
    this.refresh();
};

WorldMap.prototype.setZoom = function (num) {
    this.zoom = Math.max(Math.min(this.api.max, Math.floor(num)), this.api.min);
    this.refresh();
};

WorldMap.prototype.panBy = function (x, y) {
    this.lon = this.lonFromTileX(this.position.x + (x / this.tileSize));
    this.lat = this.latFromTileY(this.position.y + (y / this.tileSize));
    this.refresh();
};

WorldMap.prototype.refresh = function () {
    this.position = new Point(
        this.wrapTile(this.tileXfromLon(this.lon)),
        this.tileYfromLat(this.lat)
    );
};

WorldMap.prototype.wrapTile = function (n) {
    var max = Math.pow(2, this.zoom);
    return n < 0 ? max + n : n % max;
};

WorldMap.prototype.tileXfromLon = function (lon) {
    return (parseFloat(lon) + 180) / 360 * Math.pow(2, this.zoom);
};

WorldMap.prototype.tileYfromLat = function (lat) {
    return (1 - Math.log(Math.tan(parseFloat(lat) * Math.PI / 180) + 1 /
        Math.cos(parseFloat(lat) * Math.PI / 180)) / Math.PI) / 2 *
        Math.pow(2, this.zoom);
};

WorldMap.prototype.lonFromTileX = function (x) {
    return x / Math.pow(2, this.zoom) * 360 - 180;
};

WorldMap.prototype.latFromTileY = function (y) {
    var n = Math.PI - 2 * Math.PI * y / Math.pow(2, this.zoom);
    return 180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
};

WorldMap.prototype.lonFromSnapX = function (x) {
    return this.lonFromTileX(this.position.x + (x / this.tileSize));
};

WorldMap.prototype.latFromSnapY = function (y) {
    return this.latFromTileY(this.position.y - (y / this.tileSize));
};

WorldMap.prototype.snapXfromLon = function (lon) {
    return (this.tileXfromLon(lon) - this.position.x) * this.tileSize;
};

WorldMap.prototype.snapYfromLat = function (lat) {
    return (this.tileYfromLat(lat) - this.position.y) * -this.tileSize;
};

WorldMap.prototype.distanceInKm = function(lat1, lon1, lat2, lon2) {
    // haversine formula:
    var R = 6371, // radius of the earth in km
        dLat = radians(lat2 - lat1),
        dLon = radians(lon2 - lon1),
        a = Math.sin(dLat/2) * Math.sin(dLat/2) +
            Math.cos(radians(lat1)) * Math.cos(radians(lat2)) *
            Math.sin(dLon/2) * Math.sin(dLon/2),
        c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    return R * c;
};

WorldMap.prototype.render = function () {
    var cntr = this.extent.divideBy(2),
        size = this.tileSize,
        tile = this.position.floor(),
        off = new Point(
            this.position.x % 1,
            this.position.y % 1
        ).multiplyBy(size),
        tileOrigin = cntr.subtract(off),
        tileDistance = tileOrigin.floorDivideBy(size).add(1),
        tileGrid = this.extent.floorDivideBy(size).add(2),
        originTile = tile.subtract(tileDistance),
        mapOrigin = tileOrigin.subtract(
            tileDistance.multiplyBy(size)
        ),
        sub = 0,
        myself = this,
        max = Math.pow(2, this.zoom),
        x, y, img, ctx, tileX, tileY, coords;

    function ok() {
        myself.loading -= 1;
        ctx.drawImage(
            this,
            mapOrigin.x + (this.cx * size),
            mapOrigin.y + (this.cy * size)
        );
        crd();
    }

    function err() {
        myself.loading -= 1;
        crd();
    }

    function crd() {
        if (!myself.loading) {
            myself.addCredits();
        }
    }

    this.canvas = newCanvas(this.extent, true);
    ctx = this.canvas.getContext('2d');
    for (x = 0; x < tileGrid.x; x += 1) {
        for (y = 0; y < tileGrid.y; y += 1) {
            tileX = this.wrapTile(originTile.x + x);
            tileY = originTile.y + y;
            if ((tileX >= 0 && tileX < max) && (tileY >= 0 && tileY < max)) {
                img = new Image();
                img.cx = x;
                img.cy = y;
                img.crossOrigin = ''; // anonymous
                img.onload = ok;
                img.onerror = err;
                myself.loading += 1;
                switch (this.api.type) {
                    case 'zxy':
                        coords = '' + this.zoom + '/' + tileX + '/' + tileY;
                        break;
                    case 'zyx':
                        coords = '' + this.zoom + '/' + tileY + '/' + tileX;
                        break;
                    case 'xyz':
                        coords = '' + tileX + '/' + tileY + '/' + this.zoom ;
                        break;
                }
                img.src = 'https://' +
                    (this.api.subdomains ?
                        this.api.subdomains[sub] + '.'
                            : '') +
                    this.api.url + '/' + coords + '.png' + (this.api.key || '');
                if (this.api.subdomains) {
                    sub += 1;
                    if  (sub === this.api.subdomains.length) {
                        sub = 0;
                    }
                }
            }
        }
    }
};

WorldMap.prototype.initializeCredits = function () {
    var ctx;
    this.creditsTxt = new StringMorph(
        ' ' + this.api.credits + ' ',
        8
    );
    normalizeCanvas(this.creditsTxt.image);
    this.creditsBG = newCanvas(this.creditsTxt.extent(), true);
    ctx = this.creditsBG.getContext('2d');
    ctx.fillStyle = 'white';
    ctx.fillRect(0, 0, this.creditsBG.width, this.creditsBG.height);
};

WorldMap.prototype.addCredits = function () {
    var ctx = this.canvas.getContext('2d'),
        crd = this.creditsTxt.image;
    ctx.globalAlpha = 0.5;
    ctx.drawImage(
        this.creditsBG,
        this.canvas.width - crd.width,
        this.canvas.height - crd.height
    );
    ctx.globalAlpha = 1;
    ctx.drawImage(
        crd,
        this.canvas.width - crd.width,
        this.canvas.height - crd.height
    );
};
<|endoftext|>


<commit_before>/*
 * This file is part of the login-cidadao project or it's bundles.
 *
 * (c) Guilherme Donato <guilhermednt on github>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

$(document).ready(function () {
    $(".intl-tel").intlTelInput({
        preferredCountries: ["br"],
        utilsScript: intlTelInputUtilsScriptUrl
    });

    $("form").submit(function() {
        $('.intl-tel').each(function() {
            $(this).val($(this).intlTelInput("getNumber"));
        });
    });
});
<commit_msg>added formatting on blur #513<commit_after>/*
 * This file is part of the login-cidadao project or it's bundles.
 *
 * (c) Guilherme Donato <guilhermednt on github>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

$(document).ready(function () {
    $(".intl-tel").intlTelInput({
        preferredCountries: ["br"],
        utilsScript: intlTelInputUtilsScriptUrl
    });

    $(".intl-tel").on('blur', function () {
        var currentFormat = ($(this).val()[0] === "+") ? intlTelInputUtils.numberFormat.INTERNATIONAL : intlTelInputUtils.numberFormat.NATIONAL;
        $(this).val($(this).intlTelInput("getNumber", currentFormat));
    });

    $("form").submit(function () {
        $('.intl-tel').each(function () {
            $(this).val($(this).intlTelInput("getNumber"));
        });
    });
});
<|endoftext|>