<commit_before>var DarwinTools = loadExtension("qbs.DarwinTools");
var ModUtils = loadExtension("qbs.ModUtils");
var Process = loadExtension("qbs.Process");
var PropertyList = loadExtension("qbs.PropertyList");

function prepareIbtoold(product, input, outputs) {
    var args = [];

    var outputFormat = ModUtils.moduleProperty(input, "outputFormat");
    if (!["binary1", "xml1", "human-readable-text"].contains(outputFormat))
        throw("Invalid ibtoold output format: " + outputFormat + ". " +
              "Must be in [binary1, xml1, human-readable-text].");

    args.push("--output-format", outputFormat);

    if (ModUtils.moduleProperty(input, "warnings"))
        args.push("--warnings");

    if (ModUtils.moduleProperty(input, "errors"))
        args.push("--errors");

    if (ModUtils.moduleProperty(input, "notices"))
        args.push("--notices");

    if (input.fileTags.contains("assetcatalog")) {
        args.push("--platform", DarwinTools.applePlatformName(product.moduleProperty("qbs", "targetOS")));

        var appIconName = ModUtils.moduleProperty(input, "appIcon");
        if (appIconName)
            args.push("--app-icon", appIconName);

        var launchImageName = ModUtils.moduleProperty(input, "launchImage");
        if (launchImageName)
            args.push("--launch-image", launchImageName);

        // Undocumented but used by Xcode (only for iOS?), probably runs pngcrush or equivalent
        if (ModUtils.moduleProperty(input, "compressPngs"))
            args.push("--compress-pngs");
    } else {
        var sysroot = product.moduleProperty("qbs", "sysroot");
        if (sysroot)
            args.push("--sdk", sysroot);

        args.push("--flatten", ModUtils.moduleProperty(input, "flatten") ? 'YES' : 'NO');

        // --module and --auto-activate-custom-fonts were introduced in Xcode 6.0
        if (ModUtils.moduleProperty(input, "ibtoolVersionMajor") >= 6) {
            var module = ModUtils.moduleProperty(input, "module");
            if (module)
                args.push("--module", module);

            if (ModUtils.moduleProperty(input, "autoActivateCustomFonts"))
                args.push("--auto-activate-custom-fonts");
        }
    }

    // --minimum-deployment-target was introduced in Xcode 5.0
    if (ModUtils.moduleProperty(input, "ibtoolVersionMajor") >= 5) {
        if (product.moduleProperty("cpp", "minimumOsxVersion")) {
            args.push("--minimum-deployment-target");
            args.push(product.moduleProperty("cpp", "minimumOsxVersion"));
        }

        if (product.moduleProperty("cpp", "minimumIosVersion")) {
            args.push("--minimum-deployment-target");
            args.push(product.moduleProperty("cpp", "minimumIosVersion"));
        }
    }

    // --target-device and -output-partial-info-plist were introduced in Xcode 6.0 for ibtool
    if (ModUtils.moduleProperty(input, "ibtoolVersionMajor") >= 6 || input.fileTags.contains("assetcatalog")) {
        args.push("--output-partial-info-plist", outputs.partial_infoplist[0].filePath);

        if (product.moduleProperty("qbs", "targetOS").contains("osx"))
            args.push("--target-device", "mac");

        if (product.moduleProperty("qbs", "targetOS").contains("ios")) {
            // TODO: Only output the devices specified in TARGET_DEVICE_FAMILY
            // We can't get this info from Info.plist keys due to dependency order
            args.push("--target-device", "iphone");
            args.push("--target-device", "ipad");
        }
    }

    return args;
}

function ibtoolVersion(ibtool) {
    var process;
    var version;
    try {
        process = new Process();
        if (process.exec(ibtool, ["--version"], true) !== 0)
            print(process.readStdErr());

        var propertyList = new PropertyList();
        try {
            propertyList.readFromString(process.readStdOut());

            var plist = JSON.parse(propertyList.toJSONString());
            if (plist)
                plist = plist["com.apple.ibtool.version"];
            if (plist)
                version = plist["short-bundle-version"];
        } finally {
            propertyList.clear();
        }
    } finally {
        process.close();
    }
    return version;
}
<commit_msg>Fix wrong property names in ib.js<commit_after>var DarwinTools = loadExtension("qbs.DarwinTools");
var ModUtils = loadExtension("qbs.ModUtils");
var Process = loadExtension("qbs.Process");
var PropertyList = loadExtension("qbs.PropertyList");

function prepareIbtoold(product, input, outputs) {
    var args = [];

    var outputFormat = ModUtils.moduleProperty(input, "outputFormat");
    if (!["binary1", "xml1", "human-readable-text"].contains(outputFormat))
        throw("Invalid ibtoold output format: " + outputFormat + ". " +
              "Must be in [binary1, xml1, human-readable-text].");

    args.push("--output-format", outputFormat);

    if (ModUtils.moduleProperty(input, "warnings"))
        args.push("--warnings");

    if (ModUtils.moduleProperty(input, "errors"))
        args.push("--errors");

    if (ModUtils.moduleProperty(input, "notices"))
        args.push("--notices");

    if (input.fileTags.contains("assetcatalog")) {
        args.push("--platform", DarwinTools.applePlatformName(product.moduleProperty("qbs", "targetOS")));

        var appIconName = ModUtils.moduleProperty(input, "appIconName");
        if (appIconName)
            args.push("--app-icon", appIconName);

        var launchImageName = ModUtils.moduleProperty(input, "launchImageName");
        if (launchImageName)
            args.push("--launch-image", launchImageName);

        // Undocumented but used by Xcode (only for iOS?), probably runs pngcrush or equivalent
        if (ModUtils.moduleProperty(input, "compressPngs"))
            args.push("--compress-pngs");
    } else {
        var sysroot = product.moduleProperty("qbs", "sysroot");
        if (sysroot)
            args.push("--sdk", sysroot);

        args.push("--flatten", ModUtils.moduleProperty(input, "flatten") ? 'YES' : 'NO');

        // --module and --auto-activate-custom-fonts were introduced in Xcode 6.0
        if (ModUtils.moduleProperty(input, "ibtoolVersionMajor") >= 6) {
            var module = ModUtils.moduleProperty(input, "module");
            if (module)
                args.push("--module", module);

            if (ModUtils.moduleProperty(input, "autoActivateCustomFonts"))
                args.push("--auto-activate-custom-fonts");
        }
    }

    // --minimum-deployment-target was introduced in Xcode 5.0
    if (ModUtils.moduleProperty(input, "ibtoolVersionMajor") >= 5) {
        if (product.moduleProperty("cpp", "minimumOsxVersion")) {
            args.push("--minimum-deployment-target");
            args.push(product.moduleProperty("cpp", "minimumOsxVersion"));
        }

        if (product.moduleProperty("cpp", "minimumIosVersion")) {
            args.push("--minimum-deployment-target");
            args.push(product.moduleProperty("cpp", "minimumIosVersion"));
        }
    }

    // --target-device and -output-partial-info-plist were introduced in Xcode 6.0 for ibtool
    if (ModUtils.moduleProperty(input, "ibtoolVersionMajor") >= 6 || input.fileTags.contains("assetcatalog")) {
        args.push("--output-partial-info-plist", outputs.partial_infoplist[0].filePath);

        if (product.moduleProperty("qbs", "targetOS").contains("osx"))
            args.push("--target-device", "mac");

        if (product.moduleProperty("qbs", "targetOS").contains("ios")) {
            // TODO: Only output the devices specified in TARGET_DEVICE_FAMILY
            // We can't get this info from Info.plist keys due to dependency order
            args.push("--target-device", "iphone");
            args.push("--target-device", "ipad");
        }
    }

    return args;
}

function ibtoolVersion(ibtool) {
    var process;
    var version;
    try {
        process = new Process();
        if (process.exec(ibtool, ["--version"], true) !== 0)
            print(process.readStdErr());

        var propertyList = new PropertyList();
        try {
            propertyList.readFromString(process.readStdOut());

            var plist = JSON.parse(propertyList.toJSONString());
            if (plist)
                plist = plist["com.apple.ibtool.version"];
            if (plist)
                version = plist["short-bundle-version"];
        } finally {
            propertyList.clear();
        }
    } finally {
        process.close();
    }
    return version;
}
<|endoftext|>


<commit_before>'use strict';

var typeUtils = require('../compiler/typeUtils.js');
var primUtils = require('./primUtils');
var liftStep = primUtils.liftStep;

function delay1(runtime, startTime, argStreams, baseTopoOrder, result) {
  if (argStreams.length !== 1) {
    throw new Error('got wrong number of arguments');
  }

  var argStream = argStreams[0];

  // create or validate outputStream, set initial value
  // initial output is just initial input
  var argVal = argStream.value;
  if (result) {
    if (result.outputStream.tempo !== 'step') {
      throw new Error('Incorrect output stream tempo');
    }
    result.outputStream.changeValue(argVal, startTime);
  } else {
    result = {
      outputStream: runtime.createStepStream(argVal, startTime),
      deactivator: null,
    };
  }

  if (result.deactivator) { throw new Error('Deactivator should be null'); }
  result.deactivator = function() {
    argStream.removeTrigger(argChangedTrigger);
    if (pendingOutputChangeTask) {
      runtime.priorityQueue.remove(pendingOutputChangeTask);
    }
  };

  var scheduledChanges = []; // ordered list of {time: ..., value: ...}
  var pendingOutputChangeTask = null;

  // if needed, add task for updating output, and update our bookeeping
  var updateTasks = function() {
    if ((scheduledChanges.length > 0) && !pendingOutputChangeTask) {
      var nextChange = scheduledChanges[0];
      // TODO: this should probably call a method of runtime instead of accessing priorityQueue directly
      // TODO: this call could get back a 'task handle' that we use to remove a pending task on deactivate
      var changeTask = {
        time: nextChange.time,
        topoOrder: baseTopoOrder,
        closure: changeOutput,
      };
      runtime.priorityQueue.insert(changeTask);
      pendingOutputChangeTask = changeTask;
    }
  };

  // closure to be called when time has come to change output value
  var changeOutput = function(atTime) {
    if (scheduledChanges.length === 0) {
      throw new Error('no changes to make');
    }

    // pull next change off 'front' of queue
    var nextChange = scheduledChanges.shift();

    // sanity check
    if (atTime !== nextChange.time) {
      throw new Error('times do not match');
    }

    result.outputStream.changeValue(nextChange.value, atTime);

    pendingOutputChangeTask = null;
    updateTasks();
  };

  var argChangedTask = function(atTime) {
    var argVal = argStream.value;
    scheduledChanges.push({
      time: atTime + 1.0, // here is the delay amount
      value: argVal,
    });

    updateTasks();
  };

  // make closure to add task when argument value changes
  var argChangedTrigger = function(atTime) {
    runtime.priorityQueue.insert({
      time: atTime,
      topoOrder: baseTopoOrder,
      closure: argChangedTask,
    });
  };

  // add trigger on argument
  argStream.addTrigger(argChangedTrigger);

  // create deactivator
  result.deactivator = function() {
    argStream.removeTrigger(argChangedTrigger);
    if (pendingOutputChangeTask) {
      runtime.priorityQueue.remove(pendingOutputChangeTask);
    }
  };

  return result;
};

