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

    var fn = T.fn;

    function SynthDefNode(_args) {
        T.Object.call(this, 2, _args);
        fn.fixAR(this);

        var _ = this._;
        this.playbackState = fn.FINISHED_STATE;
        _.poly     = 4;
        _.genList  = [];
        _.genDict  = {};
        _.synthdef = null;
        _.remGen = make_remGen(this);
        _.onended = fn.make_onended(this);
    }
    fn.extend(SynthDefNode);

    var $ = SynthDefNode.prototype;

    Object.defineProperties($, {
        def: {
            set: function(value) {
                if (typeof value === "function") {
                    this._.synthdef = value;
                }
            },
            get: function() {
                return this._.synthdef;
            }
        },
        poly: {
            set: function(value) {
                if (typeof value === "number") {
                    if (0 < value && value <= 64) {
                        this._.poly = value;
                    }
                }
            },
            get: function() {
                return this._.poly;
            }
        }
    });

    var make_doneAction = function(self, opts) {
        return function() {
            self._.remGen(opts.gen);
        };
    };

    var make_remGen = function(self) {
        return function(gen) {
            var _ = self._;
            var i = _.genList.indexOf(gen);
            if (i !== -1) {
                _.genList.splice(i, 1);
            }
            if (typeof gen.noteNum !== "undefined") {
                _.genDict[gen.noteNum] = null;
            }
        };
    };

    var noteOn = function(noteNum, freq, velocity, _opts) {
        velocity |= 0;
        if (velocity <= 0) {
            this.noteOff(this, noteNum);
        } else if (velocity > 127) {
            velocity = 127;
        }
        var _ = this._;
        var list = _.genList, dict = _.genDict;
        var gen = dict[noteNum];
        if (gen) {
            _.remGen(gen);
        }

        var opts = {
            freq    : freq,
            noteNum : noteNum,
            velocity: velocity
        };
        if (_opts) {
            for (var key in _opts) {
                opts[key] = _opts[key];
            }
        }
        opts.doneAction = make_doneAction(this, opts);

        gen = _.synthdef.call(this, opts);

        if (gen instanceof T.Object) {
            gen.noteNum = noteNum;
            list.push(gen);
            dict[noteNum] = opts.gen = gen;

            this.playbackState = fn.PLAYING_STATE;

            if (list.length > _.poly) {
                _.remGen(list[0]);
            }
        }
    };

    var midicps = (function() {
        var table = new Float32Array(128);
        for (var i = 0; i < 128; ++i) {
            table[i] = 440 * Math.pow(2, (i - 69) * 1 / 12);
        }
        return table;
    })();

    var cpsmidi = function(cps) {
        if (cps > 0) {
            return Math.log(cps * 1 / 440) * Math.LOG2E * 12 + 69;
        } else {
            return 0;
        }
    };

    $.noteOn = function(noteNum, velocity, _opts) {
        var freq = midicps[noteNum] || (440 * Math.pow(2, (noteNum - 69) / 12));
        noteOn.call(this, (noteNum + 0.5)|0, freq, velocity, _opts);
        return this;
    };

    $.noteOff = function(noteNum) {
        var gen = this._.genDict[noteNum];
        if (gen && gen.release) {
            gen.release();
        }
        return this;
    };

    $.noteOnWithFreq = function(freq, velocity, _opts) {
        var noteNum = cpsmidi(freq);
        noteOn.call(this, (noteNum + 0.5)|0, freq, velocity, _opts);
        return this;
    };

    $.noteOffWithFreq = function(freq) {
        var noteNum = cpsmidi(freq);
        return this.noteOff((noteNum + 0.5)|0);
    };

    $.allNoteOff = function() {
        var list = this._.genList;
        for (var i = 0, imax = list.length; i < imax; ++i) {
            if (list[i].release) {
                list[i].release();
            }
        }
    };

    $.allSoundOff = function() {
        var _ = this._;
        var list = _.genList;
        var dict = _.genDict;
        while (list.length) {
            delete dict[list.shift().noteNum];
        }
    };

    $.synth = function(_opts) {
        var _ = this._;
        var list = _.genList;
        var gen, opts = {};

        if (_opts) {
            for (var key in _opts) {
                opts[key] = _opts[key];
            }
        }
        opts.doneAction = make_doneAction(this, opts);

        gen = _.synthdef.call(this, opts);

        if (gen instanceof T.Object) {
            list.push(gen);
            opts.gen = gen;
            this.playbackState = fn.PLAYING_STATE;

            if (list.length > _.poly) {
                _.remGen(list[0]);
            }
        }

        return this;
    };

    $.process = function(tickID) {
        var cell = this.cells[0];
        var _ = this._;

        if (this.tickID !== tickID) {
            this.tickID = tickID;

            if (this.playbackState === fn.PLAYING_STATE) {
                var list = _.genList;
                var gen;
                var cellL = this.cells[1];
                var cellR = this.cells[2];
                var i, imax;
                var j, jmax = cell.length;
                var tmpL, tmpR;

                if (list.length) {
                    gen = list[0];
                    gen.process(tickID);
                    cellL.set(gen.cells[1]);
                    cellR.set(gen.cells[2]);
                    for (i = 1, imax = list.length; i < imax; ++i) {
                        gen = list[i];
                        gen.process(tickID);
                        tmpL = gen.cells[1];
                        tmpR = gen.cells[2];
                        for (j = 0; j < jmax; ++j) {
                            cellL[j] += tmpL[j];
                            cellR[j] += tmpR[j];
                        }
                    }
                } else {
                    fn.nextTick(_.onended);
                }
            }

            fn.outputSignalAR(this);
        }

        return this;
    };

    fn.register("SynthDef", SynthDefNode);

    var env_desc = {
        set: function(value) {
            if (fn.isDictionary(value)) {
                if (typeof value.type === "string") {
                    this._.env = value;
                }
            } else if (value instanceof T.Object) {
                this._.env = value;
            }
        },
        get: function() {
            return this._.env;
        }
    };

    fn.register("OscGen", (function() {

        var osc_desc = {
            set: function(value) {
                if (value instanceof T.Object) {
                    this._.osc = value;
                }
            },
            get: function() {
                return this._.osc;
            }
        };

        var wave_desc = {
            set: function(value) {
                if (typeof value === "string") {
                    this._.wave = value;
                }
            },
            get: function() {
                return this._.wave;
            }
        };

        var synthdef = function(opts) {
            var _ = this._;
            var synth, osc, env, envtype;

            osc = _.osc || null;
            env = _.env || {};
            envtype = env.type || "perc";

            if (osc instanceof T.Object) {
                if (typeof osc.clone === "function") {
                    osc = osc.clone();
                }
            }
            if (!osc) {
                osc = T("osc", {wave:_.wave});
            }
            osc.freq = opts.freq;
            osc.mul  = osc.mul * opts.velocity/128;

            synth = osc;
            if (env instanceof T.Object) {
                if (typeof env.clone === "function") {
                    synth = env.clone().append(synth);
                }
            } else {
                synth = T(envtype, env, synth);
            }
            synth.on("ended", opts.doneAction).bang();

            return synth;
        };

        return function(_args) {
            var instance = new SynthDefNode(_args);

            instance._.wave = "sin";

            Object.defineProperties(instance, {
                env: env_desc, osc: osc_desc, wave: wave_desc
            });

            instance.def = synthdef;

            return instance;
        };
    })());

    fn.register("PluckGen", (function() {

        var synthdef = function(opts) {
            var _ = this._;
            var synth, env, envtype;

            env = _.env || {};
            envtype = env.type || "perc";

            synth = T("pluck", {freq:opts.freq, mul:opts.velocity/128}).bang();
            if (env instanceof T.Object) {
                if (typeof env.clone === "function") {
                    synth = env.clone().append(synth);
                }
            } else {
                synth = T(envtype, env, synth);
            }
            synth.on("ended", opts.doneAction).bang();

            return synth;
        };

        return function(_args) {
            var instance = new SynthDefNode(_args);

            Object.defineProperties(instance, {
                env: env_desc
            });

            instance.def = synthdef;

            return instance;
        };
    })());

})(timbre);
<commit_msg>fix .mul<commit_after>(function(T) {
    "use strict";

    var fn = T.fn;

    function SynthDefNode(_args) {
        T.Object.call(this, 2, _args);
        fn.fixAR(this);

        var _ = this._;
        this.playbackState = fn.FINISHED_STATE;
        _.poly     = 4;
        _.genList  = [];
        _.genDict  = {};
        _.synthdef = null;
        _.remGen = make_remGen(this);
        _.onended = fn.make_onended(this);
    }
    fn.extend(SynthDefNode);

    var $ = SynthDefNode.prototype;

    Object.defineProperties($, {
        def: {
            set: function(value) {
                if (typeof value === "function") {
                    this._.synthdef = value;
                }
            },
            get: function() {
                return this._.synthdef;
            }
        },
        poly: {
            set: function(value) {
                if (typeof value === "number") {
                    if (0 < value && value <= 64) {
                        this._.poly = value;
                    }
                }
            },
            get: function() {
                return this._.poly;
            }
        }
    });

    var make_doneAction = function(self, opts) {
        return function() {
            self._.remGen(opts.gen);
        };
    };

    var make_remGen = function(self) {
        return function(gen) {
            var _ = self._;
            var i = _.genList.indexOf(gen);
            if (i !== -1) {
                _.genList.splice(i, 1);
            }
            if (typeof gen.noteNum !== "undefined") {
                _.genDict[gen.noteNum] = null;
            }
        };
    };

    var noteOn = function(noteNum, freq, velocity, _opts) {
        velocity |= 0;
        if (velocity <= 0) {
            this.noteOff(this, noteNum);
        } else if (velocity > 127) {
            velocity = 127;
        }
        var _ = this._;
        var list = _.genList, dict = _.genDict;
        var gen = dict[noteNum];
        if (gen) {
            _.remGen(gen);
        }

        var opts = {
            freq    : freq,
            noteNum : noteNum,
            velocity: velocity,
            mul     : velocity * 0.0078125
        };
        if (_opts) {
            for (var key in _opts) {
                opts[key] = _opts[key];
            }
        }
        opts.doneAction = make_doneAction(this, opts);

        gen = _.synthdef.call(this, opts);

        if (gen instanceof T.Object) {
            gen.noteNum = noteNum;
            list.push(gen);
            dict[noteNum] = opts.gen = gen;

            this.playbackState = fn.PLAYING_STATE;

            if (list.length > _.poly) {
                _.remGen(list[0]);
            }
        }
    };

    var midicps = (function() {
        var table = new Float32Array(128);
        for (var i = 0; i < 128; ++i) {
            table[i] = 440 * Math.pow(2, (i - 69) * 1 / 12);
        }
        return table;
    })();

    var cpsmidi = function(cps) {
        if (cps > 0) {
            return Math.log(cps * 1 / 440) * Math.LOG2E * 12 + 69;
        } else {
            return 0;
        }
    };

    $.noteOn = function(noteNum, velocity, _opts) {
        var freq = midicps[noteNum] || (440 * Math.pow(2, (noteNum - 69) / 12));
        noteOn.call(this, (noteNum + 0.5)|0, freq, velocity, _opts);
        return this;
    };

    $.noteOff = function(noteNum) {
        var gen = this._.genDict[noteNum];
        if (gen && gen.release) {
            gen.release();
        }
        return this;
    };

    $.noteOnWithFreq = function(freq, velocity, _opts) {
        var noteNum = cpsmidi(freq);
        noteOn.call(this, (noteNum + 0.5)|0, freq, velocity, _opts);
        return this;
    };

    $.noteOffWithFreq = function(freq) {
        var noteNum = cpsmidi(freq);
        return this.noteOff((noteNum + 0.5)|0);
    };

    $.allNoteOff = function() {
        var list = this._.genList;
        for (var i = 0, imax = list.length; i < imax; ++i) {
            if (list[i].release) {
                list[i].release();
            }
        }
    };

    $.allSoundOff = function() {
        var _ = this._;
        var list = _.genList;
        var dict = _.genDict;
        while (list.length) {
            delete dict[list.shift().noteNum];
        }
    };

    $.synth = function(_opts) {
        var _ = this._;
        var list = _.genList;
        var gen, opts = {};

        if (_opts) {
            for (var key in _opts) {
                opts[key] = _opts[key];
            }
        }
        opts.doneAction = make_doneAction(this, opts);

        gen = _.synthdef.call(this, opts);

        if (gen instanceof T.Object) {
            list.push(gen);
            opts.gen = gen;
            this.playbackState = fn.PLAYING_STATE;

            if (list.length > _.poly) {
                _.remGen(list[0]);
            }
        }

        return this;
    };

    $.process = function(tickID) {
        var cell = this.cells[0];
        var _ = this._;

        if (this.tickID !== tickID) {
            this.tickID = tickID;

            if (this.playbackState === fn.PLAYING_STATE) {
                var list = _.genList;
                var gen;
                var cellL = this.cells[1];
                var cellR = this.cells[2];
                var i, imax;
                var j, jmax = cell.length;
                var tmpL, tmpR;

                if (list.length) {
                    gen = list[0];
                    gen.process(tickID);
                    cellL.set(gen.cells[1]);
                    cellR.set(gen.cells[2]);
                    for (i = 1, imax = list.length; i < imax; ++i) {
                        gen = list[i];
                        gen.process(tickID);
                        tmpL = gen.cells[1];
                        tmpR = gen.cells[2];
                        for (j = 0; j < jmax; ++j) {
                            cellL[j] += tmpL[j];
                            cellR[j] += tmpR[j];
                        }
                    }
                } else {
                    fn.nextTick(_.onended);
                }
            }

            fn.outputSignalAR(this);
        }

        return this;
    };

    fn.register("SynthDef", SynthDefNode);

    var env_desc = {
        set: function(value) {
            if (fn.isDictionary(value)) {
                if (typeof value.type === "string") {
                    this._.env = value;
                }
            } else if (value instanceof T.Object) {
                this._.env = value;
            }
        },
        get: function() {
            return this._.env;
        }
    };

    fn.register("OscGen", (function() {

        var osc_desc = {
            set: function(value) {
                if (value instanceof T.Object) {
                    this._.osc = value;
                }
            },
            get: function() {
                return this._.osc;
            }
        };

        var wave_desc = {
            set: function(value) {
                if (typeof value === "string") {
                    this._.wave = value;
                }
            },
            get: function() {
                return this._.wave;
            }
        };

        var synthdef = function(opts) {
            var _ = this._;
            var synth, osc, env, envtype;

            osc = _.osc || null;
            env = _.env || {};
            envtype = env.type || "perc";

            if (osc instanceof T.Object) {
                if (typeof osc.clone === "function") {
                    osc = osc.clone();
                }
            }
            if (!osc) {
                osc = T("osc", {wave:_.wave});
            }
            osc.freq = opts.freq;
            osc.mul  = osc.mul * opts.velocity/128;

            synth = osc;
            if (env instanceof T.Object) {
                if (typeof env.clone === "function") {
                    synth = env.clone().append(synth);
                }
            } else {
                synth = T(envtype, env, synth);
            }
            synth.on("ended", opts.doneAction).bang();

            return synth;
        };

        return function(_args) {
            var instance = new SynthDefNode(_args);

            instance._.wave = "sin";

            Object.defineProperties(instance, {
                env: env_desc, osc: osc_desc, wave: wave_desc
            });

            instance.def = synthdef;

            return instance;
        };
    })());

    fn.register("PluckGen", (function() {

        var synthdef = function(opts) {
            var _ = this._;
            var synth, env, envtype;

            env = _.env || {};
            envtype = env.type || "perc";

            synth = T("pluck", {freq:opts.freq, mul:opts.velocity/128}).bang();
            if (env instanceof T.Object) {
                if (typeof env.clone === "function") {
                    synth = env.clone().append(synth);
                }
            } else {
                synth = T(envtype, env, synth);
            }
            synth.on("ended", opts.doneAction).bang();

            return synth;
        };

        return function(_args) {
            var instance = new SynthDefNode(_args);

            Object.defineProperties(instance, {
                env: env_desc
            });

            instance.def = synthdef;

            return instance;
        };
    })());

})(timbre);
<|endoftext|>


<commit_before>/**
 * @module ol/render/canvas
 */
import {getFontFamilies} from '../css.js';
import {createCanvasContext2D} from '../dom.js';
import {clear} from '../obj.js';
import LRUCache from '../structs/LRUCache.js';
import {create as createTransform} from '../transform.js';

/**
 * @typedef {{fillStyle: module:ol/colorlike~ColorLike}} FillState
 */

/**
 * @typedef {Object} FillStrokeState
 * @property {module:ol/colorlike~ColorLike} [currentFillStyle]
 * @property {module:ol/colorlike~ColorLike} [currentStrokeStyle]
 * @property string{} [currentLineCap]
 * @property {Array.<number>} currentLineDash
 * @property {number} [currentLineDashOffset]
 * @property {string} [currentLineJoin]
 * @property {number} [currentLineWidth]
 * @property {number} [currentMiterLimit]
 * @property {number} [lastStroke]
 * @property {module:ol/colorlike~ColorLike} [fillStyle]
 * @property {module:ol/colorlike~ColorLike} [strokeStyle]
 * @property {string} [lineCap]
 * @property {Array.<number>} lineDash
 * @property {number} [lineDashOffset]
 * @property {string} [lineJoin]
 * @property {number} [lineWidth]
 * @property {number} [miterLimit]
 */

/**
 * @typedef {Object} StrokeState
 * @property {string} lineCap
 * @property {Array.<number>} lineDash
 * @property {number} lineDashOffset
 * @property {string} lineJoin
 * @property {number} lineWidth
 * @property {number} miterLimit
 * @property {module:ol/colorlike~ColorLike} strokeStyle
 */

/**
 * @typedef {Object} TextState
 * @property {string} font
 * @property {string} [textAlign]
 * @property {string} textBaseline
 */

/**
 * Container for decluttered replay instructions that need to be rendered or
 * omitted together, i.e. when styles render both an image and text, or for the
 * characters that form text along lines. The basic elements of this array are
 * `[minX, minY, maxX, maxY, count]`, where the first four entries are the
 * rendered extent of the group in pixel space. `count` is the number of styles
 * in the group, i.e. 2 when an image and a text are grouped, or 1 otherwise.
 * In addition to these four elements, declutter instruction arrays (i.e. the
 * arguments to @{link ol.render.canvas.drawImage} are appended to the array.
 * @typedef {Array.<*>} DeclutterGroup
 */

/**
 * @const
 * @type {string}
 */
export const defaultFont = '10px sans-serif';

/**
 * @const
 * @type {module:ol/color~Color}
 */
export const defaultFillStyle = [0, 0, 0, 1];

/**
 * @const
 * @type {string}
 */
export const defaultLineCap = 'round';

/**
 * @const
 * @type {Array.<number>}
 */
export const defaultLineDash = [];

/**
 * @const
 * @type {number}
 */
export const defaultLineDashOffset = 0;

/**
 * @const
 * @type {string}
 */
export const defaultLineJoin = 'round';

/**
 * @const
 * @type {number}
 */
export const defaultMiterLimit = 10;

/**
 * @const
 * @type {module:ol/color~Color}
 */
export const defaultStrokeStyle = [0, 0, 0, 1];

/**
 * @const
 * @type {string}
 */
export const defaultTextAlign = 'center';

/**
 * @const
 * @type {string}
 */
export const defaultTextBaseline = 'middle';

/**
 * @const
 * @type {Array.<number>}
 */
export const defaultPadding = [0, 0, 0, 0];

/**
 * @const
 * @type {number}
 */
export const defaultLineWidth = 1;

/**
 * The label cache for text rendering. To change the default cache size of 2048
 * entries, use {@link ol.structs.LRUCache#setSize}.
 * @type {ol.structs.LRUCache.<HTMLCanvasElement>}
 * @api
 */
export const labelCache = new LRUCache();

/**
 * @type {!Object.<string, number>}
 */
export const checkedFonts = {};

/**
 * @type {CanvasRenderingContext2D}
 */
let measureContext = null;

/**
 * @type {!Object.<string, number>}
 */
export const textHeights = {};

/**
 * Clears the label cache when a font becomes available.
 * @param {string} fontSpec CSS font spec.
 */
export const checkFont = (function() {
  const retries = 60;
  const checked = checkedFonts;
  const size = '32px ';
  const referenceFonts = ['monospace', 'serif'];
  const len = referenceFonts.length;
  const text = 'wmytzilWMYTZIL@#/&?$%10\uF013';
  let interval, referenceWidth;

  function isAvailable(font) {
    const context = getMeasureContext();
    let available = true;
    for (let i = 0; i < len; ++i) {
      const referenceFont = referenceFonts[i];
      context.font = size + referenceFont;
      referenceWidth = context.measureText(text).width;
      if (font != referenceFont) {
        context.font = size + font + ',' + referenceFont;
        const width = context.measureText(text).width;
        // If width and referenceWidth are the same, then the fallback was used
        // instead of the font we wanted, so the font is not available.
        available = available && width != referenceWidth;
      }
    }
    return available;
  }

  function check() {
    let done = true;
    for (const font in checked) {
      if (checked[font] < retries) {
        if (isAvailable(font)) {
          checked[font] = retries;
          clear(textHeights);
          // Make sure that loaded fonts are picked up by Safari
          measureContext = null;
          labelCache.clear();
        } else {
          ++checked[font];
          done = false;
        }
      }
    }
    if (done) {
      clearInterval(interval);
      interval = undefined;
    }
  }

  return function(fontSpec) {
    const fontFamilies = getFontFamilies(fontSpec);
    if (!fontFamilies) {
      return;
    }
    for (let i = 0, ii = fontFamilies.length; i < ii; ++i) {
      const fontFamily = fontFamilies[i];
      if (!(fontFamily in checked)) {
        checked[fontFamily] = retries;
        if (!isAvailable(fontFamily)) {
          checked[fontFamily] = 0;
          if (interval === undefined) {
            interval = setInterval(check, 32);
          }
        }
      }
    }
  };
})();

/**
 * @return {CanvasRenderingContext2D} Measure context.
 */
function getMeasureContext() {
  if (!measureContext) {
    measureContext = createCanvasContext2D(1, 1);
  }
  return measureContext;
}

/**
 * @param {string} font Font to use for measuring.
 * @return {module:ol/size~Size} Measurement.
 */
export const measureTextHeight = (function() {
  let span;
  const heights = textHeights;
  return function(font) {
    let height = heights[font];
    if (height == undefined) {
      if (!span) {
        span = document.createElement('span');
        span.textContent = 'M';
        span.style.margin = span.style.padding = '0 !important';
        span.style.position = 'absolute !important';
        span.style.left = '-99999px !important';
      }
      span.style.font = font;
      document.body.appendChild(span);
      height = heights[font] = span.offsetHeight;
      document.body.removeChild(span);
    }
    return height;
  };
})();

/**
 * @param {string} font Font.
 * @param {string} text Text.
 * @return {number} Width.
 */
export function measureTextWidth(font, text) {
  const measureContext = getMeasureContext();
  if (font != measureContext.font) {
    measureContext.font = font;
  }
  return measureContext.measureText(text).width;
}

/**
 * @param {CanvasRenderingContext2D} context Context.
 * @param {number} rotation Rotation.
 * @param {number} offsetX X offset.
 * @param {number} offsetY Y offset.
 */
export function rotateAtOffset(context, rotation, offsetX, offsetY) {
  if (rotation !== 0) {
    context.translate(offsetX, offsetY);
    context.rotate(rotation);
    context.translate(-offsetX, -offsetY);
  }
}

export const resetTransform = createTransform();

/**
 * @param {CanvasRenderingContext2D} context Context.
 * @param {module:ol/transform~Transform|null} transform Transform.
 * @param {number} opacity Opacity.
 * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} image Image.
 * @param {number} originX Origin X.
 * @param {number} originY Origin Y.
 * @param {number} w Width.
 * @param {number} h Height.
 * @param {number} x X.
 * @param {number} y Y.
 * @param {number} scale Scale.
 */
export function drawImage(context,
  transform, opacity, image, originX, originY, w, h, x, y, scale) {
  let alpha;
  if (opacity != 1) {
    alpha = context.globalAlpha;
    context.globalAlpha = alpha * opacity;
  }
  if (transform) {
    context.setTransform.apply(context, transform);
  }

  context.drawImage(image, originX, originY, w, h, x, y, w * scale, h * scale);

  if (alpha) {
    context.globalAlpha = alpha;
  }
  if (transform) {
    context.setTransform.apply(context, resetTransform);
  }
}
<commit_msg>Type is {string} instead of string{}<commit_after>/**
 * @module ol/render/canvas
 */