function timeOfLatest(runtime, startTime, argStreams, baseTopoOrder, result) {
  if (argStreams.length !== 1) {
    throw new Error('got wrong number of arguments');
  }

  var argStream = argStreams[0];
  if (argStream.tempo !== 'event') {
    throw new Error('Incorrect input stream tempo');
  }

  // create or validate result, set initial output value
  if (result) {
    if (result.outputStream.tempo !== 'step') {
      throw new Error('Incorrect output stream tempo');
    }
    result.outputStream.changeValue(0, startTime);
  } else {
    result = {
      outputStream: runtime.createStepStream(0, startTime),
      deactivator: null,
    };
  }

  if (result.deactivator) { throw new Error('Deactivator should be null'); }
  result.deactivator = function() {
    argStream.removeTrigger(argChangedTrigger);
  };

  // closure to update output value
  var argChangedTask = function(atTime) {
    result.outputStream.changeValue(atTime-startTime, atTime);
  };

  // make closure to add task when argument value changes
  var argChangedTrigger = function(atTime) {
    runtime.priorityQueue.insert({
      time: atTime,
      topoOrder: baseTopoOrder,
      closure: argChangedTask,
    });
  };

  // add trigger on argument
  argStream.addTrigger(argChangedTrigger);

  return result;
}

function integral(runtime, startTime, argStreams, baseTopoOrder, result) {
  if (argStreams.length !== 3) {
    throw new Error('got wrong number of arguments');
  }

  // split out parameters and validate their tempos
  var integrand = argStreams[0];
  if (integrand.tempo !== 'step') {
    throw new Error('Argument integrand must be step');
  }
  var initialValue = argStreams[1];
  if (initialValue.tempo !== 'const') {
    throw new Error('Argument initialValue must be const');
  }
  var update = argStreams[2];
  if (update.tempo !== 'event') {
    throw new Error('Argument update must be event');
  }

  // create or validate result, set initial output value to zero
  if (result) {
    if (result.outputStream.tempo !== 'step') {
      throw new Error('Incorrect output stream tempo');
    }
    result.outputStream.changeValue(0, startTime);
  } else {
    result = {
      outputStream: runtime.createStepStream(0, startTime),
      deactivator: null,
    };
  }

  // here is our internal state and accumulating machinery
  var sum = initialValue.value; // the integral up to this point
  var lastTime = startTime; // the last time we accumulated to sum
  var lastIntegrandVal = undefined; // the value of integrand at lastTime
  function accumulate(upToTime) {
    if (lastIntegrandVal !== undefined) {
      sum += (upToTime - lastTime)*lastIntegrandVal;
    }
    lastTime = upToTime;
    lastIntegrandVal = integrand.value;
  }

  // If integrand and update inputs change at same time, it seems like it
  //  doesn't matter which we handle first, but we'll have integrand go
  //  first to keep things consistent.

  // task for when integrand changes
  var integrandChangedTask = function(atTime) {
    accumulate(atTime);
    // don't change output
  };

  // trigger for when integrand changes
  var integrandChangedTrigger = function(atTime) {
    runtime.priorityQueue.insert({
      time: atTime,
      topoOrder: baseTopoOrder+'0',
      closure: integrandChangedTask,
    });
  };

  // add trigger on integrand
  integrand.addTrigger(integrandChangedTrigger);

  // task for when update changes
  var updateChangedTask = function(atTime) {
    accumulate(atTime);
    result.outputStream.changeValue(sum, atTime);
  };

  // trigger for when update changes
  var updateChangedTrigger = function(atTime) {
    runtime.priorityQueue.insert({
      time: atTime,
      topoOrder: baseTopoOrder+'1',
      closure: updateChangedTask,
    });
  };

  // add trigger on update
  update.addTrigger(updateChangedTrigger);

  if (result.deactivator) { throw new Error('Deactivator should be null'); }
  result.deactivator = function() {
    update.removeTrigger(updateChangedTrigger);
    integrand.removeTrigger(integrandChangedTrigger);
  };

  return result;
}

module.exports = {
  id: {
    value: liftStep(function(a) { return a; }, 1),
    type: (function() {
      var a = typeUtils.createVariableType();
      return typeUtils.createFunctionType([{type: a}], a);
    })(),
  },
  Vec2: {
    value: liftStep(function(x, y) { return {x: x, y: y}; }, 2),
    // TODO: make yield type more specific when type system supports it
    type: typeUtils.createFunctionType([{type: typeUtils.NUMBER}, {type: typeUtils.NUMBER}], typeUtils.createVariableType()),
  },
  sin: {
    value: liftStep(function(x) { return Math.sin(x); }, 1),
    type: typeUtils.createFunctionType([{type: typeUtils.NUMBER}], typeUtils.NUMBER),
  },
  cos: {
    value: liftStep(function(x) { return Math.cos(x); }, 1),
    type: typeUtils.createFunctionType([{type: typeUtils.NUMBER}], typeUtils.NUMBER),
  },

  delay1: {
    value: delay1,
    type: (function() {
      var a = typeUtils.createVariableType();
      return typeUtils.createFunctionType([{type: a}], a);
    })(),
  },
  timeOfLatest: {
    value: timeOfLatest,
    // TODO: make parameter type more specific when type system supports it
    type: typeUtils.createFunctionType([{type: typeUtils.createVariableType()}], typeUtils.NUMBER),
  },
  integral: {
    value: integral,
    // TODO: make third parameter type more specific when type system supports it
    type: typeUtils.createFunctionType([{type: typeUtils.NUMBER, delayed: true}, {type: typeUtils.NUMBER}, {type: typeUtils.createVariableType()}], typeUtils.NUMBER),
  },
};
<commit_msg>Fix bug with integral initial output<commit_after>'use strict';

var typeUtils = require('../compiler/typeUtils.js');
var primUtils = require('./primUtils');
var liftStep = primUtils.liftStep;

function delay1(runtime, startTime, argStreams, baseTopoOrder, result) {
  if (argStreams.length !== 1) {
    throw new Error('got wrong number of arguments');
  }

  var argStream = argStreams[0];

  // create or validate outputStream, set initial value
  // initial output is just initial input
  var argVal = argStream.value;
  if (result) {
    if (result.outputStream.tempo !== 'step') {
      throw new Error('Incorrect output stream tempo');
    }
    result.outputStream.changeValue(argVal, startTime);
  } else {
    result = {
      outputStream: runtime.createStepStream(argVal, startTime),
      deactivator: null,
    };
  }

  if (result.deactivator) { throw new Error('Deactivator should be null'); }
  result.deactivator = function() {
    argStream.removeTrigger(argChangedTrigger);
    if (pendingOutputChangeTask) {
      runtime.priorityQueue.remove(pendingOutputChangeTask);
    }
  };

  var scheduledChanges = []; // ordered list of {time: ..., value: ...}
  var pendingOutputChangeTask = null;

  // if needed, add task for updating output, and update our bookeeping
  var updateTasks = function() {
    if ((scheduledChanges.length > 0) && !pendingOutputChangeTask) {
      var nextChange = scheduledChanges[0];
      // TODO: this should probably call a method of runtime instead of accessing priorityQueue directly
      // TODO: this call could get back a 'task handle' that we use to remove a pending task on deactivate
      var changeTask = {
        time: nextChange.time,
        topoOrder: baseTopoOrder,
        closure: changeOutput,
      };
      runtime.priorityQueue.insert(changeTask);
      pendingOutputChangeTask = changeTask;
    }
  };

  // closure to be called when time has come to change output value
  var changeOutput = function(atTime) {
    if (scheduledChanges.length === 0) {
      throw new Error('no changes to make');
    }

    // pull next change off 'front' of queue
    var nextChange = scheduledChanges.shift();

    // sanity check
    if (atTime !== nextChange.time) {
      throw new Error('times do not match');
    }

    result.outputStream.changeValue(nextChange.value, atTime);

    pendingOutputChangeTask = null;
    updateTasks();
  };

  var argChangedTask = function(atTime) {
    var argVal = argStream.value;
    scheduledChanges.push({
      time: atTime + 1.0, // here is the delay amount
      value: argVal,
    });

    updateTasks();
  };

  // make closure to add task when argument value changes
  var argChangedTrigger = function(atTime) {
    runtime.priorityQueue.insert({
      time: atTime,
      topoOrder: baseTopoOrder,
      closure: argChangedTask,
    });
  };

  // add trigger on argument
  argStream.addTrigger(argChangedTrigger);

  // create deactivator
  result.deactivator = function() {
    argStream.removeTrigger(argChangedTrigger);
    if (pendingOutputChangeTask) {
      runtime.priorityQueue.remove(pendingOutputChangeTask);
    }
  };

  return result;
};

function timeOfLatest(runtime, startTime, argStreams, baseTopoOrder, result) {
  if (argStreams.length !== 1) {
    throw new Error('got wrong number of arguments');
  }

  var argStream = argStreams[0];
  if (argStream.tempo !== 'event') {
    throw new Error('Incorrect input stream tempo');
  }

  // create or validate result, set initial output value
  if (result) {
    if (result.outputStream.tempo !== 'step') {
      throw new Error('Incorrect output stream tempo');
    }
    result.outputStream.changeValue(0, startTime);
  } else {
    result = {
      outputStream: runtime.createStepStream(0, startTime),
      deactivator: null,
    };
  }

  if (result.deactivator) { throw new Error('Deactivator should be null'); }
  result.deactivator = function() {
    argStream.removeTrigger(argChangedTrigger);
  };

  // closure to update output value
  var argChangedTask = function(atTime) {
    result.outputStream.changeValue(atTime-startTime, atTime);
  };

  // make closure to add task when argument value changes
  var argChangedTrigger = function(atTime) {
    runtime.priorityQueue.insert({
      time: atTime,
      topoOrder: baseTopoOrder,
      closure: argChangedTask,
    });
  };

  // add trigger on argument
  argStream.addTrigger(argChangedTrigger);

  return result;
}