import {getFontFamilies} from '../css.js';
import {createCanvasContext2D} from '../dom.js';
import {clear} from '../obj.js';
import LRUCache from '../structs/LRUCache.js';
import {create as createTransform} from '../transform.js';

/**
 * @typedef {{fillStyle: module:ol/colorlike~ColorLike}} FillState
 */

/**
 * @typedef {Object} FillStrokeState
 * @property {module:ol/colorlike~ColorLike} [currentFillStyle]
 * @property {module:ol/colorlike~ColorLike} [currentStrokeStyle]
 * @property {string} [currentLineCap]
 * @property {Array.<number>} currentLineDash
 * @property {number} [currentLineDashOffset]
 * @property {string} [currentLineJoin]
 * @property {number} [currentLineWidth]
 * @property {number} [currentMiterLimit]
 * @property {number} [lastStroke]
 * @property {module:ol/colorlike~ColorLike} [fillStyle]
 * @property {module:ol/colorlike~ColorLike} [strokeStyle]
 * @property {string} [lineCap]
 * @property {Array.<number>} lineDash
 * @property {number} [lineDashOffset]
 * @property {string} [lineJoin]
 * @property {number} [lineWidth]
 * @property {number} [miterLimit]
 */

/**
 * @typedef {Object} StrokeState
 * @property {string} lineCap
 * @property {Array.<number>} lineDash
 * @property {number} lineDashOffset
 * @property {string} lineJoin
 * @property {number} lineWidth
 * @property {number} miterLimit
 * @property {module:ol/colorlike~ColorLike} strokeStyle
 */

/**
 * @typedef {Object} TextState
 * @property {string} font
 * @property {string} [textAlign]
 * @property {string} textBaseline
 */

/**
 * Container for decluttered replay instructions that need to be rendered or
 * omitted together, i.e. when styles render both an image and text, or for the
 * characters that form text along lines. The basic elements of this array are
 * `[minX, minY, maxX, maxY, count]`, where the first four entries are the
 * rendered extent of the group in pixel space. `count` is the number of styles
 * in the group, i.e. 2 when an image and a text are grouped, or 1 otherwise.
 * In addition to these four elements, declutter instruction arrays (i.e. the
 * arguments to @{link ol.render.canvas.drawImage} are appended to the array.
 * @typedef {Array.<*>} DeclutterGroup
 */

/**
 * @const
 * @type {string}
 */
export const defaultFont = '10px sans-serif';

/**
 * @const
 * @type {module:ol/color~Color}
 */
export const defaultFillStyle = [0, 0, 0, 1];

/**
 * @const
 * @type {string}
 */
export const defaultLineCap = 'round';

/**
 * @const
 * @type {Array.<number>}
 */
export const defaultLineDash = [];

/**
 * @const
 * @type {number}
 */
export const defaultLineDashOffset = 0;

/**
 * @const
 * @type {string}
 */
export const defaultLineJoin = 'round';

/**
 * @const
 * @type {number}
 */
export const defaultMiterLimit = 10;

/**
 * @const
 * @type {module:ol/color~Color}
 */
export const defaultStrokeStyle = [0, 0, 0, 1];

/**
 * @const
 * @type {string}
 */
export const defaultTextAlign = 'center';

/**
 * @const
 * @type {string}
 */
export const defaultTextBaseline = 'middle';

/**
 * @const
 * @type {Array.<number>}
 */
export const defaultPadding = [0, 0, 0, 0];

/**
 * @const
 * @type {number}
 */
export const defaultLineWidth = 1;

/**
 * The label cache for text rendering. To change the default cache size of 2048
 * entries, use {@link ol.structs.LRUCache#setSize}.
 * @type {ol.structs.LRUCache.<HTMLCanvasElement>}
 * @api
 */
export const labelCache = new LRUCache();

/**
 * @type {!Object.<string, number>}
 */
export const checkedFonts = {};

/**
 * @type {CanvasRenderingContext2D}
 */
let measureContext = null;

/**
 * @type {!Object.<string, number>}
 */
export const textHeights = {};

/**
 * Clears the label cache when a font becomes available.
 * @param {string} fontSpec CSS font spec.
 */
export const checkFont = (function() {
  const retries = 60;
  const checked = checkedFonts;
  const size = '32px ';
  const referenceFonts = ['monospace', 'serif'];
  const len = referenceFonts.length;
  const text = 'wmytzilWMYTZIL@#/&?$%10\uF013';
  let interval, referenceWidth;

  function isAvailable(font) {
    const context = getMeasureContext();
    let available = true;
    for (let i = 0; i < len; ++i) {
      const referenceFont = referenceFonts[i];
      context.font = size + referenceFont;
      referenceWidth = context.measureText(text).width;
      if (font != referenceFont) {
        context.font = size + font + ',' + referenceFont;
        const width = context.measureText(text).width;
        // If width and referenceWidth are the same, then the fallback was used
        // instead of the font we wanted, so the font is not available.
        available = available && width != referenceWidth;
      }
    }
    return available;
  }

  function check() {
    let done = true;
    for (const font in checked) {
      if (checked[font] < retries) {
        if (isAvailable(font)) {
          checked[font] = retries;
          clear(textHeights);
          // Make sure that loaded fonts are picked up by Safari
          measureContext = null;
          labelCache.clear();
        } else {
          ++checked[font];
          done = false;
        }
      }
    }
    if (done) {
      clearInterval(interval);
      interval = undefined;
    }
  }

  return function(fontSpec) {
    const fontFamilies = getFontFamilies(fontSpec);
    if (!fontFamilies) {
      return;
    }
    for (let i = 0, ii = fontFamilies.length; i < ii; ++i) {
      const fontFamily = fontFamilies[i];
      if (!(fontFamily in checked)) {
        checked[fontFamily] = retries;
        if (!isAvailable(fontFamily)) {
          checked[fontFamily] = 0;
          if (interval === undefined) {
            interval = setInterval(check, 32);
          }
        }
      }
    }
  };
})();

/**
 * @return {CanvasRenderingContext2D} Measure context.
 */
function getMeasureContext() {
  if (!measureContext) {
    measureContext = createCanvasContext2D(1, 1);
  }
  return measureContext;
}

/**
 * @param {string} font Font to use for measuring.
 * @return {module:ol/size~Size} Measurement.
 */
export const measureTextHeight = (function() {
  let span;
  const heights = textHeights;
  return function(font) {
    let height = heights[font];
    if (height == undefined) {
      if (!span) {
        span = document.createElement('span');
        span.textContent = 'M';
        span.style.margin = span.style.padding = '0 !important';
        span.style.position = 'absolute !important';
        span.style.left = '-99999px !important';
      }
      span.style.font = font;
      document.body.appendChild(span);
      height = heights[font] = span.offsetHeight;
      document.body.removeChild(span);
    }
    return height;
  };
})();

/**
 * @param {string} font Font.
 * @param {string} text Text.
 * @return {number} Width.
 */
export function measureTextWidth(font, text) {
  const measureContext = getMeasureContext();
  if (font != measureContext.font) {
    measureContext.font = font;
  }
  return measureContext.measureText(text).width;
}

/**
 * @param {CanvasRenderingContext2D} context Context.
 * @param {number} rotation Rotation.
 * @param {number} offsetX X offset.
 * @param {number} offsetY Y offset.
 */
export function rotateAtOffset(context, rotation, offsetX, offsetY) {
  if (rotation !== 0) {
    context.translate(offsetX, offsetY);
    context.rotate(rotation);
    context.translate(-offsetX, -offsetY);
  }
}

export const resetTransform = createTransform();

/**
 * @param {CanvasRenderingContext2D} context Context.
 * @param {module:ol/transform~Transform|null} transform Transform.
 * @param {number} opacity Opacity.
 * @param {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} image Image.
 * @param {number} originX Origin X.
 * @param {number} originY Origin Y.
 * @param {number} w Width.
 * @param {number} h Height.
 * @param {number} x X.
 * @param {number} y Y.
 * @param {number} scale Scale.
 */
export function drawImage(context,
  transform, opacity, image, originX, originY, w, h, x, y, scale) {
  let alpha;
  if (opacity != 1) {
    alpha = context.globalAlpha;
    context.globalAlpha = alpha * opacity;
  }
  if (transform) {
    context.setTransform.apply(context, transform);
  }

  context.drawImage(image, originX, originY, w, h, x, y, w * scale, h * scale);

  if (alpha) {
    context.globalAlpha = alpha;
  }
  if (transform) {
    context.setTransform.apply(context, resetTransform);
  }
}
<|endoftext|>


<commit_before>goog.provide('ol.reproj');

goog.require('goog.array');
goog.require('goog.math');
goog.require('ol.extent');
goog.require('ol.math');
goog.require('ol.proj');

/**
 * Calculates ideal resolution to use from the source in order to achieve
 * pixel mapping as close as possible to 1:1 during reprojection.
 * The resolution is calculated regardless on what resolutions
 * are actually available in the dataset (TileGrid, Image, ...).
 *
 * @param {ol.proj.Projection} sourceProj
 * @param {ol.proj.Projection} targetProj
 * @param {ol.Coordinate} targetCenter
 * @param {number} targetResolution
 * @return {number} The best resolution to use. Can be +-Infinity, NaN or 0.
 */
ol.reproj.calculateSourceResolution = function(sourceProj, targetProj,
    targetCenter, targetResolution) {

  var sourceCenter = ol.proj.transform(targetCenter, targetProj, sourceProj);

  // calculate the ideal resolution of the source data
  var sourceResolution =
      targetProj.getPointResolution(targetResolution, targetCenter) *
      targetProj.getMetersPerUnit() / sourceProj.getMetersPerUnit();

  // based on the projection properties, the point resolution at the specified
  // coordinates may be slightly different. We need to reverse-compensate this
  // in order to achieve optimal results.

  var compensationFactor =
      sourceProj.getPointResolution(sourceResolution, sourceCenter) /
      sourceResolution;

  if (goog.math.isFiniteNumber(compensationFactor) && compensationFactor > 0) {
    sourceResolution /= compensationFactor;
  }

  return sourceResolution;
};

/**
 * Renders the source into the canvas based on the triangulation.
 * @param {CanvasRenderingContext2D} context
 * @param {number} sourceResolution
 * @param {number} targetResolution
 * @param {ol.Extent} targetExtent
 * @param {ol.reproj.Triangulation} triangulation
 * @param {Array.<{extent: ol.Extent,
 *                 image: (HTMLCanvasElement|Image)}>} sources
 */
ol.reproj.renderTriangles = function(context,
    sourceResolution, targetResolution, targetExtent, triangulation, sources) {

  var renderImage = function(image) {
    context.scale(sourceResolution, -sourceResolution);

    // the image has to be scaled by half a pixel in every direction
    //    in order to prevent artifacts between the original tiles
    //    that are introduced by the canvas antialiasing.
    context.drawImage(image, -0.5, -0.5,
                      image.width + 1, image.height + 1);
  };

  var shiftDistance = triangulation.shiftDistance;

  goog.array.forEach(triangulation.triangles, function(tri, i, arr) {
    context.save();

    var targetTL = ol.extent.getTopLeft(targetExtent);

    /* Calculate affine transform (src -> dst)
     * Resulting matrix can be used to transform coordinate
     * from `sourceProjection` to destination pixels.
     *
     * To optimize number of context calls and increase numerical stability,
     * we also do the following operations:
     * trans(-topLeftExtentCorner), scale(1 / targetResolution), scale(1, -1)
     * here before solving the linear system.
     *
     * Src points: xi, yi
     * Dst points: ui, vi
     * Affine coefficients: aij
     *
     * | x0 y0 1  0  0 0 |   |a00|   |u0|
     * | x1 y1 1  0  0 0 |   |a01|   |u1|
     * | x2 y2 1  0  0 0 | x |a02| = |u2|
     * |  0  0 0 x0 y0 1 |   |a10|   |v0|
     * |  0  0 0 x1 y1 1 |   |a11|   |v1|
     * |  0  0 0 x2 y2 1 |   |a12|   |v2|
     */
    var src = tri.source, tgt = tri.target;
    var x0 = src[0][0], y0 = src[0][1],
        x1 = src[1][0], y1 = src[1][1],
        x2 = src[2][0], y2 = src[2][1];
    var u0 = tgt[0][0] - targetTL[0], v0 = -(tgt[0][1] - targetTL[1]),
        u1 = tgt[1][0] - targetTL[0], v1 = -(tgt[1][1] - targetTL[1]),
        u2 = tgt[2][0] - targetTL[0], v2 = -(tgt[2][1] - targetTL[1]);
    if (tri.needsShift) {
      x0 = goog.math.modulo(x0 + shiftDistance, shiftDistance);
      x1 = goog.math.modulo(x1 + shiftDistance, shiftDistance);
      x2 = goog.math.modulo(x2 + shiftDistance, shiftDistance);
    }
    var augmentedMatrix = [
      [x0, y0, 1, 0, 0, 0, u0 / targetResolution],
      [x1, y1, 1, 0, 0, 0, u1 / targetResolution],
      [x2, y2, 1, 0, 0, 0, u2 / targetResolution],
      [0, 0, 0, x0, y0, 1, v0 / targetResolution],
      [0, 0, 0, x1, y1, 1, v1 / targetResolution],
      [0, 0, 0, x2, y2, 1, v2 / targetResolution]
    ];
    var coefs = ol.math.solveLinearSystem(augmentedMatrix);
    if (goog.isNull(coefs)) {
      return;
    }

    context.setTransform(coefs[0], coefs[3], coefs[1],
                         coefs[4], coefs[2], coefs[5]);

    var pixelSize = sourceResolution;
    var centroid = [(x0 + x1 + x2) / 3, (y0 + y1 + y2) / 3];

    // moves the `point` farther away from the `anchor`
    var increasePointDistance = function(point, anchor, increment) {
      var dir = [point[0] - anchor[0], point[1] - anchor[1]];
      var distance = Math.sqrt(dir[0] * dir[0] + dir[1] * dir[1]);
      var scaleFactor = (distance + increment) / distance;
      return [anchor[0] + scaleFactor * dir[0],
              anchor[1] + scaleFactor * dir[1]];
    };

    // enlarge the triangle so that the clip paths of individual triangles
    //   slightly (1px) overlap to prevent transparency errors on triangle edges
    var p0 = increasePointDistance([x0, y0], centroid, pixelSize);
    var p1 = increasePointDistance([x1, y1], centroid, pixelSize);
    var p2 = increasePointDistance([x2, y2], centroid, pixelSize);

    context.beginPath();
    context.moveTo(p0[0], p0[1]);
    context.lineTo(p1[0], p1[1]);
    context.lineTo(p2[0], p2[1]);
    context.closePath();
    context.clip();

    goog.array.forEach(sources, function(src, i, arr) {
      context.save();
      var tlSrcFromData = ol.extent.getTopLeft(src.extent);
      context.translate(tlSrcFromData[0], tlSrcFromData[1]);
      if (tri.needsShift) {
        context.save();
        context.translate(shiftDistance, 0);
        renderImage(src.image);
        context.restore();
        renderImage(src.image);

        if (goog.DEBUG) {
          context.fillStyle =
              sources.length > 16 ? 'rgba(255,0,0,1)' :
              (sources.length > 4 ? 'rgba(0,255,0,.3)' : 'rgba(0,0,255,.1)');
          context.fillRect(0, 0, 256, 256);
        }
      } else {
        renderImage(src.image);
      }
      context.restore();
    });

    if (goog.DEBUG) {
      context.strokeStyle = 'black';
      context.lineWidth = 2 * pixelSize;
      context.beginPath();
      context.moveTo(x0, y0);
      context.lineTo(x1, y1);
      context.lineTo(x2, y2);
      context.closePath();
      context.stroke();
    }

    context.restore();
  });
};
<commit_msg>Reproject every image only once per triangle<commit_after>goog.provide('ol.reproj');

goog.require('goog.array');
goog.require('goog.math');
goog.require('ol.extent');
goog.require('ol.math');
goog.require('ol.proj');

/**
 * Calculates ideal resolution to use from the source in order to achieve
 * pixel mapping as close as possible to 1:1 during reprojection.
 * The resolution is calculated regardless on what resolutions
 * are actually available in the dataset (TileGrid, Image, ...).
 *
 * @param {ol.proj.Projection} sourceProj
 * @param {ol.proj.Projection} targetProj
 * @param {ol.Coordinate} targetCenter
 * @param {number} targetResolution
 * @return {number} The best resolution to use. Can be +-Infinity, NaN or 0.
 */
ol.reproj.calculateSourceResolution = function(sourceProj, targetProj,
    targetCenter, targetResolution) {

  var sourceCenter = ol.proj.transform(targetCenter, targetProj, sourceProj);

  // calculate the ideal resolution of the source data
  var sourceResolution =
      targetProj.getPointResolution(targetResolution, targetCenter) *
      targetProj.getMetersPerUnit() / sourceProj.getMetersPerUnit();

  // based on the projection properties, the point resolution at the specified
  // coordinates may be slightly different. We need to reverse-compensate this
  // in order to achieve optimal results.

  var compensationFactor =
      sourceProj.getPointResolution(sourceResolution, sourceCenter) /
      sourceResolution;

  if (goog.math.isFiniteNumber(compensationFactor) && compensationFactor > 0) {
    sourceResolution /= compensationFactor;
  }

  return sourceResolution;
};

/**
 * Renders the source into the canvas based on the triangulation.
 * @param {CanvasRenderingContext2D} context
 * @param {number} sourceResolution
 * @param {number} targetResolution
 * @param {ol.Extent} targetExtent
 * @param {ol.reproj.Triangulation} triangulation
 * @param {Array.<{extent: ol.Extent,
 *                 image: (HTMLCanvasElement|Image)}>} sources
 */
ol.reproj.renderTriangles = function(context,
    sourceResolution, targetResolution, targetExtent, triangulation, sources) {

  var shiftDistance = triangulation.shiftDistance;
  var targetTL = ol.extent.getTopLeft(targetExtent);

  goog.array.forEach(triangulation.triangles, function(tri, i, arr) {
    context.save();

    /* Calculate affine transform (src -> dst)
     * Resulting matrix can be used to transform coordinate
     * from `sourceProjection` to destination pixels.
     *
     * To optimize number of context calls and increase numerical stability,
     * we also do the following operations:
     * trans(-topLeftExtentCorner), scale(1 / targetResolution), scale(1, -1)
     * here before solving the linear system.
     *
     * Src points: xi, yi
     * Dst points: ui, vi
     * Affine coefficients: aij
     *
     * | x0 y0 1  0  0 0 |   |a00|   |u0|
     * | x1 y1 1  0  0 0 |   |a01|   |u1|
     * | x2 y2 1  0  0 0 | x |a02| = |u2|
     * |  0  0 0 x0 y0 1 |   |a10|   |v0|
     * |  0  0 0 x1 y1 1 |   |a11|   |v1|
     * |  0  0 0 x2 y2 1 |   |a12|   |v2|
     */
    var src = tri.source, tgt = tri.target;
    var x0 = src[0][0], y0 = src[0][1],
        x1 = src[1][0], y1 = src[1][1],
        x2 = src[2][0], y2 = src[2][1];
    var u0 = tgt[0][0] - targetTL[0], v0 = -(tgt[0][1] - targetTL[1]),
        u1 = tgt[1][0] - targetTL[0], v1 = -(tgt[1][1] - targetTL[1]),
        u2 = tgt[2][0] - targetTL[0], v2 = -(tgt[2][1] - targetTL[1]);
    if (tri.needsShift) {
      x0 = goog.math.modulo(x0 + shiftDistance, shiftDistance);
      x1 = goog.math.modulo(x1 + shiftDistance, shiftDistance);
      x2 = goog.math.modulo(x2 + shiftDistance, shiftDistance);
    }
    var augmentedMatrix = [
      [x0, y0, 1, 0, 0, 0, u0 / targetResolution],
      [x1, y1, 1, 0, 0, 0, u1 / targetResolution],
      [x2, y2, 1, 0, 0, 0, u2 / targetResolution],
      [0, 0, 0, x0, y0, 1, v0 / targetResolution],
      [0, 0, 0, x1, y1, 1, v1 / targetResolution],
      [0, 0, 0, x2, y2, 1, v2 / targetResolution]
    ];
    var coefs = ol.math.solveLinearSystem(augmentedMatrix);
    if (goog.isNull(coefs)) {
      return;
    }

    context.setTransform(coefs[0], coefs[3], coefs[1],
                         coefs[4], coefs[2], coefs[5]);

    var pixelSize = sourceResolution;
    var centroid = [(x0 + x1 + x2) / 3, (y0 + y1 + y2) / 3];

    // moves the `point` farther away from the `anchor`
    var increasePointDistance = function(point, anchor, increment) {
      var dir = [point[0] - anchor[0], point[1] - anchor[1]];
      var distance = Math.sqrt(dir[0] * dir[0] + dir[1] * dir[1]);
      var scaleFactor = (distance + increment) / distance;
      return [anchor[0] + scaleFactor * dir[0],
              anchor[1] + scaleFactor * dir[1]];
    };

    // enlarge the triangle so that the clip paths of individual triangles
    //   slightly (1px) overlap to prevent transparency errors on triangle edges
    var p0 = increasePointDistance([x0, y0], centroid, pixelSize);
    var p1 = increasePointDistance([x1, y1], centroid, pixelSize);
    var p2 = increasePointDistance([x2, y2], centroid, pixelSize);

    context.beginPath();
    context.moveTo(p0[0], p0[1]);
    context.lineTo(p1[0], p1[1]);
    context.lineTo(p2[0], p2[1]);
    context.closePath();
    context.clip();

    goog.array.forEach(sources, function(src, i, arr) {
      context.save();
      var dataTL = ol.extent.getTopLeft(src.extent);
      context.translate(dataTL[0], dataTL[1]);
      // if the triangle needs to be shifted (because of the dateline wrapping),
      // shift only the source images that need it
      if (tri.needsShift && dataTL[0] < 0) {
        context.translate(shiftDistance, 0);
      }
      context.scale(sourceResolution, -sourceResolution);

      // the image has to be scaled by half a pixel in every direction
      //    in order to prevent artifacts between the original tiles
      //    that are introduced by the canvas antialiasing.
      context.drawImage(src.image, -0.5, -0.5,
                        src.image.width + 1, src.image.height + 1);

      context.restore();
    });

    if (goog.DEBUG) {
      context.strokeStyle = 'black';
      context.lineWidth = 2 * pixelSize;
      context.beginPath();
      context.moveTo(x0, y0);
      context.lineTo(x1, y1);
      context.lineTo(x2, y2);
      context.closePath();
      context.stroke();
    }

    context.restore();
  });
};
<|endoftext|>


<commit_before>/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */

const express = require("express");
const session = require("express-session")
const msal = require('@azure/msal-node');

/**
 * Command line arguments can be used to configure:
 * - The port the application runs on
 * - The cache file location
 * - The authentication scenario/configuration file name
 */
const argv = require("../cliArgs");
const { request } = require("express");

const SERVER_PORT = argv.p || 3000;
const cacheLocation = argv.c || "./data/cache.json";
const cachePlugin = require('../cachePlugin')(cacheLocation);

/**
 * The scenario string is the name of a .json file which contains the MSAL client configuration
 * For an example of what a configuration file should look like, check out the customConfig.json file in the
 * /config directory.
 *
 * You can create your own configuration file and replace the path inside the "config" require statement below
 * with the path to your custom configuraiton.
 */
const scenario = argv.s || "customConfig";
const config = require(`./config/${scenario}.json`);

const sessionConfig = {
    secret: 'ENTER_YOUR_SECRET_HERE',
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: false, // set this to true on production
    }
}