function integral(runtime, startTime, argStreams, baseTopoOrder, result) {
  if (argStreams.length !== 3) {
    throw new Error('got wrong number of arguments');
  }

  // split out parameters and validate their tempos
  var integrand = argStreams[0];
  if (integrand.tempo !== 'step') {
    throw new Error('Argument integrand must be step');
  }
  var initialValue = argStreams[1];
  if (initialValue.tempo !== 'const') {
    throw new Error('Argument initialValue must be const');
  }
  var update = argStreams[2];
  if (update.tempo !== 'event') {
    throw new Error('Argument update must be event');
  }

  // here is our internal state and accumulating machinery
  var sum = initialValue.value; // the integral up to this point
  var lastTime = startTime; // the last time we accumulated to sum
  var lastIntegrandVal = undefined; // the value of integrand at lastTime
  function accumulate(upToTime) {
    if (lastIntegrandVal !== undefined) {
      sum += (upToTime - lastTime)*lastIntegrandVal;
    }
    lastTime = upToTime;
    lastIntegrandVal = integrand.value;
  }

  // create or validate result, set initial output value to zero
  if (result) {
    if (result.outputStream.tempo !== 'step') {
      throw new Error('Incorrect output stream tempo');
    }
    result.outputStream.changeValue(sum, startTime);
  } else {
    result = {
      outputStream: runtime.createStepStream(sum, startTime),
      deactivator: null,
    };
  }

  // If integrand and update inputs change at same time, it seems like it
  //  doesn't matter which we handle first, but we'll have integrand go
  //  first to keep things consistent.

  // task for when integrand changes
  var integrandChangedTask = function(atTime) {
    accumulate(atTime);
    // don't change output
  };

  // trigger for when integrand changes
  var integrandChangedTrigger = function(atTime) {
    runtime.priorityQueue.insert({
      time: atTime,
      topoOrder: baseTopoOrder+'0',
      closure: integrandChangedTask,
    });
  };

  // add trigger on integrand
  integrand.addTrigger(integrandChangedTrigger);

  // task for when update changes
  var updateChangedTask = function(atTime) {
    accumulate(atTime);
    result.outputStream.changeValue(sum, atTime);
  };

  // trigger for when update changes
  var updateChangedTrigger = function(atTime) {
    runtime.priorityQueue.insert({
      time: atTime,
      topoOrder: baseTopoOrder+'1',
      closure: updateChangedTask,
    });
  };

  // add trigger on update
  update.addTrigger(updateChangedTrigger);

  if (result.deactivator) { throw new Error('Deactivator should be null'); }
  result.deactivator = function() {
    update.removeTrigger(updateChangedTrigger);
    integrand.removeTrigger(integrandChangedTrigger);
  };

  return result;
}

module.exports = {
  id: {
    value: liftStep(function(a) { return a; }, 1),
    type: (function() {
      var a = typeUtils.createVariableType();
      return typeUtils.createFunctionType([{type: a}], a);
    })(),
  },
  Vec2: {
    value: liftStep(function(x, y) { return {x: x, y: y}; }, 2),
    // TODO: make yield type more specific when type system supports it
    type: typeUtils.createFunctionType([{type: typeUtils.NUMBER}, {type: typeUtils.NUMBER}], typeUtils.createVariableType()),
  },
  sin: {
    value: liftStep(function(x) { return Math.sin(x); }, 1),
    type: typeUtils.createFunctionType([{type: typeUtils.NUMBER}], typeUtils.NUMBER),
  },
  cos: {
    value: liftStep(function(x) { return Math.cos(x); }, 1),
    type: typeUtils.createFunctionType([{type: typeUtils.NUMBER}], typeUtils.NUMBER),
  },

  delay1: {
    value: delay1,
    type: (function() {
      var a = typeUtils.createVariableType();
      return typeUtils.createFunctionType([{type: a}], a);
    })(),
  },
  timeOfLatest: {
    value: timeOfLatest,
    // TODO: make parameter type more specific when type system supports it
    type: typeUtils.createFunctionType([{type: typeUtils.createVariableType()}], typeUtils.NUMBER),
  },
  integral: {
    value: integral,
    // TODO: make third parameter type more specific when type system supports it
    type: typeUtils.createFunctionType([{type: typeUtils.NUMBER, delayed: true}, {type: typeUtils.NUMBER}, {type: typeUtils.createVariableType()}], typeUtils.NUMBER),
  },
};
<|endoftext|>


<commit_before>/**
 * Better Volume Indicator
 *
 * This simple extension implements two things:
 *     1. When scrolling on the volume indicator, the volume slider/menu appears to
 *         display current level.
 *     2. Middle/scroll clicking on the indicator, output is muted.
 *
 */
const Clutter = imports.gi.Clutter;
const Mainloop = imports.mainloop;

const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const VolumeMenu = imports.ui.status.volume.VolumeMenu;

const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
const prettyPrint = Convenience.dbPrintObj;

const POPUP_TIMEOUT_SECS = 1;
const VOLUME_MUTE_ICON = 'audio-volume-muted-symbolic';
let onlyVolumeMenuShown = false;
let volumeIndicator;
let aggregateMenu;
let _onAggregateMenuClickEventId;
let _onAggregateMenuEnterEventId;
let _onAggregateMenuLeaveEventId;
let _onVolumeIndicatorScrollEventId;
let _onVolumeIndicatorClickEventId;

let popupTimeout = null;
let availableMenus = [];
function _onVolumeIndicatorScroll(indicators, e) {
    let menu = aggregateMenu.menu;

    // Only if menu is not already open (and not due to our request)
    if (menu.actor.visible && popupTimeout === null)
        return;

    // We need to remove the previous timeout if the user scrolled again since
    // it means they are not ready yet for us to hide the menu
    if (popupTimeout !== null) _removeTimeout();

    // Set a timeout for the menu to close (we don't want it staying open forever)
    _setTimeout();

    // We want to hide all menus which are not the volume menu
    _setMenusVisibility(false);

    // Open the aggregateMenu so we can see some volume
    aggregateMenu.menu.open();
}

function _onVolumeIndicatorScrollTimeout () {
    // When time is up, we close the menu
    aggregateMenu.menu.close();

    // Re-show the aggegate menu entries
    _setMenusVisibility(true);

    _removeTimeout();
}

function _onAggregateMenuClick (actor, e) {
    // Make sure menus are displayed
    _setMenusVisibility(true);

    // We want to see if the popup is already displayed, and if so, kill its
    // timeout, so it won't suddenly disappear on us
    // If it's not open, we don't won't to do anything
    if (popupTimeout !== null) {
        _removeTimeout();

        // Make sure the menu is open
        // This is kinda hacky - since the aggregateMenu toggles its own
        // visibility, we want to reverse the effect. It's a dirty trick but I
        // could not figure out a better way to overcome its self-toggling
        aggregateMenu.menu.toggle();
    }
}

function _onAggregateMenuEnter () {
    _removeTimeout();
}

function _onAggregateMenuLeave () {
    // When mouse leaves, set a new timeout only if menu was shown due to
    // volume-scrolling (meaning - this extension caused the menu to appear, and
    // not, say, a click on the aggregate-menu by the user)
    if (onlyVolumeMenuShown) _setTimeout();
}

let _previousVolumeValue, _previousVolumeIcon;
function _onVolumeIndicatorClick (actor, e) {
    // When middle-clicking on the indicator we want to toggle mute
    if (e.get_button() === Clutter.BUTTON_MIDDLE) {
        let sliderActor = volumeIndicator._volumeMenu._output._slider; // hummm.. hack?
        let currentValue = sliderActor._getCurrentValue(); // starting to look like a hack
        let currentIcon = volumeIndicator._primaryIndicator.icon_name;

        if (currentValue === 0 && _previousVolumeValue) {
            // this is definitely a hack
            sliderActor.setValue(_previousVolumeValue);
            sliderActor.emit('value-changed', _previousVolumeValue); // mimic slider behvaiour so volume will actually change
            volumeIndicator._primaryIndicator.icon_name = _previousVolumeIcon;
        }
        else {
            // a dirty dirty hack
            sliderActor.setValue(0);
            sliderActor.emit('value-changed', 0); // like above
            volumeIndicator._primaryIndicator.icon_name = VOLUME_MUTE_ICON;
            _previousVolumeValue = currentValue;
            _previousVolumeIcon = currentIcon;
        }

        aggregateMenu.menu.toggle(); // again with that previous hack
    }
}

function _setTimeout () {
    popupTimeout = Mainloop.timeout_add_seconds(POPUP_TIMEOUT_SECS, _onVolumeIndicatorScrollTimeout);
}

function _removeTimeout () {
    Mainloop.source_remove(popupTimeout);
    popupTimeout = null;
}

function _setMenusVisibility (visibility) {
    // Find the menus inside the aggregateMenu (I couldn't find a better
    // way for finding the menus except finding their indicators. seems to work
    // though)
    for (let k in aggregateMenu) {
        let entry = aggregateMenu[k];
        if (entry instanceof PanelMenu.SystemIndicator) {
            if (entry !== volumeIndicator) {
                entry.menu.actor.visible = visibility;
            }
        }
    }

    // Hide inner menus inside the volume section
    volumeIndicator.menu._getMenuItems().forEach(function (item, i) {
        if ( !(item instanceof VolumeMenu) ) {
            item.actor.visible = visibility;
        }
    });

    // Mark volume menu visibility accordingly
    onlyVolumeMenuShown = !visibility;
}

function init() {

}

function enable() {
    aggregateMenu = Main.panel.statusArea.aggregateMenu;

    if (aggregateMenu.hasOwnProperty('_volume') && aggregateMenu._volume instanceof PanelMenu.SystemIndicator) {
        volumeIndicator = aggregateMenu._volume;
        _onVolumeIndicatorScrollEventId = volumeIndicator.indicators.connect('scroll-event', _onVolumeIndicatorScroll);
        _onVolumeIndicatorClickEventId = volumeIndicator.indicators.connect('button-press-event', _onVolumeIndicatorClick);
        _onAggregateMenuClickEventId = aggregateMenu.actor.connect('button-press-event', _onAggregateMenuClick);
        _onAggregateMenuEnterEventId = aggregateMenu.menu.actor.connect('enter-event', _onAggregateMenuEnter);
        _onAggregateMenuLeaveEventId = aggregateMenu.menu.actor.connect('leave-event', _onAggregateMenuLeave);
    }
}

function disable() {
    // We need to verify we still have connections and disconnect them
    if (_onVolumeIndicatorScrollEventId)
        volumeIndicator.indicators.disconnect(_onVolumeIndicatorScrollEventId);

    if (_onVolumeIndicatorClickEventId)
        volumeIndicator.indicators.disconnect(_onVolumeIndicatorClickEventId);

    if (_onAggregateMenuEnterEventId)
        aggregateMenu.actor.disconnect(_onAggregateMenuEnterEventId);

    if (_onAggregateMenuLeaveEventId)
        aggregateMenu.actor.disconnect(_onAggregateMenuLeaveEventId);

    if (_onAggregateMenuClickEventId)
        aggregateMenu.actor.disconnect(_onAggregateMenuClickEventId);
}

<commit_msg>[Fix #3] Disconnect the enter/leave events on aggregateMenu.menu<commit_after>/**
 * Better Volume Indicator
 *
 * This simple extension implements two things:
 *     1. When scrolling on the volume indicator, the volume slider/menu appears to
 *         display current level.
 *     2. Middle/scroll clicking on the indicator, output is muted.
 *
 */
const Clutter = imports.gi.Clutter;
const Mainloop = imports.mainloop;

const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const VolumeMenu = imports.ui.status.volume.VolumeMenu;

const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
const prettyPrint = Convenience.dbPrintObj;

const POPUP_TIMEOUT_SECS = 1;
const VOLUME_MUTE_ICON = 'audio-volume-muted-symbolic';
let onlyVolumeMenuShown = false;
let volumeIndicator;
let aggregateMenu;
let _onAggregateMenuClickEventId;
let _onAggregateMenuEnterEventId;
let _onAggregateMenuLeaveEventId;
let _onVolumeIndicatorScrollEventId;
let _onVolumeIndicatorClickEventId;

let popupTimeout = null;
let availableMenus = [];
function _onVolumeIndicatorScroll(indicators, e) {
    let menu = aggregateMenu.menu;

    // Only if menu is not already open (and not due to our request)
    if (menu.actor.visible && popupTimeout === null)
        return;

    // We need to remove the previous timeout if the user scrolled again since
    // it means they are not ready yet for us to hide the menu
    if (popupTimeout !== null) _removeTimeout();

    // Set a timeout for the menu to close (we don't want it staying open forever)
    _setTimeout();

    // We want to hide all menus which are not the volume menu
    _setMenusVisibility(false);

    // Open the aggregateMenu so we can see some volume
    aggregateMenu.menu.open();
}

function _onVolumeIndicatorScrollTimeout () {
    // When time is up, we close the menu
    aggregateMenu.menu.close();

    // Re-show the aggegate menu entries
    _setMenusVisibility(true);

    _removeTimeout();
}

function _onAggregateMenuClick (actor, e) {
    // Make sure menus are displayed
    _setMenusVisibility(true);

    // We want to see if the popup is already displayed, and if so, kill its
    // timeout, so it won't suddenly disappear on us
    // If it's not open, we don't won't to do anything
    if (popupTimeout !== null) {
        _removeTimeout();

        // Make sure the menu is open
        // This is kinda hacky - since the aggregateMenu toggles its own
        // visibility, we want to reverse the effect. It's a dirty trick but I
        // could not figure out a better way to overcome its self-toggling
        aggregateMenu.menu.toggle();
    }
}

function _onAggregateMenuEnter () {
    _removeTimeout();
}

function _onAggregateMenuLeave () {
    // When mouse leaves, set a new timeout only if menu was shown due to
    // volume-scrolling (meaning - this extension caused the menu to appear, and
    // not, say, a click on the aggregate-menu by the user)
    if (onlyVolumeMenuShown) _setTimeout();
}

let _previousVolumeValue, _previousVolumeIcon;
function _onVolumeIndicatorClick (actor, e) {
    // When middle-clicking on the indicator we want to toggle mute
    if (e.get_button() === Clutter.BUTTON_MIDDLE) {
        let sliderActor = volumeIndicator._volumeMenu._output._slider; // hummm.. hack?
        let currentValue = sliderActor._getCurrentValue(); // starting to look like a hack
        let currentIcon = volumeIndicator._primaryIndicator.icon_name;

        if (currentValue === 0 && _previousVolumeValue) {
            // this is definitely a hack
            sliderActor.setValue(_previousVolumeValue);
            sliderActor.emit('value-changed', _previousVolumeValue); // mimic slider behvaiour so volume will actually change
            volumeIndicator._primaryIndicator.icon_name = _previousVolumeIcon;
        }
        else {
            // a dirty dirty hack
            sliderActor.setValue(0);
            sliderActor.emit('value-changed', 0); // like above
            volumeIndicator._primaryIndicator.icon_name = VOLUME_MUTE_ICON;
            _previousVolumeValue = currentValue;
            _previousVolumeIcon = currentIcon;
        }

        aggregateMenu.menu.toggle(); // again with that previous hack
    }
}

function _setTimeout () {
    popupTimeout = Mainloop.timeout_add_seconds(POPUP_TIMEOUT_SECS, _onVolumeIndicatorScrollTimeout);
}

function _removeTimeout () {
    Mainloop.source_remove(popupTimeout);
    popupTimeout = null;
}

function _setMenusVisibility (visibility) {
    // Find the menus inside the aggregateMenu (I couldn't find a better
    // way for finding the menus except finding their indicators. seems to work
    // though)
    for (let k in aggregateMenu) {
        let entry = aggregateMenu[k];
        if (entry instanceof PanelMenu.SystemIndicator) {
            if (entry !== volumeIndicator) {
                entry.menu.actor.visible = visibility;
            }
        }
    }

    // Hide inner menus inside the volume section
    volumeIndicator.menu._getMenuItems().forEach(function (item, i) {
        if ( !(item instanceof VolumeMenu) ) {
            item.actor.visible = visibility;
        }
    });

    // Mark volume menu visibility accordingly
    onlyVolumeMenuShown = !visibility;
}

function init() {

}

function enable() {
    aggregateMenu = Main.panel.statusArea.aggregateMenu;

    if (aggregateMenu.hasOwnProperty('_volume') && aggregateMenu._volume instanceof PanelMenu.SystemIndicator) {
        volumeIndicator = aggregateMenu._volume;
        _onVolumeIndicatorScrollEventId = volumeIndicator.indicators.connect('scroll-event', _onVolumeIndicatorScroll);
        _onVolumeIndicatorClickEventId = volumeIndicator.indicators.connect('button-press-event', _onVolumeIndicatorClick);
        _onAggregateMenuEnterEventId = aggregateMenu.menu.actor.connect('enter-event', _onAggregateMenuEnter);
        _onAggregateMenuLeaveEventId = aggregateMenu.menu.actor.connect('leave-event', _onAggregateMenuLeave);
        _onAggregateMenuClickEventId = aggregateMenu.actor.connect('button-press-event', _onAggregateMenuClick);
    }
}

function disable() {
    // We need to verify we still have connections and disconnect them
    if (_onVolumeIndicatorScrollEventId)
        volumeIndicator.indicators.disconnect(_onVolumeIndicatorScrollEventId);

    if (_onVolumeIndicatorClickEventId)
        volumeIndicator.indicators.disconnect(_onVolumeIndicatorClickEventId);

    if (_onAggregateMenuEnterEventId)
        aggregateMenu.menu.actor.disconnect(_onAggregateMenuEnterEventId);

    if (_onAggregateMenuLeaveEventId)
        aggregateMenu.menu.actor.disconnect(_onAggregateMenuLeaveEventId);

    if (_onAggregateMenuClickEventId)
        aggregateMenu.actor.disconnect(_onAggregateMenuClickEventId);
}

<|endoftext|>


<commit_before>export { ScaleTranslateSet };

import * as FloatValue from "../Unpacker/FloatValue.js";
import * as ConvBiasActivation from "./ConvBiasActivation.js";

/**
 * Several scale-translate for escaping a value bounds from being activated (i.e. being non-linearized) by activation function.
 *
 *
 * @member {FloatValue.ScaleTranslate} doWithoutPreviousUndo
 *   The scale-translate for moving current value bounds into the linear domain of the activation function. That is, for letting
 * ConvBiasActivation.ValueBoundsSet.beforeActivation escape from activation function's non-linear domain into linear domain.
 *
 * @member {FloatValue.ScaleTranslate} do
 *   The result of combining this.doWithoutPreviousUndo with previous ActivationEscape.ScaleTranslateSet.undo. It both undo the
 * previous escaping scale-translate and do itself escaping scale-translate.
 *
 * @member {FloatValue.ScaleTranslate} undo
 *   If apply this.undo (important: scale first, translate second), it will have the effect of undoing the this.do.
 */
class ScaleTranslateSet {

  constructor() {
    this.doWithoutPreviousUndo = new FloatValue.ScaleTranslate();
    this.do = new FloatValue.ScaleTranslate();
    this.undo = new FloatValue.ScaleTranslate();
  }

  clone() {
    let result = new ScaleTranslateSet();
    result.setBy_ScaleTranslateSet( this );
    return result;
  }

  /**
   * @param {ScaleTranslateSet} another
   *   The ScaleTranslateSet to be copied.
   */
  setBy_ScaleTranslateSet( another ) {
    this.doWithoutPreviousUndo.setBy_ScaleTranslate( another.doWithoutPreviousUndo );
    this.do.setBy_ScaleTranslate( another.do );
    this.undo.setBy_ScaleTranslate( another.undo );
  }

  /** Reset all scale-translate values. Default is ( scale = 1, translate = 0 ) (i.e. no scale and no translate). */
  reset( scale = 1, translate = 0 ) {
    this.doWithoutPreviousUndo.set( scale , translate );
    this.do.set( scale , translate );
    this.undo.set( scale , translate );
  }

//!!! ...unfinished... (2021/12/26)
//
// - For depthwise with ( pad == same ), it seems that the activation escaping can not be undone completely, because pad is always 0
//   (can not scale-translate). Unless, pad can be non-zero?
//
//   - Perhaps, force use ( pad == valid ) so that the activation escaping always can be undone completely.
//   - In original ShuffleNetV2, using ( pad == same ) is necessary for concatenating ( depthwise1, input1 ).
//   - However, in our combined-depthwise1-with-higher-half-pass-through (i.e. ONE_INPUT_HALF_THROUGH_XXX), the ( depthwise1, input1 )
//       already combined together. There is no concatenation issue. So using ( pad == valid ) is possible.
//
// - If previous convolution does not have activation escaping do (i.e. is a normal convolution without pass-through)，this
//     this convolution's .do should be different.
//
// - Perhaps, there should be value-bounds and activation-escaping scale-translate for every single channel (i.e. lower-array,
//     upper-array, scale-array and translate-array). Even if channels are shuffled, they could be still tracked correctly.
//
// - When extractFilters() and extractBiases(), pre-apply the per channel undoing scale and translate to filter-value, bias-value,
//     and their bounds. (The .do and .undo should also affect the value bounds.)
//
// - When across Block (i.e. at ShuffleNetV2_ByMobileNetV1's head), the higher-half-copy-lower-half channels's value bounds
//     does not come from previous corresponding channels. They comes from the lower-half channels which they copied from.
//

  /**
   * Set the following properties:
   *   - this.doWithoutPreviousUndo
   *   - this.do
   *   - this.undo
   *
   * @param {ConvBiasActivation.ValueBoundsSet} current_ConvBiasActivation_ValueBoundsSet
   *   The ValueBoundsSet of current convolution-bias-activation for calculating this.doWithoutPreviousUndo.
   *
   * @param {ScaleTranslateSet} previous_ActivationEscaping_ScaleTranslateSet
   *   The ActivationEscaping.ScaleTranslateSet of previous convolution-bias-activation for calculating this.do.
   */
  setBy_currentValueBoundsSet_previousActivationEscaping(
    current_ConvBiasActivation_ValueBoundsSet, previous_ActivationEscaping_ScaleTranslateSet ) {

    // Calculate the scale-translate for escaping from activation function's non-linear domain into linear domain.
    //
    // Note: This does not work for avg/max pooling.
    this.doWithoutPreviousUndo.setBy_fromBounds_ToBounds(
      current_ConvBiasActivation_ValueBoundsSet.beforeActivation, current_ConvBiasActivation_ValueBoundsSet.output );

    // Combine undoing previous activation escaping scale-translate and doing current activation escaping scale-translate.
    this.do.setBy_ScaleTranslate( previous_ActivationEscaping_ScaleTranslateSet.undo );
    this.do.scaleTranslateBy( this.doWithoutPreviousUndo );

    // Prepare the undoing scale-translate for the next convolution-bias-activation.
    this.undo.setBy_undoScaleTranslate( this.do );
  }

}

<commit_msg>Update ActivationEscapeing.js<commit_after>export { ScaleTranslateSet };

import * as FloatValue from "../Unpacker/FloatValue.js";
import * as ConvBiasActivation from "./ConvBiasActivation.js";

/**
 * Several scale-translate for escaping a value bounds from being activated (i.e. being non-linearized) by activation function.
 *
 *
 * @member {FloatValue.ScaleTranslateArray} doWithoutPreviousUndo
 *   The scale-translate for moving current value bounds into the linear domain of the activation function. That is, for letting
 * ConvBiasActivation.ValueBoundsSet.beforeActivation escape from activation function's non-linear domain into linear domain.
 *
 * @member {FloatValue.ScaleTranslateArray} do
 *   The result of combining this.doWithoutPreviousUndo with previous ActivationEscape.ScaleTranslateSet.undo. It both undo the
 * previous escaping scale-translate and do itself escaping scale-translate.
 *
 * @member {FloatValue.ScaleTranslateArray} undo
 *   If apply this.undo (important: scale first, translate second), it will have the effect of undoing the this.do.
 */
class ScaleTranslateSet {

  constructor() {
    this.doWithoutPreviousUndo = new FloatValue.ScaleTranslateArray();
    this.do = new FloatValue.ScaleTranslateArray();
    this.undo = new FloatValue.ScaleTranslateArray();
  }

  clone() {
    let result = new ScaleTranslateSet();
    result.set_ScaleTranslateSet( this );
    return result;
  }

  /**
   * @param {ScaleTranslateSet} aScaleTranslateSet
   *   The ScaleTranslateSet to be copied.
   */
  set_ScaleTranslateSet( aScaleTranslateSet ) {
    this.doWithoutPreviousUndo.set_ScaleTranslateArray( aScaleTranslateSet.doWithoutPreviousUndo );
    this.do.set_ScaleTranslateArray( aScaleTranslateSet.do );
    this.undo.set_ScaleTranslateArray( aScaleTranslateSet.undo );
  }

  /** Reset all scale-translate values. Default is ( scale = 1, translate = 0 ) (i.e. no scale and no translate). */
  reset( scale = 1, translate = 0 ) {
    this.doWithoutPreviousUndo.set_scale_translate( scale , translate );
    this.do.set_scale_translate( scale , translate );
    this.undo.set_scale_translate( scale , translate );
  }

//!!! ...unfinished... (2021/12/26)
//
// - For depthwise with ( pad == same ), it seems that the activation escaping can not be undone completely, because pad is always 0
//   (can not scale-translate). Unless, pad can be non-zero?
//
//   - Perhaps, force use ( pad == valid ) so that the activation escaping always can be undone completely.
//   - In original ShuffleNetV2, using ( pad == same ) is necessary for concatenating ( depthwise1, input1 ).
//   - However, in our combined-depthwise1-with-higher-half-pass-through (i.e. ONE_INPUT_HALF_THROUGH_XXX), the ( depthwise1, input1 )
//       already combined together. There is no concatenation issue. So using ( pad == valid ) is possible.
//
// - If previous convolution does not have activation escaping do (i.e. is a normal convolution without pass-through)，this
//     this convolution's .do should be different.
//
// - Perhaps, there should be value-bounds and activation-escaping scale-translate for every single channel (i.e. lower-array,
//     upper-array, scale-array and translate-array). Even if channels are shuffled, they could be still tracked correctly.
//
// - When extractFilters() and extractBiases(), pre-apply the per channel undoing scale and translate to filter-value, bias-value,
//     and their bounds. (The .do and .undo should also affect the value bounds.)
//
// - When across Block (i.e. at ShuffleNetV2_ByMobileNetV1's head), the higher-half-copy-lower-half channels's value bounds
//     does not come from previous corresponding channels. They comes from the lower-half channels which they copied from.
//

  /**
   * Set the following properties:
   *   - this.doWithoutPreviousUndo
   *   - this.do
   *   - this.undo
   *
   * @param {ConvBiasActivation.ValueBoundsSet} current_ConvBiasActivation_ValueBoundsSet
   *   The ValueBoundsSet of current convolution-bias-activation for calculating this.doWithoutPreviousUndo.
   *
   * @param {ScaleTranslateSet} previous_ActivationEscaping_ScaleTranslateSet
   *   The ActivationEscaping.ScaleTranslateSet of previous convolution-bias-activation for calculating this.do.
   */
  setBy_currentValueBoundsSet_previousActivationEscaping(
    current_ConvBiasActivation_ValueBoundsSet, previous_ActivationEscaping_ScaleTranslateSet ) {

    // Calculate the scale-translate for escaping from activation function's non-linear domain into linear domain.
    //
    // Note: This does not work for avg/max pooling.
    this.doWithoutPreviousUndo.set_fromBoundsArray_ToBoundsArray(
      current_ConvBiasActivation_ValueBoundsSet.beforeActivation, current_ConvBiasActivation_ValueBoundsSet.output );

    // Combine undoing previous activation escaping scale-translate and doing current activation escaping scale-translate.
    this.do.set_ScaleTranslateArray( previous_ActivationEscaping_ScaleTranslateSet.undo );
    this.do.scaleTranslateBy( this.doWithoutPreviousUndo );

    // Prepare the undoing scale-translate for the next convolution-bias-activation.
    this.undo.set_undoScaleTranslate( this.do );
  }

}

<|endoftext|>


<commit_before>/* global $ location django */
$(document).ready(function () {
  var $main = $('main')
  var currentPath
  var popup
  var patternsForPopup = /\/accounts\b/

  var headers = {
    'X-Embed': ''
  }

  testCanSetCookie()

  var loadHtml = function (html, textStatus, xhr) {
    var $root = $(html).filter('main')
    var nextPath = xhr.getResponseHeader('x-ajax-path')

    if (patternsForPopup.test(nextPath)) {
      $('#embed-confirm').modal('show')
      return false
    }
    // only update the currentPath if there was no modal opened
    currentPath = nextPath

    $main.empty()
    $main.append($root.children())
    onReady()
  }

  var onReady = function () {
    // adhocracy4.onReady($main)
  }

  var getEmbedTarget = function ($element, url) {
    var embedTarget = $element.data('embedTarget')

    if (embedTarget) {
      return embedTarget
    } else if (!url || url[0] === '#' || $element.attr('target')) {
      return 'ignore'
    } else if ($element.is('.rich-text a')) {
      return 'external'
    } else if (patternsForPopup.test(url)) {
      return 'popup'
    } else {
      return 'internal'
    }
  }

  $(document).on('click', 'a[href]', function (event) {
    // NOTE: event.target.href is resolved against /embed/
    var url = event.target.getAttribute('href')
    var $link = $(event.target)
    var embedTarget = getEmbedTarget($link, url)

    if (embedTarget === 'internal') {
      event.preventDefault()

      if (url[0] === '?') {
        url = currentPath + url
      }

      $.ajax({
        url: url,
        headers: headers,
        success: loadHtml
      })
    } else if (embedTarget === 'popup') {
      event.preventDefault()
      popup = window.open(
        url,
        'embed_popup',
        'height=650,width=500,location=yes,menubar=no,toolbar=no,status=no'
      )
    }
  })

  $(document).on('submit', 'form[action]', function (event) {
    var form = event.target
    var $form = $(form)
    var embedTarget = getEmbedTarget($form, form.method)

    if (embedTarget === 'internal') {
      event.preventDefault()

      $.ajax({
        url: form.action,
        method: form.method,
        headers: headers,
        data: $form.serialize(),
        success: loadHtml
      })
    }
  })

  $('.js-embed-logout').on('click', function (e) {
    e.preventDefault()
    $.post(
      '/accounts/logout/',
      function () {
        location.reload()
      }
    )
  })

  // The popup will send a message when the user is logged in. Only after
  // this message the Popup will close.
  window.addEventListener('message', function (e) {
    if (e.origin === location.origin) {
      var data = JSON.parse(e.data)

      if (data.name === 'popup-close' && popup) {
        popup.close()
        location.reload()
      }
    }
  }, false)

  $(document).ajaxError(function (event, jqxhr) {
    var text
    var $error = $('

')
    var $close = $('<button class="alert__close"></button>')

    var removeMessage = function () {
      $error.remove()
    }

    switch (jqxhr.status) {
      case 404:
        text = django.gettext('We couldn\'t find what you were looking for.')
        break
      case 401:
      case 403:
        text = django.gettext('You don\'t have the permission to view this page.')
        break
      default:
        text = django.gettext('Something went wrong!')
        break
    }

    $error.text(text)
    $error.append($close)
    $error.prependTo($('#embed-status'))
    $close.attr('title', django.gettext('Close'))

    $error.on('click', removeMessage)
    setTimeout(removeMessage, 6000)
  })

  $.ajax({
    url: $('body').data('url'),
    headers: headers,
    success: loadHtml
  })
})

function testCanSetCookie () {
  var cookie = 'can-set-cookie=true;'
  var regExp = new RegExp(cookie)
  document.cookie = cookie
  return regExp.test(document.cookie)
}
<commit_msg>refactor alert generation so it can be used for info alerts too<commit_after>/* global $ location django */
$(document).ready(function () {
  var $main = $('main')
  var currentPath
  var popup
  var patternsForPopup = /\/accounts\b/

  var headers = {
    'X-Embed': ''
  }

  testCanSetCookie()

  var loadHtml = function (html, textStatus, xhr) {
    var $root = $(html).filter('main')
    var nextPath = xhr.getResponseHeader('x-ajax-path')

    if (patternsForPopup.test(nextPath)) {
      $('#embed-confirm').modal('show')
      return false
    }
    // only update the currentPath if there was no modal opened
    currentPath = nextPath

    $main.empty()
    $main.append($root.children())
    onReady()
  }

  var onReady = function () {
    // adhocracy4.onReady($main)
  }

  var getEmbedTarget = function ($element, url) {
    var embedTarget = $element.data('embedTarget')

    if (embedTarget) {
      return embedTarget
    } else if (!url || url[0] === '#' || $element.attr('target')) {
      return 'ignore'
    } else if ($element.is('.rich-text a')) {
      return 'external'
    } else if (patternsForPopup.test(url)) {
      return 'popup'
    } else {
      return 'internal'
    }
  }

  $(document).on('click', 'a[href]', function (event) {
    // NOTE: event.target.href is resolved against /embed/
    var url = event.target.getAttribute('href')
    var $link = $(event.target)
    var embedTarget = getEmbedTarget($link, url)

    if (embedTarget === 'internal') {
      event.preventDefault()

      if (url[0] === '?') {
        url = currentPath + url
      }

      $.ajax({
        url: url,
        headers: headers,
        success: loadHtml
      })
    } else if (embedTarget === 'popup') {
      event.preventDefault()
      popup = window.open(
        url,
        'embed_popup',
        'height=650,width=500,location=yes,menubar=no,toolbar=no,status=no'
      )
    }
  })

  $(document).on('submit', 'form[action]', function (event) {
    var form = event.target
    var $form = $(form)
    var embedTarget = getEmbedTarget($form, form.method)

    if (embedTarget === 'internal') {
      event.preventDefault()

      $.ajax({
        url: form.action,
        method: form.method,
        headers: headers,
        data: $form.serialize(),
        success: loadHtml
      })
    }
  })

  $('.js-embed-logout').on('click', function (e) {
    e.preventDefault()
    $.post(
      '/accounts/logout/',
      function () {
        location.reload()
      }
    )
  })

  // The popup will send a message when the user is logged in. Only after
  // this message the Popup will close.
  window.addEventListener('message', function (e) {
    if (e.origin === location.origin) {
      var data = JSON.parse(e.data)

      if (data.name === 'popup-close' && popup) {
        popup.close()
        location.reload()
      }
    }
  }, false)

  $(document).ajaxError(function (event, jqxhr) {
    var text
    switch (jqxhr.status) {
      case 404:
        text = django.gettext('We couldn\'t find what you were looking for.')
        break
      case 401:
      case 403:
        text = django.gettext('You don\'t have the permission to view this page.')
        break
      default:
        text = django.gettext('Something went wrong!')
        break
    }

    var $error = getAlert(text, 'danger', 6000)
    $error.prependTo($('#embed-status'))
  })

  function getAlert (text, state, timeout) {
    var $alert = $('
' + text + '
')
    var $close = $('<button class="alert__close"></button>')

    $alert.append($close)
    $close.attr('title', django.gettext('Close'))

    var removeMessage = function () {
      $alert.remove()
    }
    $alert.on('click', removeMessage)
    if (typeof timeout === 'number') {
      setTimeout(removeMessage, timeout)
    }
    return $alert
  }

  $.ajax({
    url: $('body').data('url'),
    headers: headers,
    success: loadHtml
  })
})

function testCanSetCookie () {
  var cookie = 'can-set-cookie=true;'
  var regExp = new RegExp(cookie)
  document.cookie = cookie
  return regExp.test(document.cookie)
}
<|endoftext|>


<commit_before>import { getUrlExtension } from '../utilities/urlExtension.js';
import { LRUCache } from '../utilities/LRUCache.js';
import { PriorityQueue } from '../utilities/PriorityQueue.js';
import { determineFrustumSet, toggleTiles, skipTraversal, markUsedSetLeaves, traverseSet } from './traverseFunctions.js';
import { UNLOADED, LOADING, PARSING, LOADED, FAILED } from './constants.js';

/**
 * Function for provided to sort all tiles for prioritizing loading/unloading.
 *
 * @param {Tile} a
 * @param {Tile} b
 * @returns number
 */
const priorityCallback = ( a, b ) => {

	if ( a.__depth !== b.__depth ) {

		// load shallower tiles first
		return a.__depth > b.__depth ? - 1 : 1;

	} else if ( a.__inFrustum !== b.__inFrustum ) {

		// load tiles that are in the frustum at the current depth
		return a.__inFrustum ? 1 : - 1;

	} else if ( a.__used !== b.__used ) {

		// load tiles that have been used
		return a.__used ? 1 : - 1;

	} else if ( a.__error !== b.__error ) {

		// load the tile with the higher error
		return a.__error > b.__error ? 1 : - 1;

	} else if ( a.__distanceFromCamera !== b.__distanceFromCamera ) {

		// and finally visible tiles which have equal error (ex: if geometricError === 0)
		// should prioritize based on distance.
		return a.__distanceFromCamera > b.__distanceFromCamera ? - 1 : 1;

	}

	return 0;

};

/**
 * Function for sorting the evicted LRU items. We should evict the shallowest depth first.
 * @param {Tile} tile
 * @returns number
 */
const lruPriorityCallback = ( tile ) => 1 / ( tile.__depthFromRenderedParent + 1 );

export class TilesRendererBase {

	get rootTileSet() {

		const tileSet = this.tileSets[ this.rootURL ];
		if ( ! tileSet || tileSet instanceof Promise ) {

			return null;

		} else {

			return tileSet;

		}

	}

	get root() {

		const tileSet = this.rootTileSet;
		return tileSet ? tileSet.root : null;

	}

	constructor( url ) {

		// state
		this.tileSets = {};
		this.rootURL = url;
		this.fetchOptions = {};

		this.preprocessURL = null;

		const lruCache = new LRUCache();
		lruCache.unloadPriorityCallback = lruPriorityCallback;

		const downloadQueue = new PriorityQueue();
		downloadQueue.maxJobs = 4;
		downloadQueue.priorityCallback = priorityCallback;

		const parseQueue = new PriorityQueue();
		parseQueue.maxJobs = 1;
		parseQueue.priorityCallback = priorityCallback;

		this.lruCache = lruCache;
		this.downloadQueue = downloadQueue;
		this.parseQueue = parseQueue;
		this.stats = {
			parsing: 0,
			downloading: 0,
			failed: 0,
			inFrustum: 0,
			used: 0,
			active: 0,
			visible: 0,
		};
		this.frameCount = 0;

		// options
		this.errorTarget = 6.0;
		this.errorThreshold = Infinity;
		this.loadSiblings = true;
		this.displayActiveTiles = false;
		this.maxDepth = Infinity;
		this.stopAtEmptyTiles = true;

	}

	traverse( beforecb, aftercb ) {

		const tileSets = this.tileSets;
		const rootTileSet = tileSets[ this.rootURL ];
		if ( ! rootTileSet || ! rootTileSet.root ) return;

		traverseSet( rootTileSet.root, beforecb, aftercb );

	}

	// Public API
	update() {

		const stats = this.stats;
		const lruCache = this.lruCache;
		const tileSets = this.tileSets;
		const rootTileSet = tileSets[ this.rootURL ];
		if ( ! ( this.rootURL in tileSets ) ) {

			this.loadRootTileSet( this.rootURL );
			return;

		} else if ( ! rootTileSet || ! rootTileSet.root ) {

			return;

		}

		const root = rootTileSet.root;

		stats.inFrustum = 0,
		stats.used = 0,
		stats.active = 0,
		stats.visible = 0,
		this.frameCount ++;

		determineFrustumSet( root, this );
		markUsedSetLeaves( root, this );
		skipTraversal( root, this );
		toggleTiles( root, this );

		lruCache.scheduleUnload();

	}

	// Overrideable
	parseTile( buffer, tile, extension ) {

		return null;

	}

	disposeTile( tile ) {

	}

	preprocessNode( tile, parentTile, tileSetDir ) {

		if ( tile.content ) {

			// Fix old file formats
			if ( ! ( 'uri' in tile.content ) && 'url' in tile.content ) {

				tile.content.uri = tile.content.url;
				delete tile.content.url;

			}

			if ( tile.content.uri ) {

				// tile content uri has to be interpreted relative to the tileset.json
				tile.content.uri = new URL( tile.content.uri, tileSetDir ).toString();

			}

			// NOTE: fix for some cases where tilesets provide the bounding volume
			// but volumes are not present.
			if (
				tile.content.boundingVolume &&
				! (
					'box' in tile.content.boundingVolume ||
					'sphere' in tile.content.boundingVolume ||
					'region' in tile.content.boundingVolume
				)
			) {

				delete tile.content.boundingVolume;

			}

		}

		tile.parent = parentTile;
		tile.children = tile.children || [];

		const uri = tile.content && tile.content.uri;
		if ( uri ) {

			// "content" should only indicate loadable meshes, not external tile sets
			const extension = getUrlExtension( tile.content.uri );
			const isExternalTileSet = Boolean( extension && extension.toLowerCase() === 'json' );
			tile.__externalTileSet = isExternalTileSet;
			tile.__contentEmpty = isExternalTileSet;

		} else {

			tile.__externalTileSet = false;
			tile.__contentEmpty = true;

		}

		// Expected to be set during calculateError()
		tile.__distanceFromCamera = Infinity;
		tile.__error = Infinity;

		tile.__inFrustum = false;
		tile.__isLeaf = false;

		tile.__usedLastFrame = false;
		tile.__used = false;

		tile.__wasSetVisible = false;
		tile.__visible = false;
		tile.__childrenWereVisible = false;
		tile.__allChildrenLoaded = false;

		tile.__wasSetActive = false;
		tile.__active = false;

		tile.__loadingState = UNLOADED;
		tile.__loadIndex = 0;

		tile.__loadAbort = null;

		tile.__depthFromRenderedParent = - 1;
		if ( parentTile === null ) {

			tile.__depth = 0;
			tile.refine = tile.refine || 'REPLACE';

		} else {

			tile.__depth = parentTile.__depth + 1;
			tile.refine = tile.refine || parentTile.refine;

		}

	}

	setTileActive( tile, state ) {

	}

	setTileVisible( tile, state ) {

	}

	calculateError( tile ) {

		return 0;

	}

	tileInView( tile ) {

		return true;

	}

	// Private Functions
	fetchTileSet( url, fetchOptions, parent = null ) {

		return fetch( url, fetchOptions )
			.then( res => {

				if ( res.ok ) {

					return res.json();

				} else {

					throw new Error( `TilesRenderer: Failed to load tileset "${ url }" with status ${ res.status } : ${ res.statusText }` );

				}

			} )
			.then( json => {

				const version = json.asset.version;
				console.assert(
					version === '1.0' || version === '0.0',
					'asset.version is expected to be a string of "1.0" or "0.0"'
				);

				const basePath = url.replace( /\/[^\/]*\/?$/, '' );

				traverseSet(
					json.root,
					( node, parent ) => this.preprocessNode( node, parent, basePath ),
					null,
					parent,
					parent ? parent.__depth : 0,
				);

				return json;

			} );

	}

	loadRootTileSet( url ) {

		const tileSets = this.tileSets;
		if ( ! ( url in tileSets ) ) {

			const pr = this
				.fetchTileSet( this.preprocessURL ? this.preprocessURL( url ) : url, this.fetchOptions )
				.then( json => {

					tileSets[ url ] = json;

				} );

			pr.catch( err => {

				console.error( err );
				tileSets[ url ] = err;

			} );

			tileSets[ url ] = pr;

			return pr;

		} else if ( tileSets[ url ] instanceof Error ) {

			return Promise.reject( tileSets[ url ] );

		} else {

			return Promise.resolve( tileSets[ url ] );

		}

	}

	requestTileContents( tile ) {

		// If the tile is already being loaded then don't
		// start it again.
		if ( tile.__loadingState !== UNLOADED ) {

			return;

		}

		const stats = this.stats;
		const lruCache = this.lruCache;
		const downloadQueue = this.downloadQueue;
		const parseQueue = this.parseQueue;
		const isExternalTileSet = tile.__externalTileSet;
		lruCache.add( tile, t => {

			// Stop the load if it's started
			if ( t.__loadingState === LOADING ) {

				t.__loadAbort.abort();
				t.__loadAbort = null;

			} else if ( isExternalTileSet ) {

				t.children.length = 0;

			} else {

				this.disposeTile( t );

			}

			// Decrement stats
			if ( t.__loadingState === LOADING ) {

				stats.downloading --;

			} else if ( t.__loadingState === PARSING ) {

				stats.parsing --;

			}

			t.__loadingState = UNLOADED;
			t.__loadIndex ++;

			parseQueue.remove( t );
			downloadQueue.remove( t );

		} );

		// Track a new load index so we avoid the condition where this load is stopped and
		// another begins soon after so we don't parse twice.
		tile.__loadIndex ++;
		const loadIndex = tile.__loadIndex;
		const controller = new AbortController();
		const signal = controller.signal;

		stats.downloading ++;
		tile.__loadAbort = controller;
		tile.__loadingState = LOADING;

		const errorCallback = e => {

			// if it has been unloaded then the tile has been disposed
			if ( tile.__loadIndex !== loadIndex ) {

				return;

			}

			if ( e.name !== 'AbortError' ) {

				parseQueue.remove( tile );
				downloadQueue.remove( tile );

				if ( tile.__loadingState === PARSING ) {

					stats.parsing --;

				} else if ( tile.__loadingState === LOADING ) {

					stats.downloading --;

				}

				stats.failed ++;

				console.error( `TilesRenderer : Failed to load tile at url "${ tile.content.uri }".` );
				console.error( e );
				tile.__loadingState = FAILED;

			} else {

				lruCache.remove( tile );

			}

		};

		if ( isExternalTileSet ) {

			downloadQueue.add( tile, tileCb => {

				// if it has been unloaded then the tile has been disposed
				if ( tileCb.__loadIndex !== loadIndex ) {

					return Promise.resolve();

				}

				const uri = this.preprocessURL ? this.preprocessURL( tileCb.content.uri ) : tileCb.content.uri;
				return this.fetchTileSet( uri, Object.assign( { signal }, this.fetchOptions ), tileCb );

			} )
				.then( json => {

					// if it has been unloaded then the tile has been disposed
					if ( tile.__loadIndex !== loadIndex ) {

						return;

					}

					stats.downloading --;
					tile.__loadAbort = null;
					tile.__loadingState = LOADED;

					tile.children.push( json.root );

				} )
				.catch( errorCallback );

		} else {

			downloadQueue.add( tile, downloadTile => {

				if ( downloadTile.__loadIndex !== loadIndex ) {

					return Promise.resolve();

				}

				const uri = this.preprocessURL ? this.preprocessURL( downloadTile.content.uri ) : downloadTile.content.uri;
				return fetch( uri, Object.assign( { signal }, this.fetchOptions ) );

			} )
				.then( res => {

					if ( tile.__loadIndex !== loadIndex ) {

						return;

					}

					if ( res.ok ) {

						return res.arrayBuffer();

					} else {

						throw new Error( `Failed to load model with error code ${res.status}` );

					}

				} )
				.then( buffer => {

					// if it has been unloaded then the tile has been disposed
					if ( tile.__loadIndex !== loadIndex ) {

						return;

					}

					stats.downloading --;
					stats.parsing ++;
					tile.__loadAbort = null;
					tile.__loadingState = PARSING;

					return parseQueue.add( tile, parseTile => {

						// if it has been unloaded then the tile has been disposed
						if ( parseTile.__loadIndex !== loadIndex ) {

							return Promise.resolve();

						}

						const uri = parseTile.content.uri;
						const extension = getUrlExtension( uri );

						return this.parseTile( buffer, parseTile, extension );

					} );

				} )
				.then( () => {

					// if it has been unloaded then the tile has been disposed
					if ( tile.__loadIndex !== loadIndex ) {

						return;

					}

					stats.parsing --;
					tile.__loadingState = LOADED;

					if ( tile.__wasSetVisible ) {

						this.setTileVisible( tile, true );

					}

					if ( tile.__wasSetActive ) {

						this.setTileActive( tile, true );

					}

				} )
				.catch( errorCallback );

		}

	}

	dispose() {

		const lruCache = this.lruCache;
		this.traverse( tile => {

			lruCache.remove( tile );

		} );

	}

}
<commit_msg>fix: add comment for dirname-regex<commit_after>import { getUrlExtension } from '../utilities/urlExtension.js';
import { LRUCache } from '../utilities/LRUCache.js';
import { PriorityQueue } from '../utilities/PriorityQueue.js';
import { determineFrustumSet, toggleTiles, skipTraversal, markUsedSetLeaves, traverseSet } from './traverseFunctions.js';
import { UNLOADED, LOADING, PARSING, LOADED, FAILED } from './constants.js';

/**
 * Function for provided to sort all tiles for prioritizing loading/unloading.
 *
 * @param {Tile} a
 * @param {Tile} b
 * @returns number
 */
const priorityCallback = ( a, b ) => {

	if ( a.__depth !== b.__depth ) {

		// load shallower tiles first
		return a.__depth > b.__depth ? - 1 : 1;

	} else if ( a.__inFrustum !== b.__inFrustum ) {

		// load tiles that are in the frustum at the current depth
		return a.__inFrustum ? 1 : - 1;

	} else if ( a.__used !== b.__used ) {

		// load tiles that have been used
		return a.__used ? 1 : - 1;

	} else if ( a.__error !== b.__error ) {

		// load the tile with the higher error
		return a.__error > b.__error ? 1 : - 1;

	} else if ( a.__distanceFromCamera !== b.__distanceFromCamera ) {

		// and finally visible tiles which have equal error (ex: if geometricError === 0)
		// should prioritize based on distance.
		return a.__distanceFromCamera > b.__distanceFromCamera ? - 1 : 1;

	}

	return 0;

};

/**
 * Function for sorting the evicted LRU items. We should evict the shallowest depth first.
 * @param {Tile} tile
 * @returns number
 */
const lruPriorityCallback = ( tile ) => 1 / ( tile.__depthFromRenderedParent + 1 );

export class TilesRendererBase {

	get rootTileSet() {

		const tileSet = this.tileSets[ this.rootURL ];
		if ( ! tileSet || tileSet instanceof Promise ) {

			return null;

		} else {

			return tileSet;

		}

	}

	get root() {

		const tileSet = this.rootTileSet;
		return tileSet ? tileSet.root : null;

	}

	constructor( url ) {

		// state
		this.tileSets = {};
		this.rootURL = url;
		this.fetchOptions = {};

		this.preprocessURL = null;

		const lruCache = new LRUCache();
		lruCache.unloadPriorityCallback = lruPriorityCallback;

		const downloadQueue = new PriorityQueue();
		downloadQueue.maxJobs = 4;
		downloadQueue.priorityCallback = priorityCallback;

		const parseQueue = new PriorityQueue();
		parseQueue.maxJobs = 1;
		parseQueue.priorityCallback = priorityCallback;

		this.lruCache = lruCache;
		this.downloadQueue = downloadQueue;
		this.parseQueue = parseQueue;
		this.stats = {
			parsing: 0,
			downloading: 0,
			failed: 0,
			inFrustum: 0,
			used: 0,
			active: 0,
			visible: 0,
		};
		this.frameCount = 0;

		// options
		this.errorTarget = 6.0;
		this.errorThreshold = Infinity;
		this.loadSiblings = true;
		this.displayActiveTiles = false;
		this.maxDepth = Infinity;
		this.stopAtEmptyTiles = true;

	}

	traverse( beforecb, aftercb ) {

		const tileSets = this.tileSets;
		const rootTileSet = tileSets[ this.rootURL ];
		if ( ! rootTileSet || ! rootTileSet.root ) return;

		traverseSet( rootTileSet.root, beforecb, aftercb );

	}

	// Public API
	update() {

		const stats = this.stats;
		const lruCache = this.lruCache;
		const tileSets = this.tileSets;
		const rootTileSet = tileSets[ this.rootURL ];
		if ( ! ( this.rootURL in tileSets ) ) {

			this.loadRootTileSet( this.rootURL );
			return;

		} else if ( ! rootTileSet || ! rootTileSet.root ) {

			return;

		}

		const root = rootTileSet.root;

		stats.inFrustum = 0,
		stats.used = 0,
		stats.active = 0,
		stats.visible = 0,
		this.frameCount ++;

		determineFrustumSet( root, this );
		markUsedSetLeaves( root, this );
		skipTraversal( root, this );
		toggleTiles( root, this );

		lruCache.scheduleUnload();

	}

	// Overrideable
	parseTile( buffer, tile, extension ) {

		return null;

	}

	disposeTile( tile ) {

	}

	preprocessNode( tile, parentTile, tileSetDir ) {

		if ( tile.content ) {

			// Fix old file formats
			if ( ! ( 'uri' in tile.content ) && 'url' in tile.content ) {

				tile.content.uri = tile.content.url;
				delete tile.content.url;

			}

			if ( tile.content.uri ) {

				// tile content uri has to be interpreted relative to the tileset.json
				tile.content.uri = new URL( tile.content.uri, tileSetDir ).toString();

			}

			// NOTE: fix for some cases where tilesets provide the bounding volume
			// but volumes are not present.
			if (
				tile.content.boundingVolume &&
				! (
					'box' in tile.content.boundingVolume ||
					'sphere' in tile.content.boundingVolume ||
					'region' in tile.content.boundingVolume
				)
			) {

				delete tile.content.boundingVolume;

			}

		}

		tile.parent = parentTile;
		tile.children = tile.children || [];

		const uri = tile.content && tile.content.uri;
		if ( uri ) {

			// "content" should only indicate loadable meshes, not external tile sets
			const extension = getUrlExtension( tile.content.uri );
			const isExternalTileSet = Boolean( extension && extension.toLowerCase() === 'json' );
			tile.__externalTileSet = isExternalTileSet;
			tile.__contentEmpty = isExternalTileSet;

		} else {

			tile.__externalTileSet = false;
			tile.__contentEmpty = true;

		}

		// Expected to be set during calculateError()
		tile.__distanceFromCamera = Infinity;
		tile.__error = Infinity;

		tile.__inFrustum = false;
		tile.__isLeaf = false;

		tile.__usedLastFrame = false;
		tile.__used = false;

		tile.__wasSetVisible = false;
		tile.__visible = false;
		tile.__childrenWereVisible = false;
		tile.__allChildrenLoaded = false;

		tile.__wasSetActive = false;
		tile.__active = false;

		tile.__loadingState = UNLOADED;
		tile.__loadIndex = 0;

		tile.__loadAbort = null;

		tile.__depthFromRenderedParent = - 1;
		if ( parentTile === null ) {

			tile.__depth = 0;
			tile.refine = tile.refine || 'REPLACE';

		} else {

			tile.__depth = parentTile.__depth + 1;
			tile.refine = tile.refine || parentTile.refine;

		}

	}

	setTileActive( tile, state ) {

	}

	setTileVisible( tile, state ) {

	}

	calculateError( tile ) {

		return 0;

	}

	tileInView( tile ) {

		return true;

	}

	// Private Functions
	fetchTileSet( url, fetchOptions, parent = null ) {

		return fetch( url, fetchOptions )
			.then( res => {

				if ( res.ok ) {

					return res.json();

				} else {

					throw new Error( `TilesRenderer: Failed to load tileset "${ url }" with status ${ res.status } : ${ res.statusText }` );

				}

			} )
			.then( json => {

				const version = json.asset.version;
				console.assert(
					version === '1.0' || version === '0.0',
					'asset.version is expected to be a string of "1.0" or "0.0"'
				);

				// remove trailing slash and last path-segment from the URL
				const basePath = url.replace( /\/[^\/]*\/?$/, '' );

				traverseSet(
					json.root,
					( node, parent ) => this.preprocessNode( node, parent, basePath ),
					null,
					parent,
					parent ? parent.__depth : 0,
				);

				return json;

			} );

	}

	loadRootTileSet( url ) {

		const tileSets = this.tileSets;
		if ( ! ( url in tileSets ) ) {

			const pr = this
				.fetchTileSet( this.preprocessURL ? this.preprocessURL( url ) : url, this.fetchOptions )
				.then( json => {

					tileSets[ url ] = json;

				} );

			pr.catch( err => {

				console.error( err );
				tileSets[ url ] = err;

			} );

			tileSets[ url ] = pr;

			return pr;

		} else if ( tileSets[ url ] instanceof Error ) {

			return Promise.reject( tileSets[ url ] );

		} else {

			return Promise.resolve( tileSets[ url ] );

		}

	}

	requestTileContents( tile ) {

		// If the tile is already being loaded then don't
		// start it again.
		if ( tile.__loadingState !== UNLOADED ) {

			return;

		}

		const stats = this.stats;
		const lruCache = this.lruCache;
		const downloadQueue = this.downloadQueue;
		const parseQueue = this.parseQueue;
		const isExternalTileSet = tile.__externalTileSet;
		lruCache.add( tile, t => {

			// Stop the load if it's started
			if ( t.__loadingState === LOADING ) {

				t.__loadAbort.abort();
				t.__loadAbort = null;

			} else if ( isExternalTileSet ) {

				t.children.length = 0;

			} else {

				this.disposeTile( t );

			}

			// Decrement stats
			if ( t.__loadingState === LOADING ) {

				stats.downloading --;

			} else if ( t.__loadingState === PARSING ) {

				stats.parsing --;

			}

			t.__loadingState = UNLOADED;
			t.__loadIndex ++;

			parseQueue.remove( t );
			downloadQueue.remove( t );

		} );

		// Track a new load index so we avoid the condition where this load is stopped and
		// another begins soon after so we don't parse twice.
		tile.__loadIndex ++;
		const loadIndex = tile.__loadIndex;
		const controller = new AbortController();
		const signal = controller.signal;

		stats.downloading ++;
		tile.__loadAbort = controller;
		tile.__loadingState = LOADING;

		const errorCallback = e => {

			// if it has been unloaded then the tile has been disposed
			if ( tile.__loadIndex !== loadIndex ) {

				return;

			}

			if ( e.name !== 'AbortError' ) {

				parseQueue.remove( tile );
				downloadQueue.remove( tile );

				if ( tile.__loadingState === PARSING ) {

					stats.parsing --;

				} else if ( tile.__loadingState === LOADING ) {

					stats.downloading --;

				}

				stats.failed ++;

				console.error( `TilesRenderer : Failed to load tile at url "${ tile.content.uri }".` );
				console.error( e );
				tile.__loadingState = FAILED;

			} else {

				lruCache.remove( tile );

			}

		};

		if ( isExternalTileSet ) {

			downloadQueue.add( tile, tileCb => {

				// if it has been unloaded then the tile has been disposed
				if ( tileCb.__loadIndex !== loadIndex ) {

					return Promise.resolve();

				}

				const uri = this.preprocessURL ? this.preprocessURL( tileCb.content.uri ) : tileCb.content.uri;
				return this.fetchTileSet( uri, Object.assign( { signal }, this.fetchOptions ), tileCb );

			} )
				.then( json => {

					// if it has been unloaded then the tile has been disposed
					if ( tile.__loadIndex !== loadIndex ) {

						return;

					}

					stats.downloading --;
					tile.__loadAbort = null;
					tile.__loadingState = LOADED;

					tile.children.push( json.root );

				} )
				.catch( errorCallback );

		} else {

			downloadQueue.add( tile, downloadTile => {

				if ( downloadTile.__loadIndex !== loadIndex ) {

					return Promise.resolve();

				}

				const uri = this.preprocessURL ? this.preprocessURL( downloadTile.content.uri ) : downloadTile.content.uri;
				return fetch( uri, Object.assign( { signal }, this.fetchOptions ) );

			} )
				.then( res => {

					if ( tile.__loadIndex !== loadIndex ) {

						return;

					}

					if ( res.ok ) {

						return res.arrayBuffer();

					} else {

						throw new Error( `Failed to load model with error code ${res.status}` );

					}

				} )
				.then( buffer => {

					// if it has been unloaded then the tile has been disposed
					if ( tile.__loadIndex !== loadIndex ) {

						return;

					}

					stats.downloading --;
					stats.parsing ++;
					tile.__loadAbort = null;
					tile.__loadingState = PARSING;

					return parseQueue.add( tile, parseTile => {

						// if it has been unloaded then the tile has been disposed
						if ( parseTile.__loadIndex !== loadIndex ) {

							return Promise.resolve();

						}

						const uri = parseTile.content.uri;
						const extension = getUrlExtension( uri );

						return this.parseTile( buffer, parseTile, extension );

					} );

				} )
				.then( () => {

					// if it has been unloaded then the tile has been disposed
					if ( tile.__loadIndex !== loadIndex ) {

						return;

					}

					stats.parsing --;
					tile.__loadingState = LOADED;

					if ( tile.__wasSetVisible ) {

						this.setTileVisible( tile, true );

					}

					if ( tile.__wasSetActive ) {

						this.setTileActive( tile, true );

					}

				} )
				.catch( errorCallback );

		}

	}

	dispose() {

		const lruCache = this.lruCache;
		this.traverse( tile => {

			lruCache.remove( tile );

		} );

	}

}
<|endoftext|>


<commit_before>import * as Sentry from '@sentry/browser';
import { getSetting, addCallback } from 'meteor/vulcan:core'

const sentryUrl = getSetting('sentry.url');
const sentryEnvironment = getSetting('sentry.environment');

Sentry.init({
  dsn: sentryUrl,
  beforeBreadcrumb(breadcrumb, hint) {
    if (breadcrumb.level === "error" && breadcrumb.message) {
      Sentry.captureException(breadcrumb.message)
    }
    return breadcrumb
  },
  environment: sentryEnvironment
});
// Initializing sentry on the client browser

function identifyUserToSentry(user) {
  // Set user in sentry scope
  Sentry.configureScope((scope) => {
    scope.setUser({id: user._id, email: user.email, username: user.username});
  });
}

addCallback('events.identify', identifyUserToSentry)

function addUserIdToGoogleAnalytics(user) {
  if (window && window.ga) {
    window.ga('set', 'userId', user._id); // Set the user ID using signed-in user_id.
  }
}

addCallback('events.identify', addUserIdToGoogleAnalytics)
<commit_msg>Remove Sentry logging of all error-level console logs<commit_after>import * as Sentry from '@sentry/browser';
import { getSetting, addCallback } from 'meteor/vulcan:core'

const sentryUrl = getSetting('sentry.url');
const sentryEnvironment = getSetting('sentry.environment');

Sentry.init({
  dsn: sentryUrl,
  environment: sentryEnvironment
});
// Initializing sentry on the client browser

function identifyUserToSentry(user) {
  // Set user in sentry scope
  Sentry.configureScope((scope) => {
    scope.setUser({id: user._id, email: user.email, username: user.username});
  });
}

addCallback('events.identify', identifyUserToSentry)

function addUserIdToGoogleAnalytics(user) {
  if (window && window.ga) {
    window.ga('set', 'userId', user._id); // Set the user ID using signed-in user_id.
  }
}

addCallback('events.identify', addUserIdToGoogleAnalytics)
<|endoftext|>