// Sample Application Code
const getTokenAuthCode = function (scenarioConfig, clientApplication, port) {
    // Set the port that the express server will listen on
    const serverPort = port || SERVER_PORT;
    // Create Express App and Routes
    const app = express();

    app.use(session(sessionConfig))

    const requestConfig = scenarioConfig.request;

    app.get("/", (req, res) => {
        const { authCodeUrlParameters } = requestConfig;

        const cryptoProvider = new msal.CryptoProvider()

        if (req.query) {
            // Check for the state parameter
            if(req.query.state) authCodeUrlParameters.state = req.query.state;
            // Check for nonce parameter
            /**
             * MSAL Node supports the OIDC nonce feature which is used to protect against token replay.
             * The CryptoProvider class provided by MSAL exposes the createNewGuid() API that generates random GUID
             * used to populate the nonce value if none is provided.
             *
             * The generated nonce is then cached and passed as part of authCodeUrlParameters during authentication request.
             *
             * For more information about nonce,
             * visit https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.5.3.2
             */

            if(req.query.nonce) {
                authCodeUrlParameters.nonce = req.query.nonce
            } else {
                authCodeUrlParameters.nonce = cryptoProvider.createNewGuid()
            }

            // Check for the prompt parameter
            if (req.query.prompt) authCodeUrlParameters.prompt = req.query.prompt;

            // Check for the loginHint parameter
            if (req.query.loginHint) authCodeUrlParameters.loginHint = req.query.loginHint;

            // Check for the domainHint parameter
            if (req.query.domainHint) authCodeUrlParameters.domainHint = req.query.domainHint;
        }

        req.session.nonce = authCodeUrlParameters.nonce //switch to a more persistent storage method.

        /**
         * MSAL Usage
         * The code below demonstrates the correct usage pattern of the ClientApplicaiton.getAuthCodeUrl API.
         *
         * Authorization Code Grant: First Leg
         *
         * In this code block, the application uses MSAL to obtain an authorization code request URL. Once the URL is
         * returned by MSAL, the express application is redirected to said request URL, concluding the first leg of the
         * Authorization Code Grant flow.
         */
        clientApplication.getAuthCodeUrl(authCodeUrlParameters).then((authCodeUrl) => {
            res.redirect(authCodeUrl);
        }).catch((error) => console.log(JSON.stringify(error)));
    });

    app.get("/redirect", (req, res) => {
        const tokenRequest = { ...requestConfig.tokenRequest, code: req.query.code };
        const authCodeResponse = { nonce: req.session.nonce, code: req.query.code }
        /**
         * MSAL Usage
         * The code below demonstrates the correct usage pattern of the ClientApplicaiton.acquireTokenByCode API.
         *
         * Authorization Code Grant: Second Leg
         *
         * In this code block, the application uses MSAL to obtain an Access Token from the configured authentication service.
         * The cached nonce is passed in authCodeResponse object and shall later be validated by MSAL once the Access Token and ID
         * token are returned.
         * The response contains an `accessToken` property. Said property contains a string representing an encoded Json Web Token
         * which can be added to the `Authorization` header in a protected resource request to demonstrate authorization.
         */

        clientApplication.acquireTokenByCode(tokenRequest, authCodeResponse).then((response) => {
            console.log("Successfully acquired token using Authorization Code.");
            res.sendStatus(200);
        }).catch((error) => {
            console.log(error);
            res.status(500).send(error);
        });
    });

    return app.listen(serverPort, () => console.log(`Msal Node Auth Code Sample app listening on port ${serverPort}!`));
}

/**
 * The code below checks if the script is being executed manually or in automation.
 * If the script was executed manually, it will initialize a PublicClientApplication object
 * and execute the sample application.
 */
 if(argv.$0 === "index.js") {
    const loggerOptions = {
        loggerCallback(loglevel, message, containsPii) {
            console.log(message);
        },
            piiLoggingEnabled: false,
        logLevel: msal.LogLevel.Verbose,
    }

    // Build MSAL ClientApplication Configuration object
    const clientConfig = {
        auth: config.authOptions,
        cache: {
            cachePlugin
        },
        // Uncomment the code below to enable the MSAL logger
        /*
         *   system: {
         *    loggerOptions: loggerOptions
         *   }
         */
    };

    // Create an MSAL PublicClientApplication object
    const publicClientApplication = new msal.PublicClientApplication(clientConfig);

    // Execute sample application with the configured MSAL PublicClientApplication
    return getTokenAuthCode(config, publicClientApplication, null);
 }

// The application code is exported so it can be executed in automation environments
 module.exports = getTokenAuthCode;<commit_msg>Added validateState method to ClientApplication<commit_after>/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */

const express = require("express");
const session = require("express-session")
const msal = require('@azure/msal-node');

/**
 * Command line arguments can be used to configure:
 * - The port the application runs on
 * - The cache file location
 * - The authentication scenario/configuration file name
 */
const argv = require("../cliArgs");
const { request } = require("express");

const SERVER_PORT = argv.p || 3000;
const cacheLocation = argv.c || "./data/cache.json";
const cachePlugin = require('../cachePlugin')(cacheLocation);

/**
 * The scenario string is the name of a .json file which contains the MSAL client configuration
 * For an example of what a configuration file should look like, check out the customConfig.json file in the
 * /config directory.
 *
 * You can create your own configuration file and replace the path inside the "config" require statement below
 * with the path to your custom configuraiton.
 */
const scenario = argv.s || "customConfig";
const config = require(`./config/${scenario}.json`);

const sessionConfig = {
    secret: 'ENTER_YOUR_SECRET_HERE',
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: false, // set this to true on production
    }
}

// Sample Application Code
const getTokenAuthCode = function (scenarioConfig, clientApplication, port) {
    // Set the port that the express server will listen on
    const serverPort = port || SERVER_PORT;
    // Create Express App and Routes
    const app = express();

    app.use(session(sessionConfig))

    const requestConfig = scenarioConfig.request;

    app.get("/", (req, res) => {
        const { authCodeUrlParameters } = requestConfig;

        const cryptoProvider = new msal.CryptoProvider()

        if (req.query) {
            // Check for the state parameter
             /**
             * MSAL Node supports the OAuth2.0 state parameter which is used to prevent CSRF attacks.
             * The CryptoProvider class provided by MSAL exposes the createNewGuid() API that generates random GUID
             * used to populate the state value if none is provided.
             *
             * The generated state is then cached and passed as part of authCodeUrlParameters during authentication request.
             *
             * For more information about state,
             * visit https://datatracker.ietf.org/doc/html/rfc6819#section-3.6
             */
            if(req.query.state) {
                authCodeUrlParameters.state = req.query.state;
            } else {
                authCodeUrlParameters.state = cryptoProvider.createNewGuid()
            }
            // Check for nonce parameter
            /**
             * MSAL Node supports the OIDC nonce feature which is used to protect against token replay.
             * The CryptoProvider class provided by MSAL exposes the createNewGuid() API that generates random GUID
             * used to populate the nonce value if none is provided.
             *
             * The generated nonce is then cached and passed as part of authCodeUrlParameters during authentication request.
             *
             * For more information about nonce,
             * visit https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.5.3.2
             */

            if(req.query.nonce) {
                authCodeUrlParameters.nonce = req.query.nonce
            } else {
                authCodeUrlParameters.nonce = cryptoProvider.createNewGuid()
            }

            // Check for the prompt parameter
            if (req.query.prompt) authCodeUrlParameters.prompt = req.query.prompt;

            // Check for the loginHint parameter
            if (req.query.loginHint) authCodeUrlParameters.loginHint = req.query.loginHint;

            // Check for the domainHint parameter
            if (req.query.domainHint) authCodeUrlParameters.domainHint = req.query.domainHint;
        }

        req.session.nonce = authCodeUrlParameters.nonce //switch to a more persistent storage method.
        req.session.state = authCodeUrlParameters.state

        /**
         * MSAL Usage
         * The code below demonstrates the correct usage pattern of the ClientApplicaiton.getAuthCodeUrl API.
         *
         * Authorization Code Grant: First Leg
         *
         * In this code block, the application uses MSAL to obtain an authorization code request URL. Once the URL is
         * returned by MSAL, the express application is redirected to said request URL, concluding the first leg of the
         * Authorization Code Grant flow.
         */
        clientApplication.getAuthCodeUrl(authCodeUrlParameters).then((authCodeUrl) => {
            res.redirect(authCodeUrl);
        }).catch((error) => console.log(JSON.stringify(error)));
    });

    app.get("/redirect", (req, res) => {
        const state = req.query.state
        const cachedState = req.session.state

        /**
         * MSAL Node provides the ClientApplication.validateSate API, which validates the state string received when
         * authorization code is returned.
         *
         */
        clientApplication.validateState(state, cachedState)
        const tokenRequest = { ...requestConfig.tokenRequest, code: req.query.code, state:req.query.state };
        const authCodeResponse = {
            nonce: req.session.nonce,
            code: req.query.code
        }

        /**
         * MSAL Usage
         * The code below demonstrates the correct usage pattern of the ClientApplicaiton.acquireTokenByCode API.
         *
         * Authorization Code Grant: Second Leg
         *
         * In this code block, the application uses MSAL to obtain an Access Token from the configured authentication service.
         * The cached nonce is passed in authCodeResponse object and shall later be validated by MSAL once the Access Token and ID
         * token are returned.
         * The response contains an `accessToken` property. Said property contains a string representing an encoded Json Web Token
         * which can be added to the `Authorization` header in a protected resource request to demonstrate authorization.
         */

        clientApplication.acquireTokenByCode(tokenRequest, authCodeResponse).then((response) => {
            console.log("Successfully acquired token using Authorization Code.");
            res.sendStatus(200);
        }).catch((error) => {
            console.log(error);
            res.status(500).send(error);
        });
    });

    return app.listen(serverPort, () => console.log(`Msal Node Auth Code Sample app listening on port ${serverPort}!`));
}

/**
 * The code below checks if the script is being executed manually or in automation.
 * If the script was executed manually, it will initialize a PublicClientApplication object
 * and execute the sample application.
 */
 if(argv.$0 === "index.js") {
    const loggerOptions = {
        loggerCallback(loglevel, message, containsPii) {
            console.log(message);
        },
            piiLoggingEnabled: false,
        logLevel: msal.LogLevel.Verbose,
    }

    // Build MSAL ClientApplication Configuration object
    const clientConfig = {
        auth: config.authOptions,
        cache: {
            cachePlugin
        },
        // Uncomment the code below to enable the MSAL logger
        /*
         *   system: {
         *    loggerOptions: loggerOptions
         *   }
         */
    };

    // Create an MSAL PublicClientApplication object
    const publicClientApplication = new msal.PublicClientApplication(clientConfig);

    // Execute sample application with the configured MSAL PublicClientApplication
    return getTokenAuthCode(config, publicClientApplication, null);
 }

// The application code is exported so it can be executed in automation environments
 module.exports = getTokenAuthCode;<|endoftext|>


<commit_before>window.urb.seqn_u = 1
window.urb.dely = 0
window.urb.puls = false
window.urb.cabs = {}
if(!window.urb.appl) window.urb.appl = null

window.urb.req = function(method,url,params,json,cb) {
  var xhr = new XMLHttpRequest()
  method = method.toUpperCase()
  if(method == "PUT" || method == "DELETE")
    xhr.open("POST", url+"?"+method)
  else xhr.open(method, url)

  if(json)
    xhr.setRequestHeader("content-type", "text/json")

  if(!window.urb.oryx) throw "No CSRF token" // XX fetch auth.json
  _data = {oryx: window.urb.oryx}
  if(params.xyro) { _data.xyro = params.xyro; }
  if(params.ship) { _data.ship = params.ship; }
  if(params.path) { _data.path = params.path; }
  if(params.appl) { _data.appl = params.appl; }
  if(params.mark) { _data.mark = params.mark; }
  if(params.wire) { _data.wire = params.wire; }
  if(cb) {
    xhr.onload = function() {
      try {
        err = null
        res = {
          status:this.status,
          data: JSON.parse(this.responseText)
        }
        if(res.data.reload)
          res.reload = res.data.reload
      } catch(e) {
        if(urb.wall || true) document.write(this.responseText) // XX
        err = {
          message:"Failed to parse JSON",
          raw:this.responseText
        }
        res = null
      }
      cb(err,res)
    }
    xhr.onerror = function() {
      cb({
        status:this.status,
        data:this.responseText
      })
    }
  }
  xhr.send(JSON.stringify(_data))
}

window.urb.reqq = []
window.urb.qreq = function(method,url,params,json,cb) {
  walk = function() {
    qobj = {}
    qobj.oargs = window.urb.reqq[0]
    qobj.nargs = [].slice.apply(qobj.oargs,[0,4])
    qobj.nargs.push(function(){
      if(this.oargs[4])
        this.oargs[4].apply(window.urb,arguments)
      window.urb.reqq.shift()
      if(window.urb.reqq.length > 0)
        walk()
    }.bind(qobj))
    window.urb.req.apply(this,qobj.nargs)
  }
  l = window.urb.reqq.length
  window.urb.reqq.push(arguments);
  if(l == 0) { walk() }
}

window.urb.gsig = function(params) {
  var path = params.path
  if(!path) path = ""
  if(path[0] !== "/") path = "/"+path
  return  "~"+params.ship+"/"+
          params.appl+
          path.replace(/[^\x00-\x7F]/g, "")
}

window.urb.poll = function(params) {
  if(!params) throw new Error("You must supply params to urb.poll.")

  var url, $this

  seqn = this.seqn_u
  if(params.seqn) seqn = params.seqn()

  url = "/~/of/"+this.ixor+"?poll="+seqn

  this.puls = true

  $this = this
  this.req("get",url,params,true,function(err,res) {
    if(res){
      if(res.data.beat)
        return $this.poll(params)
      switch(res.data.type){
          case "news":
        return document.location.reload()  // XX check autoreload
          case "rush":
          case "mean":
        if(res.data.type) == "mean"
          err = res.data.data
        var fn = $this.gsig(res.data.from)
        if($this.cabs[fn])
          $this.cabs[fn].call(this,err,
            {status: res.status, data: res.data.data.json}) // XX non-json
        break;
          default:
        throw new Error("Lost event %"+res.data.type)
      }
    }

    dely = params.dely ? params.dely : $this.dely

    if(err)
      dely = dely+Math.ceil(dely*.02)
    else {
      $this.dely = 0
      if(params.incs)
        params.incs()
      else
        $this.seqn_u++
    }

    setTimeout(function() {
      $this.poll(params)
    },dely)
  })
}

// if (window.urb.auto) {  // need dependencies
//   var tries = 0
//   var cnt = 0
//   var param = {
//     type:"pol"
//   }
//   window.urb.poll(param)
// }

// window.urb.heartbeat = function() {
//   this.poll({
//     type:"heb",
//     ship:this.ship,
//     dely:30000,
//     seqn:function() {
//       return window.urb.seqn_h
//     },
//     incs:function() {
//       window.urb.seqn_h = window.urb.seqn_h+1
//     }
//   },function() {
//     console.log('heartbeat.')
//   })
// }
// window.urb.heartbeat()

//  //  /  //  /  //  //
// end old %eyre code //
//  //  /  //  /  //  //

window.urb.seqn_s = 0

// TODO urb.send(data, [params/params.appl]?, cb?)
window.urb.send = function(params,cb) {
  if(!params)
    throw new Error("You must supply params to urb.send.")
  if(!params.appl && !this.appl){
    throw new Error("You must specify an appl for urb.send.")
  }

  var url, $this

  params.ship = params.ship ? params.ship : this.ship
  params.appl = params.appl ? params.appl : this.appl
  params.mark = params.mark ? params.mark : "json"
  params.xyro = params.data ? params.data : {}
  params.wire = params.wire ? params.wire : "/"

  url = ["to",params.appl,params.mark]
  url = "/~/"+url.join("/")

  this.seqn_s++

  $this = this
  this.qreq('post',url,params,true,function(err,data) {
    if(err) { $this.seqn_s--; }
    if(cb) { cb.apply(this,arguments); }
  })
}

window.urb.subscribe = function(params,cb) {   //  legacy interface
  if(!params) throw new Error("You must supply params to urb.subscribe")
  return window.urb.bind(params.path, params, cb, cb)
}
window.urb.bind = function(path, cb){ // or bind(path, params, cb, nicecb?)
  var params, nicecb
  if(arguments.length > 2)
    {params = cb; cb = arguments[2], nicecb = arguments[3]}
  else params = {}

  params.path = path
  if(params.path[0] !== "/") params.path = "/"+params.path
  params.ship = params.ship ? params.ship : this.ship
  params.appl = params.appl ? params.appl : this.appl
  params.mark = params.mark ? params.mark : "json"
  params.wire = params.wire ? params.wire : params.path

  if(typeof path != "string")
    throw new Error("You must specify a string path for urb.bind.")
  if(!params.appl) throw new Error("You must specify an appl for urb.bind.")
  if(!cb) throw new Error("You must supply a callback to urb.bind.")

  var method, perm, url, $this

  if(params.mark !== "json")
    throw new Error("Non-json subscriptions unimplemented.")  //  XX
  url = "/~/is/"+this.gsig(params)+"."+params.mark

  params.path = params.wire
  this.cabs[this.gsig(params)] = cb

  $this = this
  this.qreq("put",url,params,true,function(err,res) {
    if(nicecb) { nicecb.apply(this,[err,{status: res.status, data: res.data}])}
    //  XX give raw data
    //
    if(!err && !$this.puls) $this.poll(params)
  })
}

window.urb.unsubscribe = function(params,cb) {
  if(!params) throw new Error("You must supply params to urb.unsubscribe.")

  params.ship = params.ship ? params.ship : this.ship
  params.appl = params.appl ? params.appl : this.appl
  params.wire = params.wire ? params.wire : params.path

  if(!params.path) throw new Error("You must specify a path for urb.unsubscribe.")
  if(!params.appl) throw new Error("You must specify an appl for urb.unsubscribe.")
  if(!cb) throw new Error("You must supply a callback to urb.unsubscribe.")

  url = "/~/is/"+this.gsig(params)+"/"
  method = "delete"
  this.req("delete",url,params,true,function(err,res) {
    cb(err,res)
  })
}

window.urb.util = {
  toDate: function (dat){
    var mils = Math.floor((0x10000 * dat.getUTCMilliseconds()) / 1000).toString(16)
    function pad(num, str){
      return ((new Array(num + 1)).join('0') + str).substr(-num,num)
    }
    return  '~' + dat.getUTCFullYear() +
            '.' + (dat.getUTCMonth() + 1) +
            '.' + dat.getUTCDate() +
           '..' + pad(2, dat.getUTCHours()) +
            '.' + pad(2, dat.getUTCMinutes()) +
            '.' + pad(2, dat.getUTCSeconds()) +
           '..' + pad(4, mils)
  }
}
<commit_msg>fixed<commit_after>window.urb.seqn_u = 1
window.urb.dely = 0
window.urb.puls = false
window.urb.cabs = {}
if(!window.urb.appl) window.urb.appl = null

window.urb.req = function(method,url,params,json,cb) {
  var xhr = new XMLHttpRequest()
  method = method.toUpperCase()
  if(method == "PUT" || method == "DELETE")
    xhr.open("POST", url+"?"+method)
  else xhr.open(method, url)

  if(json)
    xhr.setRequestHeader("content-type", "text/json")

  if(!window.urb.oryx) throw "No CSRF token" // XX fetch auth.json
  _data = {oryx: window.urb.oryx}
  if(params.xyro) { _data.xyro = params.xyro; }
  if(params.ship) { _data.ship = params.ship; }
  if(params.path) { _data.path = params.path; }
  if(params.appl) { _data.appl = params.appl; }
  if(params.mark) { _data.mark = params.mark; }
  if(params.wire) { _data.wire = params.wire; }
  if(cb) {
    xhr.onload = function() {
      try {
        err = null
        res = {
          status:this.status,
          data: JSON.parse(this.responseText)
        }
        if(res.data.reload)
          res.reload = res.data.reload
      } catch(e) {
        if(urb.wall || true) document.write(this.responseText) // XX
        err = {
          message:"Failed to parse JSON",
          raw:this.responseText
        }
        res = null
      }
      cb(err,res)
    }
    xhr.onerror = function() {
      cb({
        status:this.status,
        data:this.responseText
      })
    }
  }
  xhr.send(JSON.stringify(_data))
}

window.urb.reqq = []
window.urb.qreq = function(method,url,params,json,cb) {
  walk = function() {
    qobj = {}
    qobj.oargs = window.urb.reqq[0]
    qobj.nargs = [].slice.apply(qobj.oargs,[0,4])
    qobj.nargs.push(function(){
      if(this.oargs[4])
        this.oargs[4].apply(window.urb,arguments)
      window.urb.reqq.shift()
      if(window.urb.reqq.length > 0)
        walk()
    }.bind(qobj))
    window.urb.req.apply(this,qobj.nargs)
  }
  l = window.urb.reqq.length
  window.urb.reqq.push(arguments);
  if(l == 0) { walk() }
}

window.urb.gsig = function(params) {
  var path = params.path
  if(!path) path = ""
  if(path[0] !== "/") path = "/"+path
  return  "~"+params.ship+"/"+
          params.appl+
          path.replace(/[^\x00-\x7F]/g, "")
}

window.urb.poll = function(params) {
  if(!params) throw new Error("You must supply params to urb.poll.")

  var url, $this

  seqn = this.seqn_u
  if(params.seqn) seqn = params.seqn()

  url = "/~/of/"+this.ixor+"?poll="+seqn

  this.puls = true

  $this = this
  this.req("get",url,params,true,function(err,res) {
    if(res){
      if(res.data.beat)
        return $this.poll(params)
      switch(res.data.type){
          case "news":
        return document.location.reload()  // XX check autoreload
          case "rush":
          case "mean":
        if(res.data.type == "mean")
          err = res.data.data
        var fn = $this.gsig(res.data.from)
        if($this.cabs[fn])
          $this.cabs[fn].call(this,err,
            {status: res.status, data: res.data.data.json}) // XX non-json
        break;
          default:
        throw new Error("Lost event %"+res.data.type)
      }
    }

    dely = params.dely ? params.dely : $this.dely

    if(err)
      dely = dely+Math.ceil(dely*.02)
    else {
      $this.dely = 0
      if(params.incs)
        params.incs()
      else
        $this.seqn_u++
    }

    setTimeout(function() {
      $this.poll(params)
    },dely)
  })
}

// if (window.urb.auto) {  // need dependencies
//   var tries = 0
//   var cnt = 0
//   var param = {
//     type:"pol"
//   }
//   window.urb.poll(param)
// }

// window.urb.heartbeat = function() {
//   this.poll({
//     type:"heb",
//     ship:this.ship,
//     dely:30000,
//     seqn:function() {
//       return window.urb.seqn_h
//     },
//     incs:function() {
//       window.urb.seqn_h = window.urb.seqn_h+1
//     }
//   },function() {
//     console.log('heartbeat.')
//   })
// }
// window.urb.heartbeat()

//  //  /  //  /  //  //
// end old %eyre code //
//  //  /  //  /  //  //

window.urb.seqn_s = 0

// TODO urb.send(data, [params/params.appl]?, cb?)
window.urb.send = function(params,cb) {
  if(!params)
    throw new Error("You must supply params to urb.send.")
  if(!params.appl && !this.appl){
    throw new Error("You must specify an appl for urb.send.")
  }

  var url, $this

  params.ship = params.ship ? params.ship : this.ship
  params.appl = params.appl ? params.appl : this.appl
  params.mark = params.mark ? params.mark : "json"
  params.xyro = params.data ? params.data : {}
  params.wire = params.wire ? params.wire : "/"

  url = ["to",params.appl,params.mark]
  url = "/~/"+url.join("/")

  this.seqn_s++

  $this = this
  this.qreq('post',url,params,true,function(err,data) {
    if(err) { $this.seqn_s--; }
    if(cb) { cb.apply(this,arguments); }
  })
}

window.urb.subscribe = function(params,cb) {   //  legacy interface
  if(!params) throw new Error("You must supply params to urb.subscribe")
  return window.urb.bind(params.path, params, cb, cb)
}
window.urb.bind = function(path, cb){ // or bind(path, params, cb, nicecb?)
  var params, nicecb
  if(arguments.length > 2)
    {params = cb; cb = arguments[2], nicecb = arguments[3]}
  else params = {}

  params.path = path
  if(params.path[0] !== "/") params.path = "/"+params.path
  params.ship = params.ship ? params.ship : this.ship
  params.appl = params.appl ? params.appl : this.appl
  params.mark = params.mark ? params.mark : "json"
  params.wire = params.wire ? params.wire : params.path

  if(typeof path != "string")
    throw new Error("You must specify a string path for urb.bind.")
  if(!params.appl) throw new Error("You must specify an appl for urb.bind.")
  if(!cb) throw new Error("You must supply a callback to urb.bind.")

  var method, perm, url, $this

  if(params.mark !== "json")
    throw new Error("Non-json subscriptions unimplemented.")  //  XX
  url = "/~/is/"+this.gsig(params)+"."+params.mark

  params.path = params.wire
  this.cabs[this.gsig(params)] = cb

  $this = this
  this.qreq("put",url,params,true,function(err,res) {
    if(nicecb) { nicecb.apply(this,[err,{status: res.status, data: res.data}])}
    //  XX give raw data
    //
    if(!err && !$this.puls) $this.poll(params)
  })
}

window.urb.unsubscribe = function(params,cb) {
  if(!params) throw new Error("You must supply params to urb.unsubscribe.")

  params.ship = params.ship ? params.ship : this.ship
  params.appl = params.appl ? params.appl : this.appl
  params.wire = params.wire ? params.wire : params.path

  if(!params.path) throw new Error("You must specify a path for urb.unsubscribe.")
  if(!params.appl) throw new Error("You must specify an appl for urb.unsubscribe.")
  if(!cb) throw new Error("You must supply a callback to urb.unsubscribe.")

  url = "/~/is/"+this.gsig(params)+"/"
  method = "delete"
  this.req("delete",url,params,true,function(err,res) {
    cb(err,res)
  })
}

window.urb.util = {
  toDate: function (dat){
    var mils = Math.floor((0x10000 * dat.getUTCMilliseconds()) / 1000).toString(16)
    function pad(num, str){
      return ((new Array(num + 1)).join('0') + str).substr(-num,num)
    }
    return  '~' + dat.getUTCFullYear() +
            '.' + (dat.getUTCMonth() + 1) +
            '.' + dat.getUTCDate() +
           '..' + pad(2, dat.getUTCHours()) +
            '.' + pad(2, dat.getUTCMinutes()) +
            '.' + pad(2, dat.getUTCSeconds()) +
           '..' + pad(4, mils)
  }
}
<|endoftext|>


<commit_before>/**
 * This is totally rewrite for the old parser module
 * I'll improve and replace them little by little.
 */

const symbols = [
  ':', ';', ',', '(', ')', '[', ']',
  '{', '}', 'π', '±', '+', '-', '*',
  '/', '%', '"', "'", '`', '@',
];

const is = {
  escape: c => c == '\\',
  space:  c => /[\r\n\t\s]/.test(c),
  digit:  c => /^[0-9]$/.test(c),
  sign:   c => /^[+-]$/.test(c),
  dot:    c => c == '.',
  quote:  c => /^["'`]$/.test(c),
  symbol: c => symbols.includes(c),
  hexNum: c => /^[0-9a-f]$/i.test(c),
  hex:           (a, b, c) => a == '0' && is.letter(b, 'x') && is.hexNum(c),
  expWithSign:   (a, b, c) => is.letter(a, 'e') && is.sign(b) && is.digit(c),
  exp:           (a, b) => is.letter(a, 'e') && is.digit(b),
  dots:          (a, b) => is.dot(a) && is.dot(b),
  letter:        (a, b) => String(a).toLowerCase() == String(b).toLowerCase(),
  comment:       (a, b) => a == '/' && b == '*',
  selfClosedTag: (a, b) => a == '/' && b == '>',
  closedTag:     (a, b) => a == '<' && b == '/',
}

class Token {
  constructor({ type, value, pos, status }) {
    this.type = type;
    this.value = value;
    this.pos = pos;
    if (status) {
      this.status = status;
    }
  }
  isSymbol(...values) {
    let isSymbol = this.type == 'Symbol';
    if (!values.length) return isSymbol;
    return values.some(c => c === this.value);
  }
  isSpace() {
    return this.type == 'Space';
  }
  isNumber() {
    return this.type == 'Number';
  }
  isWord() {
    return this.type == 'Word';
  }
}

function iterator(input) {
  let pointer = -1;
  let max = input.length;
  let col = -1, row = 0;
  return {
    curr(n = 0) {
      return input[pointer + n];
    },
    next(n = 1) {
      let next = input[pointer += n];
      if (next == '\n') row++, col = 0;
      else col += n;
      return next;
    },
    end() {
      return pointer >= max;
    },
    get() {
      return {
        prev:  input[pointer - 1],
        curr:  input[pointer + 0],
        next:  input[pointer + 1],
        next2: input[pointer + 2],
        next3: input[pointer + 3],
        pos:   [col, row],
      }
    }
  }
}

function skipComments(iter) {
  while (iter.next()) {
    let { curr, prev } = iter.get();
    if (is.comment(curr, prev)) break;
  }
}

function ignoreSpacingSymbol(value) {
   return [':', ';', ',', '{', '}', '(', ')', '[', ']'].includes(value);
}

function readWord(iter) {
  let temp = '';
  while (!iter.end()) {
    let { curr, next } = iter.get();
    temp += curr;
    let isBreak = is.symbol(next) || is.space(next) || is.digit(next);
    if (temp.length && isBreak) {
      if (!is.closedTag(curr, next)) break;
    }
    iter.next();
  }
  return temp.trim();
}

function readSpaces(iter) {
  let temp = '';
  while (!iter.end()) {
    let { curr, next } = iter.get();
    temp += curr;
    if (!is.space(next)) break;
    iter.next();
  }
  return temp;
}

function readNumber(iter) {
  let temp = '';
  let hasDot = false;
  while (!iter.end()) {
    let { curr, next, next2, next3 } = iter.get();
    temp += curr;
    if (hasDot && is.dot(next)) break;
    if (is.dot(curr)) hasDot = true;
    if (is.dots(next, next2)) break;
    if (is.expWithSign(next, next2, next3)) {
      temp += iter.next() + iter.next();
    }
    else if (is.exp(next, next2)) {
      temp += iter.next();
    }
    else if (!is.digit(next) && !is.dot(next)) {
      break;
    }
    iter.next();
  }
  return temp;
}

function readHexNumber(iter) {
  let temp = '0x';
  iter.next(2);
  while (!iter.end()) {
    let { curr, next } = iter.get();
    temp += curr;
    if (!is.hexNum(next)) break;
    iter.next();
  }
  return temp;
}

function last(array) {
  return array[array.length - 1];
}

function scan(source) {
  let iter = iterator(String(source).trim());
  let tokens = [];
  let quoteStack = [];

  while (iter.next()) {
    let { prev, curr, next, next2, pos } = iter.get();
    if (is.comment(curr, next)) {
      skipComments(iter);
    }
    else if (is.hex(curr, next, next2)) {
      let num = readHexNumber(iter);
      tokens.push(new Token({
        type: 'Number', value: num, pos
      }));
    }
    else if (is.digit(curr) || (
        is.digit(next) && is.dot(curr) && !is.dots(prev, curr))) {
      let num = readNumber(iter);
      tokens.push(new Token({
        type: 'Number', value: num, pos
      }));
    }
    else if (is.symbol(curr) && !is.selfClosedTag(curr, next)) {
      let lastToken = last(tokens);
      // negative
      let isNextDigit = is.digit(next) || (is.dot(next) && is.digit(next2));
      if (curr === '-' && isNextDigit && (!lastToken || !lastToken.isNumber())) {
        let num = readNumber(iter);
        tokens.push(new Token({
          type: 'Number', value: num, pos
        }));
        continue;
      }

      let token = {
        type: 'Symbol', value: curr, pos
      }
      // Escaped symbols
      if (quoteStack.length && is.escape(lastToken.value)) {
        tokens.pop();
        let word = readWord(iter);
        if (word.length) {
          tokens.push(new Token({
            type: 'Word', value: word, pos
          }));
        }
      }
      else {
        if (is.quote(curr)) {
          let lastQuote = last(quoteStack);
          if (lastQuote == curr) {
            quoteStack.pop();
            token.status = 'close';
          } else {
            quoteStack.push(curr);
            token.status = 'open';
          }
        }

        tokens.push(new Token(token));
      }
    }
    else if (is.space(curr)) {
      let spaces = readSpaces(iter);
      let lastToken = last(tokens);
      let { next } = iter.get();

      // Reduce unnecessary spaces
      if (!quoteStack.length && lastToken) {
        if (ignoreSpacingSymbol(lastToken.value) || ignoreSpacingSymbol(next)) {
          continue;
        } else {
          spaces = ' ';
        }
      }
      if (tokens.length && (next && next.trim())) {
        tokens.push(new Token({
          type: 'Space', value: spaces, pos
        }));
      }
    }
    else {
      let word = readWord(iter);
      if (word.length) {
        tokens.push(new Token({
          type: 'Word', value: word, pos
        }));
      }
    }
  }

  // Remove last space token
  let lastToken = last(tokens);
  if (lastToken && lastToken.isSpace()) {
    tokens.length = tokens.length - 1;
  }
  return tokens;
}

export {
  symbols,
  is,
  iterator,
  scan,
  Token
}
<commit_msg>Do not remove spaces around parens<commit_after>/**
 * This is totally rewrite for the old parser module
 * I'll improve and replace them little by little.
 */

const symbols = [
  ':', ';', ',', '(', ')', '[', ']',
  '{', '}', 'π', '±', '+', '-', '*',
  '/', '%', '"', "'", '`', '@',
];

const is = {
  escape: c => c == '\\',
  space:  c => /[\r\n\t\s]/.test(c),
  digit:  c => /^[0-9]$/.test(c),
  sign:   c => /^[+-]$/.test(c),
  dot:    c => c == '.',
  quote:  c => /^["'`]$/.test(c),
  symbol: c => symbols.includes(c),
  hexNum: c => /^[0-9a-f]$/i.test(c),
  hex:           (a, b, c) => a == '0' && is.letter(b, 'x') && is.hexNum(c),
  expWithSign:   (a, b, c) => is.letter(a, 'e') && is.sign(b) && is.digit(c),
  exp:           (a, b) => is.letter(a, 'e') && is.digit(b),
  dots:          (a, b) => is.dot(a) && is.dot(b),
  letter:        (a, b) => String(a).toLowerCase() == String(b).toLowerCase(),
  comment:       (a, b) => a == '/' && b == '*',
  selfClosedTag: (a, b) => a == '/' && b == '>',
  closedTag:     (a, b) => a == '<' && b == '/',
}

class Token {
  constructor({ type, value, pos, status }) {
    this.type = type;
    this.value = value;
    this.pos = pos;
    if (status) {
      this.status = status;
    }
  }
  isSymbol(...values) {
    let isSymbol = this.type == 'Symbol';
    if (!values.length) return isSymbol;
    return values.some(c => c === this.value);
  }
  isSpace() {
    return this.type == 'Space';
  }
  isNumber() {
    return this.type == 'Number';
  }
  isWord() {
    return this.type == 'Word';
  }
}

function iterator(input) {
  let pointer = -1;
  let max = input.length;
  let col = -1, row = 0;
  return {
    curr(n = 0) {
      return input[pointer + n];
    },
    next(n = 1) {
      let next = input[pointer += n];
      if (next == '\n') row++, col = 0;
      else col += n;
      return next;
    },
    end() {
      return pointer >= max;
    },
    get() {
      return {
        prev:  input[pointer - 1],
        curr:  input[pointer + 0],
        next:  input[pointer + 1],
        next2: input[pointer + 2],
        next3: input[pointer + 3],
        pos:   [col, row],
      }
    }
  }
}

function skipComments(iter) {
  while (iter.next()) {
    let { curr, prev } = iter.get();
    if (is.comment(curr, prev)) break;
  }
}

function ignoreSpacingSymbol(value) {
   return [':', ';', ',', '{', '}', '(', ')', '[', ']'].includes(value);
}

function readWord(iter) {
  let temp = '';
  while (!iter.end()) {
    let { curr, next } = iter.get();
    temp += curr;
    let isBreak = is.symbol(next) || is.space(next) || is.digit(next);
    if (temp.length && isBreak) {
      if (!is.closedTag(curr, next)) break;
    }
    iter.next();
  }
  return temp.trim();
}

function readSpaces(iter) {
  let temp = '';
  while (!iter.end()) {
    let { curr, next } = iter.get();
    temp += curr;
    if (!is.space(next)) break;
    iter.next();
  }
  return temp;
}

function readNumber(iter) {
  let temp = '';
  let hasDot = false;
  while (!iter.end()) {
    let { curr, next, next2, next3 } = iter.get();
    temp += curr;
    if (hasDot && is.dot(next)) break;
    if (is.dot(curr)) hasDot = true;
    if (is.dots(next, next2)) break;
    if (is.expWithSign(next, next2, next3)) {
      temp += iter.next() + iter.next();
    }
    else if (is.exp(next, next2)) {
      temp += iter.next();
    }
    else if (!is.digit(next) && !is.dot(next)) {
      break;
    }
    iter.next();
  }
  return temp;
}

function readHexNumber(iter) {
  let temp = '0x';
  iter.next(2);
  while (!iter.end()) {
    let { curr, next } = iter.get();
    temp += curr;
    if (!is.hexNum(next)) break;
    iter.next();
  }
  return temp;
}

function last(array) {
  return array[array.length - 1];
}

function scan(source) {
  let iter = iterator(String(source).trim());
  let tokens = [];
  let quoteStack = [];

  while (iter.next()) {
    let { prev, curr, next, next2, pos } = iter.get();
    if (is.comment(curr, next)) {
      skipComments(iter);
    }
    else if (is.hex(curr, next, next2)) {
      let num = readHexNumber(iter);
      tokens.push(new Token({
        type: 'Number', value: num, pos
      }));
    }
    else if (is.digit(curr) || (
        is.digit(next) && is.dot(curr) && !is.dots(prev, curr))) {
      let num = readNumber(iter);
      tokens.push(new Token({
        type: 'Number', value: num, pos
      }));
    }
    else if (is.symbol(curr) && !is.selfClosedTag(curr, next)) {
      let lastToken = last(tokens);
      // negative
      let isNextDigit = is.digit(next) || (is.dot(next) && is.digit(next2));
      if (curr === '-' && isNextDigit && (!lastToken || !lastToken.isNumber())) {
        let num = readNumber(iter);
        tokens.push(new Token({
          type: 'Number', value: num, pos
        }));
        continue;
      }

      let token = {
        type: 'Symbol', value: curr, pos
      }
      // Escaped symbols
      if (quoteStack.length && is.escape(lastToken.value)) {
        tokens.pop();
        let word = readWord(iter);
        if (word.length) {
          tokens.push(new Token({
            type: 'Word', value: word, pos
          }));
        }
      }
      else {
        if (is.quote(curr)) {
          let lastQuote = last(quoteStack);
          if (lastQuote == curr) {
            quoteStack.pop();
            token.status = 'close';
          } else {
            quoteStack.push(curr);
            token.status = 'open';
          }
        }

        tokens.push(new Token(token));
      }
    }
    else if (is.space(curr)) {
      let spaces = readSpaces(iter);
      let lastToken = last(tokens);
      let { next } = iter.get();
      // Reduce unnecessary spaces
      if (!quoteStack.length && lastToken) {
        let prev = lastToken.value;
        let ignoreLeft = (ignoreSpacingSymbol(prev) && prev !== ')');
        let ignoreRight = (ignoreSpacingSymbol(next) && next !== '(');
        if (ignoreLeft || ignoreRight)  {
          continue;
        } else {
          spaces = ' ';
        }
      }
      if (tokens.length && (next && next.trim())) {
        tokens.push(new Token({
          type: 'Space', value: spaces, pos
        }));
      }
    }
    else {
      let word = readWord(iter);
      if (word.length) {
        tokens.push(new Token({
          type: 'Word', value: word, pos
        }));
      }
    }
  }

  // Remove last space token
  let lastToken = last(tokens);
  if (lastToken && lastToken.isSpace()) {
    tokens.length = tokens.length - 1;
  }
  return tokens;
}

export {
  symbols,
  is,
  iterator,
  scan,
  Token
}
<|endoftext|>


<commit_before>/* @flow */

import React, { Component, PropTypes } from 'react';
import ReactNative from 'react-native';
import shallowEqual from 'shallowequal';
import BannerUnavailable from './BannerUnavailable';
import PageEmpty from './PageEmpty';
import PageLoading from './PageLoading';
import LoadingItem from './LoadingItem';
import RoomItem from './RoomItem';
import ListItem from './ListItem';
import AppText from './AppText';
import Icon from './Icon';
import Colors from '../../Colors';
import type { RoomRel, Room } from '../../../lib/schemaTypes';

const {
	StyleSheet,
	View,
	NavigationActions,
	ListView,
} = ReactNative;

const styles = StyleSheet.create({
	container: {
		flex: 1
	},

	footer: {
		marginTop: 8,
	},

	footerItem: {
		height: 48,
	},

	footerLabel: {
		fontSize: 12,
		lineHeight: 18,
		fontWeight: 'bold',
		color: Colors.fadedBlack,
	},

	footerIcon: {
		color: Colors.fadedBlack,
		marginHorizontal: 16
	},
});

type Props = {
	available?: boolean;
	onNavigation: Function;
	data: Array<{ rel: RoomRel, room: Room } | { type: 'loading' } | { type: 'failed' }>;
}

type State = {
	dataSource: ListView.DataSource
}

export default class Rooms extends Component<void, Props, State> {
	static propTypes = {
		available: PropTypes.bool,
		onNavigation: PropTypes.func.isRequired,
		data: PropTypes.arrayOf(PropTypes.object).isRequired,
	};

	state: State = {
		dataSource: new ListView.DataSource({
			rowHasChanged: (r1, r2) => r1 !== r2
		})
	};

	componentWillMount() {
		this.setState({
			dataSource: this.state.dataSource.cloneWithRows(this.props.data)
		});
	}

	componentWillReceiveProps(nextProps: Props) {
		this.setState({
			dataSource: this.state.dataSource.cloneWithRows(nextProps.data)
		});
	}

	shouldComponentUpdate(nextProps: Props, nextState: State): boolean {
		return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);
	}

	_handleSelectLocality: Function = room => {
		this.props.onNavigation(new NavigationActions.Push({
			name: 'room',
			props: {
				room: room.id
			}
		}));
	};

	_renderRow: Function = result => {
		if (result && result.type === 'loading') {
			return <LoadingItem />;
		}

		return (
			<RoomItem
				key={result.room.id}
				room={result.room}
				onSelect={this._handleSelectLocality}
				showMenuButton
				showBadge
			/>
		);
	};

	_handleManagePlaces: Function = () => {
		this.props.onNavigation(new NavigationActions.Push({
			name: 'places',
		}));
	};

	_handleReportIssue: Function = () => {
		this.props.onNavigation(new NavigationActions.Push({
			name: 'room',
			props: {
				room: 'support'
			}
		}));
	};

	_renderFooter: Function = () => {
		return (
			<View style={styles.footer}>
				<ListItem containerStyle={styles.footerItem} onPress={this._handleManagePlaces}>
					<Icon
						style={styles.footerIcon}
						name='settings'
						size={18}
					/>
					<AppText style={styles.footerLabel}>MANAGE MY PLACES</AppText>
				</ListItem>
				<ListItem containerStyle={styles.footerItem} onPress={this._handleReportIssue}>
					<Icon
						style={styles.footerIcon}
						name='info'
						size={18}
					/>
					<AppText style={styles.footerLabel}>REPORT AN ISSUE</AppText>
				</ListItem>
			</View>
		);
	};

	render() {
		let placeHolder;

		if (this.props.data.length === 1) {
			switch (this.props.data[0] && this.props.data[0].type || null) {
			case 'loading':
				placeHolder = <PageLoading />;
				break;
			case 'failed':
				placeHolder = <PageEmpty label='Failed to load discussions' image='sad' />;
				break;
			}
		}

		return (
			<View style={styles.container}>
				{this.props.available === false ?
					<BannerUnavailable /> :
					null
				}

				{placeHolder ? placeHolder :
					<ListView
						keyboardShouldPersistTaps
						dataSource={this.state.dataSource}
						renderRow={this._renderRow}
						renderFooter={this._renderFooter}
					/>
				}
			</View>
		);
	}
}
<commit_msg>Remove manage places and support options temporarily<commit_after>/* @flow */

import React, { Component, PropTypes } from 'react';
import ReactNative from 'react-native';
import shallowEqual from 'shallowequal';
import BannerUnavailable from './BannerUnavailable';
import PageEmpty from './PageEmpty';
import PageLoading from './PageLoading';
import LoadingItem from './LoadingItem';
import RoomItem from './RoomItem';
import ListItem from './ListItem';
import AppText from './AppText';
import Icon from './Icon';
import Colors from '../../Colors';
import type { RoomRel, Room } from '../../../lib/schemaTypes';

const {
	StyleSheet,
	View,
	NavigationActions,
	ListView,
} = ReactNative;

const styles = StyleSheet.create({
	container: {
		flex: 1
	},

	footer: {
		marginTop: 8,
	},

	footerItem: {
		height: 48,
	},

	footerLabel: {
		fontSize: 12,
		lineHeight: 18,
		fontWeight: 'bold',
		color: Colors.fadedBlack,
	},

	footerIcon: {
		color: Colors.fadedBlack,
		marginHorizontal: 16
	},
});

type Props = {
	available?: boolean;
	onNavigation: Function;
	data: Array<{ rel: RoomRel, room: Room } | { type: 'loading' } | { type: 'failed' }>;
}

type State = {
	dataSource: ListView.DataSource
}

export default class Rooms extends Component<void, Props, State> {
	static propTypes = {
		available: PropTypes.bool,
		onNavigation: PropTypes.func.isRequired,
		data: PropTypes.arrayOf(PropTypes.object).isRequired,
	};

	state: State = {
		dataSource: new ListView.DataSource({
			rowHasChanged: (r1, r2) => r1 !== r2
		})
	};

	componentWillMount() {
		this.setState({
			dataSource: this.state.dataSource.cloneWithRows(this.props.data)
		});
	}

	componentWillReceiveProps(nextProps: Props) {
		this.setState({
			dataSource: this.state.dataSource.cloneWithRows(nextProps.data)
		});
	}

	shouldComponentUpdate(nextProps: Props, nextState: State): boolean {
		return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);
	}

	_handleSelectLocality: Function = room => {
		this.props.onNavigation(new NavigationActions.Push({
			name: 'room',
			props: {
				room: room.id
			}
		}));
	};

	_renderRow: Function = result => {
		if (result && result.type === 'loading') {
			return <LoadingItem />;
		}

		return (
			<RoomItem
				key={result.room.id}
				room={result.room}
				onSelect={this._handleSelectLocality}
				showMenuButton
				showBadge
			/>
		);
	};

	_handleManagePlaces: Function = () => {
		this.props.onNavigation(new NavigationActions.Push({
			name: 'places',
		}));
	};

	_handleReportIssue: Function = () => {
		this.props.onNavigation(new NavigationActions.Push({
			name: 'room',
			props: {
				room: 'support'
			}
		}));
	};

	_renderFooter: Function = () => {
		return (
			<View style={styles.footer}>
				<ListItem containerStyle={styles.footerItem} onPress={this._handleManagePlaces}>
					<Icon
						style={styles.footerIcon}
						name='settings'
						size={18}
					/>
					<AppText style={styles.footerLabel}>MANAGE MY PLACES</AppText>
				</ListItem>
				<ListItem containerStyle={styles.footerItem} onPress={this._handleReportIssue}>
					<Icon
						style={styles.footerIcon}
						name='info'
						size={18}
					/>
					<AppText style={styles.footerLabel}>REPORT AN ISSUE</AppText>
				</ListItem>
			</View>
		);
	};

	render() {
		let placeHolder;

		if (this.props.data.length === 1) {
			switch (this.props.data[0] && this.props.data[0].type || null) {
			case 'loading':
				placeHolder = <PageLoading />;
				break;
			case 'failed':
				placeHolder = <PageEmpty label='Failed to load discussions' image='sad' />;
				break;
			}
		}

		return (
			<View style={styles.container}>
				{this.props.available === false ?
					<BannerUnavailable /> :
					null
				}

				{placeHolder ? placeHolder :
					<ListView
						keyboardShouldPersistTaps
						dataSource={this.state.dataSource}
						renderRow={this._renderRow}
					/>
				}
			</View>
		);
	}
}
<|endoftext|>