<commit_before>// Give a sort spec, which can be in any of these forms:
//   {"key1": 1, "key2": -1}
//   [["key1", "asc"], ["key2", "desc"]]
//   ["key1", ["key2", "desc"]]
//
// (.. with the first form being dependent on the key enumeration
// behavior of your javascript VM, which usually does what you mean in
// this case if the key names don't look like integers ..)
//
// return a function that takes two objects, and returns -1 if the
// first object comes first in order, 1 if the second object comes
// first, or 0 if neither object comes before the other.

Sorter = function (spec) {
  var self = this;

  var sortSpecParts = [];

  if (spec instanceof Array) {
    for (var i = 0; i < spec.length; i++) {
      if (typeof spec[i] === "string") {
        sortSpecParts.push({
          lookup: makeLookupFunction(spec[i]),
          ascending: true
        });
      } else {
        sortSpecParts.push({
          lookup: makeLookupFunction(spec[i][0]),
          ascending: spec[i][1] !== "desc"
        });
      }
    }
  } else if (typeof spec === "object") {
    for (var key in spec) {
      sortSpecParts.push({
        lookup: makeLookupFunction(key),
        ascending: spec[key] >= 0
      });
    }
  } else {
    throw Error("Bad sort specification: ", JSON.stringify(spec));
  }

  // reduceValue takes in all the possible values for the sort key along various
  // branches, and returns the min or max value (according to the bool
  // findMin). Each value can itself be an array, and we look at its values
  // too. (ie, we do a single level of flattening on branchValues, then find the
  // min/max.)
  //
  // XXX This is actually wrong! In fact, the whole attempt to compile sort
  // functions independently of selectors is wrong. In MongoDB, if you have
  // documents {_id: 'x', a: [1, 10]} and {_id: 'y', a: [5, 15]},
  // then C.find({}, {sort: {a: 1}}) puts x before y (1 comes before 5).
  // But C.find({a: {$gt: 3}}, {sort: {a: 1}}) puts y before x (1 does not match
  // the selector, and 5 comes before 10).
  var reduceValue = function (branchValues, findMin) {
    // Expand any leaf arrays that we find, and ignore those arrays themselves.
    branchValues = expandArraysInBranches(branchValues, true);
    var reduced = undefined;
    var first = true;
    // Iterate over all the values found in all the branches, and if a value is
    // an array itself, iterate over the values in the array separately.
    _.each(branchValues, function (branchValue) {
      if (first) {
        reduced = branchValue.value;
        first = false;
      } else {
        // Compare the value we found to the value we found so far, saving it
        // if it's less (for an ascending sort) or more (for a descending
        // sort).
        var cmp = LocalCollection._f._cmp(reduced, branchValue.value);
        if ((findMin && cmp > 0) || (!findMin && cmp < 0))
          reduced = branchValue.value;
      }
    });
    return reduced;
  };

  var comparators = _.map(sortSpecParts, function (specPart) {
    return function (a, b) {
      var aValue = reduceValue(specPart.lookup(a), specPart.ascending);
      var bValue = reduceValue(specPart.lookup(b), specPart.ascending);
      var compare = LocalCollection._f._cmp(aValue, bValue);
      return specPart.ascending ? compare : -compare;
    };
  });

  self._baseComparator = composeComparators(comparators);
};

Sorter.prototype.getComparator = function (options) {
  var self = this;

  // If we have no distances, just use the comparator from the source
  // specification (which defaults to "everything is equal".
  if (!options || !options.distances) {
    return self._baseComparator;
  }

  var distances = options.distances;

  // Return a comparator which first tries the sort specification, and if that
  // says "it's equal", breaks ties using $near distances.
  return composeComparators([self._baseComparator, function (a, b) {
    if (!distances.has(a._id))
      throw Error("Missing distance for " + a._id);
    if (!distances.has(b._id))
      throw Error("Missing distance for " + b._id);
    return distances.get(a._id) - distances.get(b._id);
  }]);
};

MinimongoTest.Sorter = Sorter;

// Given an array of comparators
// (functions (a,b)->(negative or positive or zero)), returns a single
// comparator which uses each comparator in order and returns the first
// non-zero value.
var composeComparators = function (comparatorArray) {
  return function (a, b) {
    for (var i = 0; i < comparatorArray.length; ++i) {
      var compare = comparatorArray[i](a, b);
      if (compare !== 0)
        return compare;
    }
    return 0;
  };
};
<commit_msg>More comments about our sort inconsistency<commit_after>// Give a sort spec, which can be in any of these forms:
//   {"key1": 1, "key2": -1}
//   [["key1", "asc"], ["key2", "desc"]]
//   ["key1", ["key2", "desc"]]
//
// (.. with the first form being dependent on the key enumeration
// behavior of your javascript VM, which usually does what you mean in
// this case if the key names don't look like integers ..)
//
// return a function that takes two objects, and returns -1 if the
// first object comes first in order, 1 if the second object comes
// first, or 0 if neither object comes before the other.

Sorter = function (spec) {
  var self = this;

  var sortSpecParts = [];

  if (spec instanceof Array) {
    for (var i = 0; i < spec.length; i++) {
      if (typeof spec[i] === "string") {
        sortSpecParts.push({
          lookup: makeLookupFunction(spec[i]),
          ascending: true
        });
      } else {
        sortSpecParts.push({
          lookup: makeLookupFunction(spec[i][0]),
          ascending: spec[i][1] !== "desc"
        });
      }
    }
  } else if (typeof spec === "object") {
    for (var key in spec) {
      sortSpecParts.push({
        lookup: makeLookupFunction(key),
        ascending: spec[key] >= 0
      });
    }
  } else {
    throw Error("Bad sort specification: ", JSON.stringify(spec));
  }

  // reduceValue takes in all the possible values for the sort key along various
  // branches, and returns the min or max value (according to the bool
  // findMin). Each value can itself be an array, and we look at its values
  // too. (ie, we do a single level of flattening on branchValues, then find the
  // min/max.)
  //
  // XXX This is actually wrong! In fact, the whole attempt to compile sort
  //     functions independently of selectors is wrong. In MongoDB, if you have
  //     documents {_id: 'x', a: [1, 10]} and {_id: 'y', a: [5, 15]}, then
  //     C.find({}, {sort: {a: 1}}) puts x before y (1 comes before 5).  But
  //     C.find({a: {$gt: 3}}, {sort: {a: 1}}) puts y before x (1 does not match
  //     the selector, and 5 comes before 10).
  //
  //     The way this works is pretty subtle!  For example, if the documents are
  //     instead {_id: 'x', a: [{x: 1}, {x: 10}]}) and
  //             {_id: 'y', a: [{x: 5}, {x: 15}]}),
  //     then C.find({'a.x': {$gt: 3}}, {sort: {'a.x': 1}}) and
  //          C.find({a: {$elemMatch: {x: {$gt: 3}}}}, {sort: {'a.x': 1}})
  //     both follow this rule (y before x).  ie, you do have to apply this
  //     through $elemMatch.
  var reduceValue = function (branchValues, findMin) {
    // Expand any leaf arrays that we find, and ignore those arrays themselves.
    branchValues = expandArraysInBranches(branchValues, true);
    var reduced = undefined;
    var first = true;
    // Iterate over all the values found in all the branches, and if a value is
    // an array itself, iterate over the values in the array separately.
    _.each(branchValues, function (branchValue) {
      if (first) {
        reduced = branchValue.value;
        first = false;
      } else {
        // Compare the value we found to the value we found so far, saving it
        // if it's less (for an ascending sort) or more (for a descending
        // sort).
        var cmp = LocalCollection._f._cmp(reduced, branchValue.value);
        if ((findMin && cmp > 0) || (!findMin && cmp < 0))
          reduced = branchValue.value;
      }
    });
    return reduced;
  };

  var comparators = _.map(sortSpecParts, function (specPart) {
    return function (a, b) {
      var aValue = reduceValue(specPart.lookup(a), specPart.ascending);
      var bValue = reduceValue(specPart.lookup(b), specPart.ascending);
      var compare = LocalCollection._f._cmp(aValue, bValue);
      return specPart.ascending ? compare : -compare;
    };
  });

  self._baseComparator = composeComparators(comparators);
};

Sorter.prototype.getComparator = function (options) {
  var self = this;

  // If we have no distances, just use the comparator from the source
  // specification (which defaults to "everything is equal".
  if (!options || !options.distances) {
    return self._baseComparator;
  }

  var distances = options.distances;

  // Return a comparator which first tries the sort specification, and if that
  // says "it's equal", breaks ties using $near distances.
  return composeComparators([self._baseComparator, function (a, b) {
    if (!distances.has(a._id))
      throw Error("Missing distance for " + a._id);
    if (!distances.has(b._id))
      throw Error("Missing distance for " + b._id);
    return distances.get(a._id) - distances.get(b._id);
  }]);
};

MinimongoTest.Sorter = Sorter;

// Given an array of comparators
// (functions (a,b)->(negative or positive or zero)), returns a single
// comparator which uses each comparator in order and returns the first
// non-zero value.
var composeComparators = function (comparatorArray) {
  return function (a, b) {
    for (var i = 0; i < comparatorArray.length; ++i) {
      var compare = comparatorArray[i](a, b);
      if (compare !== 0)
        return compare;
    }
    return 0;
  };
};
<|endoftext|>


<commit_before>var Future = Npm.require('fibers/future');

DIMSE = {};

var conn = new Connection({
    vr: {
        split: false
    }
});

Meteor.startup(function() {
    var peers = Meteor.settings.dimse;
    console.log('Adding DIMSE peers');
    if (peers && peers.length) {
        peers.forEach(function(peer) {
            conn.addPeer(peer);
        });
    }
});

DIMSE.associate = function(contexts, callback, options) {
    var defaults = {
        contexts: contexts
    };
    options = Object.assign(defaults, options);

    conn.associate(options, function(pdu) {
        // associated
        console.log('==Associated');
        callback.call(this, pdu);
    });
};

DIMSE.retrievePatients = function(params, options) {
    //var start = new Date();
    var future = new Future;
    DIMSE.associate([C.SOP_PATIENT_ROOT_FIND], function(pdu) {
        var defaultParams = {
            0x00100010: '',
            0x00100020: '',
            0x00100030: '',
            0x00100040: '',
            0x00101010: '',
            0x00101040: ''
        };

        var result = this.findPatients(Object.assign(defaultParams, params)),
            o = this;

        var patients = [];
        result.on('result', function(msg) {
            patients.push(msg);
        });

        result.on('end', function() {
            o.release();
        });

        this.on('close', function() {
            //var time = new Date() - start;console.log(time + 'ms taken');
            future.return(patients);
        });
    }, options);
    return future.wait();
};

DIMSE.retrieveStudies = function(params, options) {
    //var start = new Date();
    var future = new Future;
    DIMSE.associate([C.SOP_STUDY_ROOT_FIND], function(pdu) {
        var defaultParams = {
            0x0020000D: '',
            0x00080060: '',
            0x00080005: '',
            0x00080020: '',
            0x00080030: '',
            0x00080090: '',
            0x00100010: '',
            0x00100020: '',
            0x00200010: '',
            0x00100030: ''
        };

        var result = this.findStudies(Object.assign(defaultParams, params)),
            o = this;

        var studies = [];
        result.on('result', function(msg) {
            studies.push(msg);
        });

        result.on('end', function() {
            o.release();
        });

        this.on('close', function() {
            //var time = new Date() - start;console.log(time + 'ms taken');
            future.return(studies);
        });
    }, options);
    return future.wait();
};

DIMSE.retrieveSeries = function(studyInstanceUID, params, options) {
    var future = new Future;
    DIMSE.associate([C.SOP_STUDY_ROOT_FIND], function(pdu) {
        var defaultParams = {
            0x0020000D: studyInstanceUID ? studyInstanceUID : '',
            0x00080005: '',
            0x00080020: '',
            0x00080030: '',
            0x00080090: '',
            0x00100010: '',
            0x00100020: '',
            0x00200010: '',
            0x0008103E: '',
            0x0020000E: '',
            0x00200011: ''
        };

        var result = this.findSeries(Object.assign(defaultParams, params)),
            o = this;

        var series = [];
        result.on('result', function(msg) {
            series.push(msg);
        });

        result.on('end', function() {
            o.release();
        });

        this.on('close', function() {
            future.return(series);
        });
    }, options);
    return future.wait();
};

DIMSE.retrieveInstances = function(studyInstanceUID, seriesInstanceUID, params, options) {
    var future = new Future;
    DIMSE.associate([C.SOP_STUDY_ROOT_FIND], function(pdu) {
        var defaultParams = {
            0x0020000D: studyInstanceUID ? studyInstanceUID : '',
            0x0020000E: (studyInstanceUID && seriesInstanceUID) ? seriesInstanceUID : '',
            0x00080005: '',
            0x00080020: '',
            0x00080030: '',
            0x00080090: '',
            0x00100010: '',
            0x00100020: '',
            0x00200010: '',
            0x0008103E: '',
            0x00200011: '',
            0x00080016: '', // sopClassUid
            0x00080018: '', // sopInstanceUid
            0x00200013: '', // instanceNumber
            0x00280010: '', // rows
            0x00280011: '', // columns
            0x00280100: '', // bitsAllocated
            0x00280101: '', // bitsStored
            0x00280102: '', // highBit
            0x00280103: '', // pixelRepresentation
            0x00280004: '', // photometricInterpretation
            0x00280008: '', // numFrames
            0x00181063: '', // frameTime
            0x00281052: '', // rescaleIntercept
            0x00281053: '', // rescaleSlope
            0x00280002: '', // samplesPerPixel
            0x00201041: '', // sliceLocation
            0x00281050: '', // windowCenter
            0x00281051: '', // windowWidth
            0x00280030: '', // pixelSpacing
            0x00200062: '', // laterality
            0x00185101: '', // viewPosition
            0x00080008: '', // imageType
            0x00200032: '', // imagePositionPatient
            0x00200037: '', // imageOrientationPatient
            0x00200052: '', // frameOfReferenceUID

            // Orthanc has a bug here so we can't retrieve sequences at the moment
            // https://groups.google.com/forum/#!topic/orthanc-users/ghKJfvtnK8Y
            //0x00082112: ''  // sourceImageSequence
        };
        var result = this.findInstances(Object.assign(defaultParams, params)),
            o = this;

        var instances = [];
        result.on('result', function(msg) {
            instances.push(msg);
        });

        result.on('end', function() {
            o.release();
        });

        this.on('close', function() {
            future.return(instances);
        });
    }, options);
    return future.wait();
};

DIMSE.storeInstances = function(fileList, callback) {
    var handle = conn.storeInstances(fileList);
    handle.on('file', function(err, file) {
        callback(err, file);
    });
};

DIMSE.moveInstances = function(studyInstanceUID, seriesInstanceUID, sopInstanceUID, sopClassUID, params) {
    DIMSE.associate([C.SOP_STUDY_ROOT_MOVE, sopClassUID], function() {
        var defaultParams = {
            0x0020000D: studyInstanceUID ? studyInstanceUID : '',
            0x0020000E: seriesInstanceUID ? seriesInstanceUID : '',
            0x00080018: sopInstanceUID ? sopInstanceUID : ''
        };
        this.moveInstances('OHIFDCM', Object.assign(defaultParams, params));
    });
};
<commit_msg>image level query with combined series query<commit_after>var Future = Npm.require('fibers/future');

DIMSE = {};

var conn = new Connection({
    vr: {
        split: false
    }
});

var getInstanceRetrievalParams = function(studyInstanceUID, seriesInstanceUID) {
    return {
        0x0020000D: studyInstanceUID ? studyInstanceUID : '',
        0x0020000E: (studyInstanceUID && seriesInstanceUID) ? seriesInstanceUID : '',
        0x00080005: '',
        0x00080020: '',
        0x00080030: '',
        0x00080090: '',
        0x00100010: '',
        0x00100020: '',
        0x00200010: '',
        0x0008103E: '',
        0x00200011: '',
        0x00080016: '', // sopClassUid
        0x00080018: '', // sopInstanceUid
        0x00200013: '', // instanceNumber
        0x00280010: '', // rows
        0x00280011: '', // columns
        0x00280100: '', // bitsAllocated
        0x00280101: '', // bitsStored
        0x00280102: '', // highBit
        0x00280103: '', // pixelRepresentation
        0x00280004: '', // photometricInterpretation
        0x00280008: '', // numFrames
        0x00181063: '', // frameTime
        0x00281052: '', // rescaleIntercept
        0x00281053: '', // rescaleSlope
        0x00280002: '', // samplesPerPixel
        0x00201041: '', // sliceLocation
        0x00281050: '', // windowCenter
        0x00281051: '', // windowWidth
        0x00280030: '', // pixelSpacing
        0x00200062: '', // laterality
        0x00185101: '', // viewPosition
        0x00080008: '', // imageType
        0x00200032: '', // imagePositionPatient
        0x00200037: '', // imageOrientationPatient
        0x00200052: '', // frameOfReferenceUID

        // Orthanc has a bug here so we can't retrieve sequences at the moment
        // https://groups.google.com/forum/#!topic/orthanc-users/ghKJfvtnK8Y
        //0x00082112: ''  // sourceImageSequence
    }
};

Meteor.startup(function() {
    var peers = Meteor.settings.dimse;
    console.log('Adding DIMSE peers');
    if (peers && peers.length) {
        peers.forEach(function(peer) {
            conn.addPeer(peer);
        });
    }
});

DIMSE.associate = function(contexts, callback, options) {
    var defaults = {
        contexts: contexts
    };
    options = Object.assign(defaults, options);

    conn.associate(options, function(pdu) {
        // associated
        console.log('==Associated');
        callback.call(this, pdu);
    });
};

DIMSE.retrievePatients = function(params, options) {
    //var start = new Date();
    var future = new Future;
    DIMSE.associate([C.SOP_PATIENT_ROOT_FIND], function(pdu) {
        var defaultParams = {
            0x00100010: '',
            0x00100020: '',
            0x00100030: '',
            0x00100040: '',
            0x00101010: '',
            0x00101040: ''
        };

        var result = this.findPatients(Object.assign(defaultParams, params)),
            o = this;

        var patients = [];
        result.on('result', function(msg) {
            patients.push(msg);
        });

        result.on('end', function() {
            o.release();
        });

        this.on('close', function() {
            //var time = new Date() - start;console.log(time + 'ms taken');
            future.return(patients);
        });
    }, options);
    return future.wait();
};

DIMSE.retrieveStudies = function(params, options) {
    //var start = new Date();
    var future = new Future;
    DIMSE.associate([C.SOP_STUDY_ROOT_FIND], function(pdu) {
        var defaultParams = {
            0x0020000D: '',
            0x00080060: '',
            0x00080005: '',
            0x00080020: '',
            0x00080030: '',
            0x00080090: '',
            0x00100010: '',
            0x00100020: '',
            0x00200010: '',
            0x00100030: ''
        };

        var result = this.findStudies(Object.assign(defaultParams, params)),
            o = this;

        var studies = [];
        result.on('result', function(msg) {
            studies.push(msg);
        });

        result.on('end', function() {
            o.release();
        });

        this.on('close', function() {
            //var time = new Date() - start;console.log(time + 'ms taken');
            future.return(studies);
        });
    }, options);
    return future.wait();
};

DIMSE._retrieveInstancesBySeries = function(conn, series, studyInstanceUID, callback, params) {
    var aSeries = series.shift(), seriesInstanceUID = aSeries.getValue(0x0020000E),
        defaultParams = getInstanceRetrievalParams(studyInstanceUID, seriesInstanceUID);

    var result = conn.findInstances(Object.assign(defaultParams, params)),
        instances = [];

    result.on('result', function(msg) {
        instances.push(msg);
    });
    result.on('end', function() {
        if (series.length > 0) {
            callback(instances, false);
            DIMSE._retrieveInstancesBySeries(conn, series, studyInstanceUID, callback, params);
        } else {
            callback(instances, true);
        }
    });
};

DIMSE.retrieveInstancesByStudyOnlyMulti = function(studyInstanceUID, params, options) {
    if (!studyInstanceUID) return [];

    var series = DIMSE.retrieveSeries(studyInstanceUID, params, options), instances = [];
    series.forEach(function(seriesData){
        var seriesInstanceUID = seriesData.getValue(0x0020000E);

        var relatedInstances = DIMSE.retrieveInstances(studyInstanceUID, seriesInstanceUID, params, options);
        instances = instances.concat(relatedInstances);
    });
    return instances;
};

DIMSE.retrieveInstancesByStudyOnly = function(studyInstanceUID, params, options) {
    if (!studyInstanceUID) return [];

    var future = new Future;
    DIMSE.associate([C.SOP_STUDY_ROOT_FIND], function(pdu){
        var defaultParams = {
            0x0020000D: studyInstanceUID,
            0x00080005: '',
            0x00080020: '',
            0x00080030: '',
            0x00080090: '',
            0x00100010: '',
            0x00100020: '',
            0x00200010: '',
            0x0008103E: '',
            0x0020000E: '',
            0x00200011: ''
        };
        var result = this.findSeries(Object.assign(defaultParams, params)),
            series = [], conn = this, allInstances = [];

        result.on('result', function(msg) {
            series.push(msg);
        });
        result.on('end', function(){
            if (series.length > 0) {
                DIMSE._retrieveInstancesBySeries(conn, series, studyInstanceUID, function(relatedInstances, isEnd){
                    allInstances = allInstances.concat(relatedInstances);
                    if (isEnd) {
                        conn.release();
                    }
                });
            } else {
                conn.release();
            }
        });
        conn.on('close', function() {
            future.return(allInstances);
        });
    });
    return future.wait();
}

DIMSE.retrieveSeries = function(studyInstanceUID, params, options) {
    var future = new Future;
    DIMSE.associate([C.SOP_STUDY_ROOT_FIND], function(pdu) {
        var defaultParams = {
            0x0020000D: studyInstanceUID ? studyInstanceUID : '',
            0x00080005: '',
            0x00080020: '',
            0x00080030: '',
            0x00080090: '',
            0x00100010: '',
            0x00100020: '',
            0x00200010: '',
            0x0008103E: '',
            0x0020000E: '',
            0x00200011: ''
        };

        var result = this.findSeries(Object.assign(defaultParams, params)),
            o = this;

        var series = [];
        result.on('result', function(msg) {
            series.push(msg);
        });

        result.on('end', function() {
            o.release();
        });

        this.on('close', function() {
            future.return(series);
        });
    }, options);
    return future.wait();
};

DIMSE.retrieveInstances = function(studyInstanceUID, seriesInstanceUID, params, options) {
    var future = new Future;
    DIMSE.associate([C.SOP_STUDY_ROOT_FIND], function(pdu) {
        var defaultParams = getInstanceRetrievalParams(studyInstanceUID, seriesInstanceUID);

        var result = this.findInstances(Object.assign(defaultParams, params)),
            o = this;

        var instances = [];
        result.on('result', function(msg) {
            instances.push(msg);
        });

        result.on('end', function() {
            o.release();
        });

        this.on('close', function() {
            future.return(instances);
        });
    }, options);
    return future.wait();
};

DIMSE.storeInstances = function(fileList, callback) {
    var handle = conn.storeInstances(fileList);
    handle.on('file', function(err, file) {
        callback(err, file);
    });
};

DIMSE.moveInstances = function(studyInstanceUID, seriesInstanceUID, sopInstanceUID, sopClassUID, params) {
    DIMSE.associate([C.SOP_STUDY_ROOT_MOVE, sopClassUID], function() {
        var defaultParams = {
            0x0020000D: studyInstanceUID ? studyInstanceUID : '',
            0x0020000E: seriesInstanceUID ? seriesInstanceUID : '',
            0x00080018: sopInstanceUID ? sopInstanceUID : ''
        };
        this.moveInstances('OHIFDCM', Object.assign(defaultParams, params));
    });
};
<|endoftext|>


<commit_before>import { Promise } from 'bluebird'
import { Spinner } from './lib/console'
import fs from 'fs'
import webpack from 'webpack'
import _ from 'lodash'
import bridge from './bridge'
import cache from './cache'
import handleError from './lib/handleError'
import findExports from './lib/findExports'
import exec from './lib/exec'
import { readConfig, writeConfig } from './lib/config'
import log from './lib/log'
import { touch, p, mkdir, rmdir, readFile, writeFile, writeJSON, readJSON } from './lib/fns'

let WHERE = {}
let OPTS
let INSTALLING = false

/*

  Public:
   - init: set options
   - install: checks installed and reinstalls based on current cache
   - scanFile: checks for imports in file and installs/caches

  Private:
   - bundleExternals: write cache/installed + pack
     - pack: deps.js => packages.js (bundleExternals)
   - setInstalled: cache => package.json.installed
   - writeDeps: deps => deps.js

*/

async function init(_opts) {
  OPTS = _opts

  WHERE.outDir = p(OPTS.internalDir, 'deps')
  WHERE.internalsInJS = p(WHERE.outDir, 'internals.in.js')
  WHERE.internalsOutJS = p(WHERE.outDir, 'internals.js')
  WHERE.depsJS = p(WHERE.outDir, 'deps.js')
  WHERE.depsJSON = p(WHERE.outDir, 'deps.json')
  WHERE.packagesJS = p(WHERE.outDir, 'packages.js')

  await remakeInstallDir()
}

// messaging

const onPackageStart = (name) => {
  if (OPTS.build) return
  bridge.message('package:install', { name })
}

const onPackageError = (name, error) => {
  if (OPTS.build) return
  bridge.message('package:error', { name, error })
  bridge.message('npm:error', { error })
}

const onPackageFinish = (name) => {
  if (OPTS.build) return
  log('runner: onPackageFinish: ', name)
  bridge.message('package:installed', { name })
}

const onPackagesInstalled = () => {
  if (OPTS.build) return
  bridge.message('packages:reload', {})
}

// readers / writers

const externals = [ 'flint-js', 'react', 'react-dom', 'bluebird' ]
const rmFlintExternals = ls => ls.filter(i => externals.indexOf(i) < 0)
const installKey = 'installed'

async function readInstalled() {
  try {
    const config = await readConfig()
    const installed = config[installKey] || []
    log('readInstalled()', installed)
    return installed
  }
  catch(e) {
    handleError(e)
  }
}

async function writeInstalled(deps) {
  try {
    log('writeInstalled()', deps)
    const config = await readConfig()
    config[installKey] = rmFlintExternals(deps)
    await writeConfig(config)
  }
  catch(e) {
    handleError(e)
  }
}

async function getWritten() {
  try {
    const written = await readJSON(WHERE.depsJSON)
    return written.deps
  }
  catch(e) {
    log('npm: install: no deps installed')
    return []
  }
}

// doers

const filterFalse = ls => ls.filter(l => !!l)

async function removeOld(rebundle) {
  const installed = await readInstalled()
  const toUninstall = _.difference(installed, cache.getImports())
  log('npm: removeOld() toUninstall', toUninstall)

  if (!toUninstall.length) return

  console.log(`\n  Uninstalling...`.bold)
  const attempted = await* toUninstall.map(async dep => {
    try {
      await unsave(dep, toUninstall.indexOf(dep), toUninstall.length)
      console.log(`  ✘ ${dep}`.red)
      return dep
    }
    catch(e) {
      console.log('Failed to uninstall', dep)
      return false
    }
  })

  const success = filterFalse(attempted)
  const final = _.difference(installed, success)
  log('writing from removeOld()', final)
  await writeInstalled(final)

  if (rebundle) {
    await bundleExternals()
  }

  return success
}

async function remakeInstallDir(redo) {
  if (redo)
    await rmdir(WHERE.depsJSON)

  await mkdir(WHERE.outDir)
  await* [
    touch(WHERE.depsJSON),
    touch(WHERE.depsJS),
    touch(WHERE.packagesJS),
    touch(WHERE.internalsOutJS)
  ]
}

function promiseTimeout(delay) {
  return new Promise((res, rej) => {
    setTimeout(res, delay)
  })
}

// ensures all packages installed, uninstalled, written out to bundle
async function install(force) {
  log('npm: install')
  try {
    await remakeInstallDir(force)
    await removeOld()
    await installAll()
    await bundleExternals()
  } catch(e) {
    handleError(e)
    throw new Error(e)
  }
}

// => deps.json
// => deps.js
const depRequireString = (name, onto, pathname = '') => `
  try {
    Flint.${onto}["${name}"] = require("${pathname}${name}")
  }
  catch(e) {
    console.log('Error running package!')
    console.error(e)
  };
`

// package.json.installed => deps.js
async function writeDeps(deps = []) {
  log('npm: writeDeps:', deps)
  await writeJSON(WHERE.depsJSON, { deps })
  const requireString = deps.map(name => {
    return depRequireString(name, 'packages')
  }).join('')
  await writeFile(WHERE.depsJS, requireString)
}

// allInstalled() => packExternals()
async function bundleExternals() {
  log('npm: bundleExternals')
  const installed = await readInstalled()
  await writeDeps(installed)
  await packExternals()
  onPackagesInstalled()
}

const findRequires = source =>
  getMatches(source, /require\(\s*['"]([^\'\"]+)['"]\s*\)/g, 1) || []

// <= file, source
//  > install new deps
// => update cache
function scanFile(file, source) {
  log('scanFile', file)
  try {
    // install new stuff
    checkInternals(file, source)
    installExternals(file, source)
  }
  catch (e) {
    console.log('Error installing imports!')
    console.log(e)
    console.log(e.message)
  }
}

// TODO: check this in babel to be more accurate
// we bundle any internal file that uses:
//    exports.xyz, exports['default']
async function checkInternals(file, source) {
  log('checkInternals', file)

  const isExporting = findExports(source)
  const alreadyExported = cache.isExported(file)
  log('checkInternals: found', isExporting, 'already', alreadyExported)

  cache.setExported(file, isExporting)

  // needs to rewrite internalsIn.js?
  if (!alreadyExported && isExporting || alreadyExported && !isExporting) {
    await writeInternalsIn()
  }

  if (isExporting)
    bundleInternals()
}

async function writeInternalsIn() {
  log('writeInternalsIn')
  const files = cache.getExported()
  if (!files.length) return

  const requireString = files.map(f =>
    depRequireString(f.replace(/\.js$/, ''), 'internals', './internal/')).join('')

  await writeFile(WHERE.internalsInJS, requireString)
}

export async function bundleInternals() {
  await packInternals()
  bridge.message('internals:reload', {})
}

function packInternals() {
  log('packInternals')
  return new Promise((res, rej) => {
    webpack({
      entry: WHERE.internalsInJS,
      externals: {
        react: 'React',
        bluebird: '_bluebird',
        'react-dom': 'ReactDOM'
      },
      output: {
        filename: WHERE.internalsOutJS
      }
    }, async err => {
      if (err) {
        console.error(err.stack)
        return rej(err)
      }

      log('npm: pack: finished')
      res()
    })
  })
}

const findExternalRequires = source =>
  findRequires(source).filter(x => x.charAt(0) != '.')

async function installExternals(file, source) {
  log('installExternals', file)

  const found = findExternalRequires(source)
  cache.setFileImports(file, found)
  installAll(found)
}

let installing = []
let isInstalling = false

async function installAll(deps) {
  if (!deps)
    deps = cache.getImports()

  const installed = await readInstalled()
  const fresh = _.difference(deps, installed)

  // no new ones found
  if (!fresh.length) return

  // push installing
  installing = installing.concat(fresh)

  // check if installed running
  if (isInstalling) return
  isInstalling = true

  let successful = []

  const installNext = async () => {
    const dep = installing.shift()
    log('start install:', dep)
    onPackageStart(dep)

    try {
      await save(dep)
      successful.push(dep)
      onPackageFinish(dep)
      next()
    } catch(e) {
      log('package install failed', dep)
      onPackageError(dep, e)
      next()
    }
  }

  const next = () => installing.length ? installNext() : done()

  const done = async () => {
    // cache newly successful + installed
    const total = successful.concat(installed)
    await writeInstalled(total)
    logInstalled(successful)
    log('npm: installExternals() -> bundleExternals()')
    await bundleExternals()
    isInstalling = false
    return true
  }

  installNext()
}

function logProgress(tag, name, index, total) {
  log('npm', tag, name)

  const out = total ?
    ` ${index+1} of ${total}: ${name}` :
    `${tag}: ${name}`

  if (OPTS.build)
    console.log(out)
  else {
    console.log()
    let spinner = new Spinner(out)
    spinner.start({ fps: 30 })
    return spinner
  }
}

function execPromise(name, cmd, dir, spinner) {
  return new Promise((res, rej) => {
    exec(cmd, dir, (err, stdout, stderr) => {
      if (spinner) spinner.stop()
      if (err) rej(err)
      else res(name)
    })
  })
}

async function progressTask(label, cmd, name, index, total) {
  try {
    const spinner = logProgress(label, name, index, total)
    await execPromise(name, cmd, OPTS.flintDir, spinner)
  }
  catch(e) {
    handleError(e)
  }
}

// npm install --save 'name'
async function save(name, index, total) {
  await progressTask('Installing', 'npm install --save ' + name, name, index, total)
}

// npm uninstall --save 'name'
async function unsave(name, index, total) {
  await progressTask('Uninstalling', 'npm uninstall --save ' + name, name, index, total)
}

// webpack
// deps.js => packages.js
async function packExternals(file, out) {
  log('npm: pack')
  return new Promise((resolve, reject) => {
    webpack({
      entry: WHERE.depsJS,
      externals: {
        react: 'React',
        bluebird: '_bluebird',
        'react-dom': 'ReactDOM'
      },
      output: {
        filename: WHERE.packagesJS
      }
    }, async err => {
      if (err) {
        // undo written packages
        await rmdir(WHERE.depsJSON)
        console.log("Error bundling your packages:", err)
        return reject(err)
      }

      log('npm: pack: finished')
      resolve()
    })
  })
}

function getMatches(string, regex, index) {
  index || (index = 1) // default to the first capturing group
  var matches = []
  var match
  while (match = regex.exec(string)) {
    matches.push(match[index])
  }
  return matches
}

function logInstalled(deps) {
  if (!deps.length) return
  console.log()
  console.log(`  Installed ${deps.length} packages`.bold)
  deps.forEach(dep => {
    console.log(`  ✓ ${dep}`.green)
  })
  console.log()
}

export default { init, install, scanFile, bundleInternals, removeOld }<commit_msg>filter bluebird<commit_after>import { Promise } from 'bluebird'
import { Spinner } from './lib/console'
import fs from 'fs'
import webpack from 'webpack'
import _ from 'lodash'
import bridge from './bridge'
import cache from './cache'
import handleError from './lib/handleError'
import findExports from './lib/findExports'
import exec from './lib/exec'
import { readConfig, writeConfig } from './lib/config'
import log from './lib/log'
import { touch, p, mkdir, rmdir, readFile, writeFile, writeJSON, readJSON } from './lib/fns'

let WHERE = {}
let OPTS
let INSTALLING = false

/*

  Public:
   - init: set options
   - install: checks installed and reinstalls based on current cache
   - scanFile: checks for imports in file and installs/caches

  Private:
   - bundleExternals: write cache/installed + pack
     - pack: deps.js => packages.js (bundleExternals)
   - setInstalled: cache => package.json.installed
   - writeDeps: deps => deps.js

*/

async function init(_opts) {
  OPTS = _opts

  WHERE.outDir = p(OPTS.internalDir, 'deps')
  WHERE.internalsInJS = p(WHERE.outDir, 'internals.in.js')
  WHERE.internalsOutJS = p(WHERE.outDir, 'internals.js')
  WHERE.depsJS = p(WHERE.outDir, 'deps.js')
  WHERE.depsJSON = p(WHERE.outDir, 'deps.json')
  WHERE.packagesJS = p(WHERE.outDir, 'packages.js')

  await remakeInstallDir()
}

// messaging

const onPackageStart = (name) => {
  if (OPTS.build) return
  bridge.message('package:install', { name })
}

const onPackageError = (name, error) => {
  if (OPTS.build) return
  bridge.message('package:error', { name, error })
  bridge.message('npm:error', { error })
}

const onPackageFinish = (name) => {
  if (OPTS.build) return
  log('runner: onPackageFinish: ', name)
  bridge.message('package:installed', { name })
}

const onPackagesInstalled = () => {
  if (OPTS.build) return
  bridge.message('packages:reload', {})
}

// readers / writers

const externals = [ 'flint-js', 'react', 'react-dom', 'bluebird' ]
const rmFlintExternals = ls => ls.filter(i => externals.indexOf(i) < 0)
const installKey = 'installed'

async function readInstalled() {
  try {
    const config = await readConfig()
    const installed = config[installKey] || []
    log('readInstalled()', installed)
    return installed
  }
  catch(e) {
    handleError(e)
  }
}

async function writeInstalled(deps) {
  try {
    log('writeInstalled()', deps)
    const config = await readConfig()
    config[installKey] = rmFlintExternals(deps)
    await writeConfig(config)
  }
  catch(e) {
    handleError(e)
  }
}

async function getWritten() {
  try {
    const written = await readJSON(WHERE.depsJSON)
    return written.deps
  }
  catch(e) {
    log('npm: install: no deps installed')
    return []
  }
}

// doers

const filterFalse = ls => ls.filter(l => !!l)

async function removeOld(rebundle) {
  const installed = await readInstalled()
  const toUninstall = _.difference(installed, cache.getImports())
  log('npm: removeOld() toUninstall', toUninstall)

  if (!toUninstall.length) return

  console.log(`\n  Uninstalling...`.bold)
  const attempted = await* toUninstall.map(async dep => {
    try {
      await unsave(dep, toUninstall.indexOf(dep), toUninstall.length)
      console.log(`  ✘ ${dep}`.red)
      return dep
    }
    catch(e) {
      console.log('Failed to uninstall', dep)
      return false
    }
  })

  const success = filterFalse(attempted)
  const final = _.difference(installed, success)
  log('writing from removeOld()', final)
  await writeInstalled(final)

  if (rebundle) {
    await bundleExternals()
  }

  return success
}

async function remakeInstallDir(redo) {
  if (redo)
    await rmdir(WHERE.depsJSON)

  await mkdir(WHERE.outDir)
  await* [
    touch(WHERE.depsJSON),
    touch(WHERE.depsJS),
    touch(WHERE.packagesJS),
    touch(WHERE.internalsOutJS)
  ]
}

function promiseTimeout(delay) {
  return new Promise((res, rej) => {
    setTimeout(res, delay)
  })
}

// ensures all packages installed, uninstalled, written out to bundle
async function install(force) {
  log('npm: install')
  try {
    await remakeInstallDir(force)
    await removeOld()
    await installAll()
    await bundleExternals()
  } catch(e) {
    handleError(e)
    throw new Error(e)
  }
}

// => deps.json
// => deps.js
const depRequireString = (name, onto, pathname = '') => `
  try {
    Flint.${onto}["${name}"] = require("${pathname}${name}")
  }
  catch(e) {
    console.log('Error running package!')
    console.error(e)
  };
`

// package.json.installed => deps.js
async function writeDeps(deps = []) {
  log('npm: writeDeps:', deps)
  await writeJSON(WHERE.depsJSON, { deps })
  const requireString = deps.map(name => {
    return depRequireString(name, 'packages')
  }).join('')
  await writeFile(WHERE.depsJS, requireString)
}

// allInstalled() => packExternals()
async function bundleExternals() {
  log('npm: bundleExternals')
  const installed = await readInstalled()
  await writeDeps(installed)
  await packExternals()
  onPackagesInstalled()
}

const findRequires = source =>
  getMatches(source, /require\(\s*['"]([^\'\"]+)['"]\s*\)/g, 1) || []

// <= file, source
//  > install new deps
// => update cache
function scanFile(file, source) {
  log('scanFile', file)
  try {
    // install new stuff
    checkInternals(file, source)
    installExternals(file, source)
  }
  catch (e) {
    console.log('Error installing imports!')
    console.log(e)
    console.log(e.message)
  }
}

// TODO: check this in babel to be more accurate
// we bundle any internal file that uses:
//    exports.xyz, exports['default']
async function checkInternals(file, source) {
  log('checkInternals', file)

  const isExporting = findExports(source)
  const alreadyExported = cache.isExported(file)
  log('checkInternals: found', isExporting, 'already', alreadyExported)

  cache.setExported(file, isExporting)

  // needs to rewrite internalsIn.js?
  if (!alreadyExported && isExporting || alreadyExported && !isExporting) {
    await writeInternalsIn()
  }

  if (isExporting)
    bundleInternals()
}

async function writeInternalsIn() {
  log('writeInternalsIn')
  const files = cache.getExported()
  if (!files.length) return

  const requireString = files.map(f =>
    depRequireString(f.replace(/\.js$/, ''), 'internals', './internal/')).join('')

  await writeFile(WHERE.internalsInJS, requireString)
}

export async function bundleInternals() {
  await packInternals()
  bridge.message('internals:reload', {})
}

function packInternals() {
  log('packInternals')
  return new Promise((res, rej) => {
    webpack({
      entry: WHERE.internalsInJS,
      externals: {
        react: 'React',
        bluebird: '_bluebird',
        'react-dom': 'ReactDOM'
      },
      output: {
        filename: WHERE.internalsOutJS
      }
    }, async err => {
      if (err) {
        console.error(err.stack)
        return rej(err)
      }

      log('npm: pack: finished')
      res()
    })
  })
}

const findExternalRequires = source =>
  findRequires(source).filter(x => x.charAt(0) != '.')

async function installExternals(file, source) {
  log('installExternals', file)

  const found = findExternalRequires(source)
  cache.setFileImports(file, found)
  installAll(found)
}

let installing = []
let isInstalling = false

async function installAll(deps) {
  if (!deps)
    deps = cache.getImports()

  const filteredDeps = rmFlintExternals(deps)
  const installed = await readInstalled()
  const fresh = _.difference(filteredDeps, installed)

  // no new ones found
  if (!fresh.length) return

  // push installing
  installing = installing.concat(fresh)

  // check if installed running
  if (isInstalling) return
  isInstalling = true

  let successful = []

  const installNext = async () => {
    const dep = installing.shift()
    log('start install:', dep)
    onPackageStart(dep)

    try {
      await save(dep)
      successful.push(dep)
      onPackageFinish(dep)
      next()
    } catch(e) {
      log('package install failed', dep)
      onPackageError(dep, e)
      next()
    }
  }

  const next = () => installing.length ? installNext() : done()

  const done = async () => {
    // cache newly successful + installed
    const total = successful.concat(installed)
    await writeInstalled(total)
    logInstalled(successful)
    log('npm: installExternals() -> bundleExternals()')
    await bundleExternals()
    isInstalling = false
    return true
  }

  installNext()
}

function logProgress(tag, name, index, total) {
  log('npm', tag, name)

  const out = total ?
    ` ${index+1} of ${total}: ${name}` :
    `${tag}: ${name}`

  if (OPTS.build)
    console.log(out)
  else {
    console.log()
    let spinner = new Spinner(out)
    spinner.start({ fps: 30 })
    return spinner
  }
}

function execPromise(name, cmd, dir, spinner) {
  return new Promise((res, rej) => {
    exec(cmd, dir, (err, stdout, stderr) => {
      if (spinner) spinner.stop()
      if (err) rej(err)
      else res(name)
    })
  })
}

async function progressTask(label, cmd, name, index, total) {
  try {
    const spinner = logProgress(label, name, index, total)
    await execPromise(name, cmd, OPTS.flintDir, spinner)
  }
  catch(e) {
    handleError(e)
  }
}

// npm install --save 'name'
async function save(name, index, total) {
  await progressTask('Installing', 'npm install --save ' + name, name, index, total)
}

// npm uninstall --save 'name'
async function unsave(name, index, total) {
  await progressTask('Uninstalling', 'npm uninstall --save ' + name, name, index, total)
}

// webpack
// deps.js => packages.js
async function packExternals(file, out) {
  log('npm: pack')
  return new Promise((resolve, reject) => {
    webpack({
      entry: WHERE.depsJS,
      externals: {
        react: 'React',
        bluebird: '_bluebird',
        'react-dom': 'ReactDOM'
      },
      output: {
        filename: WHERE.packagesJS
      }
    }, async err => {
      if (err) {
        // undo written packages
        await rmdir(WHERE.depsJSON)
        console.log("Error bundling your packages:", err)
        return reject(err)
      }

      log('npm: pack: finished')
      resolve()
    })
  })
}

function getMatches(string, regex, index) {
  index || (index = 1) // default to the first capturing group
  var matches = []
  var match
  while (match = regex.exec(string)) {
    matches.push(match[index])
  }
  return matches
}

function logInstalled(deps) {
  if (!deps.length) return
  console.log()
  console.log(`  Installed ${deps.length} packages`.bold)
  deps.forEach(dep => {
    console.log(`  ✓ ${dep}`.green)
  })
  console.log()
}

export default { init, install, scanFile, bundleInternals, removeOld }<|endoftext|>


<commit_before>/**
 * Obtain an imageId for Cornerstone from an image instance
 *
 * @param instance
 * @returns {string} The imageId to be used by Cornerstone
 */
getImageId = function(instance, frame) {
    if (!instance) {
        return;
    }

    if (instance.wadouri) {
        var imageId = 'dicomweb:' + instance.wadouri; // WADO-URI;
        if (frame !== undefined) {
            imageId += '&frame=' + frame;
        }

        return imageId;
    } else {
        // TODO= Check multiframe image support with WADO-RS
        return getWADORSImageId(instance); // WADO-RS Retrieve Frame
    }
};<commit_msg>Add fallback in case instances have URL specified directly<commit_after>/**
 * Obtain an imageId for Cornerstone from an image instance
 *
 * @param instance
 * @returns {string} The imageId to be used by Cornerstone
 */
getImageId = function(instance, frame) {
    if (!instance) {
        return;
    }

    if (instance.url) {
        return instance.url;
    }

    if (instance.wadouri) {
        var imageId = 'dicomweb:' + instance.wadouri; // WADO-URI;
        if (frame !== undefined) {
            imageId += '&frame=' + frame;
        }

        return imageId;
    } else {
        // TODO= Check multiframe image support with WADO-RS
        return getWADORSImageId(instance); // WADO-RS Retrieve Frame
    }
};<|endoftext|>


<commit_before>var addon = require("..");
var assert = require("chai").assert;

describe("JsObject", function () {
  it("return the v8::Global object", function () {
    assert(global === addon.return_js_global_object());
  });

  it("return a JsObject built in Rust", function () {
    assert.deepEqual({}, addon.return_js_object());
  });

  it("return a JsObject with a number key value pair", function () {
    assert.deepEqual({ number: 9000 }, addon.return_js_object_with_number());
  });

  it("return a JsObject with an string key value pair", function () {
    assert.deepEqual(
      { string: "hello node" },
      addon.return_js_object_with_string()
    );
  });

  it("return a JsObject with mixed content key value pairs", function () {
    assert.deepEqual(
      { number: 9000, string: "hello node" },
      addon.return_js_object_with_mixed_content()
    );
  });

  it("correctly reads a TypedArray using the borrow API", function () {
    var b = new ArrayBuffer(32);
    var a = new Int32Array(b, 4, 4);
    a[0] = 49;
    a[1] = 1350;
    a[2] = 11;
    a[3] = 237;
    assert.equal(addon.read_typed_array_with_borrow(a, 0), 49);
    assert.equal(addon.read_typed_array_with_borrow(a, 1), 1350);
    assert.equal(addon.read_typed_array_with_borrow(a, 2), 11);
    assert.equal(addon.read_typed_array_with_borrow(a, 3), 237);
  });

  it("correctly writes to a TypedArray using the borrow_mut API", function () {
    var b = new ArrayBuffer(32);
    var a = new Int32Array(b, 4, 4);
    addon.write_typed_array_with_borrow_mut(a, 0, 43);
    assert.equal(a[0], 43);
    addon.write_typed_array_with_borrow_mut(a, 1, 1000);
    assert.equal(a[1], 1000);
    addon.write_typed_array_with_borrow_mut(a, 2, 22);
    assert.equal(a[2], 22);
    addon.write_typed_array_with_borrow_mut(a, 3, 243);
    assert.equal(a[3], 243);
  });

  it("correctly reads a Buffer as a typed array", function () {
    var a = Buffer.from([49, 135, 11, 237]);
    assert.equal(addon.read_u8_typed_array(a, 0), 49);
    assert.equal(addon.read_u8_typed_array(a, 1), 135);
    assert.equal(addon.read_u8_typed_array(a, 2), 11);
    assert.equal(addon.read_u8_typed_array(a, 3), 237);
  });

  it("copies the contents of one typed array to another", function () {
    const a = new Uint32Array([1, 2, 3, 4]);
    const b = new Uint32Array(a.length);

    addon.copy_typed_array(a, b);

    assert.deepEqual([...a], [...b]);
  });

  it("cannot borrow overlapping buffers", function () {
    const buf = new ArrayBuffer(20);
    const arr = new Uint32Array(buf);
    const a = new Uint32Array(buf, 4, 2);
    const b = new Uint32Array(buf, 8, 2);

    assert.throws(() => addon.copy_typed_array(a, b));
  });

  it("gets a 16-byte, zeroed ArrayBuffer", function () {
    var b = addon.return_array_buffer();
    assert.equal(b.byteLength, 16);
    assert.equal(new Uint32Array(b)[0], 0);
    assert.equal(new Uint32Array(b)[1], 0);
    assert.equal(new Uint32Array(b)[2], 0);
    assert.equal(new Uint32Array(b)[3], 0);
  });

  it("correctly reads an ArrayBuffer using the lock API", function () {
    var b = new ArrayBuffer(16);
    var a = new Uint32Array(b);
    a[0] = 47;
    a[1] = 133;
    a[2] = 9;
    a[3] = 88888888;
    assert.equal(addon.read_array_buffer_with_lock(a, 0), 47);
    assert.equal(addon.read_array_buffer_with_lock(a, 1), 133);
    assert.equal(addon.read_array_buffer_with_lock(a, 2), 9);
    assert.equal(addon.read_array_buffer_with_lock(a, 3), 88888888);
  });

  it("correctly reads an ArrayBuffer using the borrow API", function () {
    var b = new ArrayBuffer(4);
    var a = new Uint8Array(b);
    a[0] = 49;
    a[1] = 135;
    a[2] = 11;
    a[3] = 237;
    assert.equal(addon.read_array_buffer_with_borrow(b, 0), 49);
    assert.equal(addon.read_array_buffer_with_borrow(b, 1), 135);
    assert.equal(addon.read_array_buffer_with_borrow(b, 2), 11);
    assert.equal(addon.read_array_buffer_with_borrow(b, 3), 237);
  });

  it("correctly writes to an ArrayBuffer using the lock API", function () {
    var b = new ArrayBuffer(16);
    addon.write_array_buffer_with_lock(b, 0, 3);
    assert.equal(new Uint8Array(b)[0], 3);
    addon.write_array_buffer_with_lock(b, 1, 42);
    assert.equal(new Uint8Array(b)[1], 42);
    addon.write_array_buffer_with_lock(b, 2, 127);
    assert.equal(new Uint8Array(b)[2], 127);
    addon.write_array_buffer_with_lock(b, 3, 255);
    assert.equal(new Uint8Array(b)[3], 255);
  });

  it("correctly writes to an ArrayBuffer using the borrow_mut API", function () {
    var b = new ArrayBuffer(4);
    addon.write_array_buffer_with_borrow_mut(b, 0, 43);
    assert.equal(new Uint8Array(b)[0], 43);
    addon.write_array_buffer_with_borrow_mut(b, 1, 100);
    assert.equal(new Uint8Array(b)[1], 100);
    addon.write_array_buffer_with_borrow_mut(b, 2, 22);
    assert.equal(new Uint8Array(b)[2], 22);
    addon.write_array_buffer_with_borrow_mut(b, 3, 243);
    assert.equal(new Uint8Array(b)[3], 243);
  });

  it("gets a 16-byte, uninitialized Buffer", function () {
    var b = addon.return_uninitialized_buffer();
    assert.ok(b.length === 16);
  });

  it("gets a 16-byte, zeroed Buffer", function () {
    var b = addon.return_buffer();
    assert.ok(b.equals(Buffer.alloc(16)));
  });

  it("gets an external Buffer", function () {
    var expected = "String to copy";
    var buf = addon.return_external_buffer(expected);
    assert.instanceOf(buf, Buffer);
    assert.strictEqual(buf.toString(), expected);
  });

  it("gets an external ArrayBuffer", function () {
    var expected = "String to copy";
    var buf = addon.return_external_array_buffer(expected);
    assert.instanceOf(buf, ArrayBuffer);
    assert.strictEqual(Buffer.from(buf).toString(), expected);
  });

  it("correctly reads a Buffer using the lock API", function () {
    var b = Buffer.allocUnsafe(16);
    b.writeUInt8(147, 0);
    b.writeUInt8(113, 1);
    b.writeUInt8(109, 2);
    b.writeUInt8(189, 3);
    assert.equal(addon.read_buffer_with_lock(b, 0), 147);
    assert.equal(addon.read_buffer_with_lock(b, 1), 113);
    assert.equal(addon.read_buffer_with_lock(b, 2), 109);
    assert.equal(addon.read_buffer_with_lock(b, 3), 189);
  });

  it("correctly reads a Buffer using the borrow API", function () {
    var b = Buffer.from([149, 224, 70, 229]);
    assert.equal(addon.read_buffer_with_borrow(b, 0), 149);
    assert.equal(addon.read_buffer_with_borrow(b, 1), 224);
    assert.equal(addon.read_buffer_with_borrow(b, 2), 70);
    assert.equal(addon.read_buffer_with_borrow(b, 3), 229);
  });

  it("correctly writes to a Buffer using the lock API", function () {
    var b = Buffer.allocUnsafe(16);
    b.fill(0);
    addon.write_buffer_with_lock(b, 0, 6);
    assert.equal(b.readUInt8(0), 6);
    addon.write_buffer_with_lock(b, 1, 61);
    assert.equal(b.readUInt8(1), 61);
    addon.write_buffer_with_lock(b, 2, 45);
    assert.equal(b.readUInt8(2), 45);
    addon.write_buffer_with_lock(b, 3, 216);
    assert.equal(b.readUInt8(3), 216);
  });

  it("correctly writes to a Buffer using the borrow_mut API", function () {
    var b = Buffer.alloc(4);
    addon.write_buffer_with_borrow_mut(b, 0, 16);
    assert.equal(b[0], 16);
    addon.write_buffer_with_borrow_mut(b, 1, 100);
    assert.equal(b[1], 100);
    addon.write_buffer_with_borrow_mut(b, 2, 232);
    assert.equal(b[2], 232);
    addon.write_buffer_with_borrow_mut(b, 3, 55);
    assert.equal(b[3], 55);
  });

  it("returns only own properties from get_own_property_names", function () {
    var superObject = {
      a: 1,
    };

    var childObject = Object.create(superObject);
    childObject.b = 2;

    assert.deepEqual(
      addon.get_own_property_names(childObject),
      Object.getOwnPropertyNames(childObject)
    );
  });

  it("does not return Symbols from get_own_property_names", function () {
    var object = {};
    object["this should be a thing"] = 0;
    object[Symbol("this should not be a thing")] = 1;

    assert.deepEqual(
      addon.get_own_property_names(object),
      Object.getOwnPropertyNames(object)
    );
    assert.equal(addon.get_own_property_names(object).length, 1);
  });

  it("data borrowed on the heap can be held longer than the handle", function () {
    const msg = "Hello, World!";
    const buf = Buffer.from(msg);

    assert.strictEqual(addon.byte_length(msg), buf.length);
    assert.strictEqual(addon.byte_length(buf), buf.length);
  });

  it("calling Object::call_with() properly calls object methods", function () {
    const obj = {
      value: 42,
      nullary() {
        return this.value;
      },
      unary(x) {
        return this.value + x;
      },
    };

    assert.strictEqual(addon.call_nullary_method(obj), 42);
    assert.strictEqual(addon.call_unary_method(obj, 17), 59);
  });

  it("calling Object::call_with() with a symbol method name works", function () {
    const sym = Symbol.for("mySymbol");
    const obj = {
      [sym]() {
        return "hello";
      },
    };

    assert.stringEqual(addon.call_symbol_method(obj, sym), "hello");
  });
});
<commit_msg>Typo: `assert.stringEqual` -> `assert.strictEqual`<commit_after>var addon = require("..");
var assert = require("chai").assert;

describe("JsObject", function () {
  it("return the v8::Global object", function () {
    assert(global === addon.return_js_global_object());
  });

  it("return a JsObject built in Rust", function () {
    assert.deepEqual({}, addon.return_js_object());
  });

  it("return a JsObject with a number key value pair", function () {
    assert.deepEqual({ number: 9000 }, addon.return_js_object_with_number());
  });

  it("return a JsObject with an string key value pair", function () {
    assert.deepEqual(
      { string: "hello node" },
      addon.return_js_object_with_string()
    );
  });

  it("return a JsObject with mixed content key value pairs", function () {
    assert.deepEqual(
      { number: 9000, string: "hello node" },
      addon.return_js_object_with_mixed_content()
    );
  });

  it("correctly reads a TypedArray using the borrow API", function () {
    var b = new ArrayBuffer(32);
    var a = new Int32Array(b, 4, 4);
    a[0] = 49;
    a[1] = 1350;
    a[2] = 11;
    a[3] = 237;
    assert.equal(addon.read_typed_array_with_borrow(a, 0), 49);
    assert.equal(addon.read_typed_array_with_borrow(a, 1), 1350);
    assert.equal(addon.read_typed_array_with_borrow(a, 2), 11);
    assert.equal(addon.read_typed_array_with_borrow(a, 3), 237);
  });

  it("correctly writes to a TypedArray using the borrow_mut API", function () {
    var b = new ArrayBuffer(32);
    var a = new Int32Array(b, 4, 4);
    addon.write_typed_array_with_borrow_mut(a, 0, 43);
    assert.equal(a[0], 43);
    addon.write_typed_array_with_borrow_mut(a, 1, 1000);
    assert.equal(a[1], 1000);
    addon.write_typed_array_with_borrow_mut(a, 2, 22);
    assert.equal(a[2], 22);
    addon.write_typed_array_with_borrow_mut(a, 3, 243);
    assert.equal(a[3], 243);
  });

  it("correctly reads a Buffer as a typed array", function () {
    var a = Buffer.from([49, 135, 11, 237]);
    assert.equal(addon.read_u8_typed_array(a, 0), 49);
    assert.equal(addon.read_u8_typed_array(a, 1), 135);
    assert.equal(addon.read_u8_typed_array(a, 2), 11);
    assert.equal(addon.read_u8_typed_array(a, 3), 237);
  });

  it("copies the contents of one typed array to another", function () {
    const a = new Uint32Array([1, 2, 3, 4]);
    const b = new Uint32Array(a.length);

    addon.copy_typed_array(a, b);

    assert.deepEqual([...a], [...b]);
  });

  it("cannot borrow overlapping buffers", function () {
    const buf = new ArrayBuffer(20);
    const arr = new Uint32Array(buf);
    const a = new Uint32Array(buf, 4, 2);
    const b = new Uint32Array(buf, 8, 2);

    assert.throws(() => addon.copy_typed_array(a, b));
  });

  it("gets a 16-byte, zeroed ArrayBuffer", function () {
    var b = addon.return_array_buffer();
    assert.equal(b.byteLength, 16);
    assert.equal(new Uint32Array(b)[0], 0);
    assert.equal(new Uint32Array(b)[1], 0);
    assert.equal(new Uint32Array(b)[2], 0);
    assert.equal(new Uint32Array(b)[3], 0);
  });

  it("correctly reads an ArrayBuffer using the lock API", function () {
    var b = new ArrayBuffer(16);
    var a = new Uint32Array(b);
    a[0] = 47;
    a[1] = 133;
    a[2] = 9;
    a[3] = 88888888;
    assert.equal(addon.read_array_buffer_with_lock(a, 0), 47);
    assert.equal(addon.read_array_buffer_with_lock(a, 1), 133);
    assert.equal(addon.read_array_buffer_with_lock(a, 2), 9);
    assert.equal(addon.read_array_buffer_with_lock(a, 3), 88888888);
  });

  it("correctly reads an ArrayBuffer using the borrow API", function () {
    var b = new ArrayBuffer(4);
    var a = new Uint8Array(b);
    a[0] = 49;
    a[1] = 135;
    a[2] = 11;
    a[3] = 237;
    assert.equal(addon.read_array_buffer_with_borrow(b, 0), 49);
    assert.equal(addon.read_array_buffer_with_borrow(b, 1), 135);
    assert.equal(addon.read_array_buffer_with_borrow(b, 2), 11);
    assert.equal(addon.read_array_buffer_with_borrow(b, 3), 237);
  });

  it("correctly writes to an ArrayBuffer using the lock API", function () {
    var b = new ArrayBuffer(16);
    addon.write_array_buffer_with_lock(b, 0, 3);
    assert.equal(new Uint8Array(b)[0], 3);
    addon.write_array_buffer_with_lock(b, 1, 42);
    assert.equal(new Uint8Array(b)[1], 42);
    addon.write_array_buffer_with_lock(b, 2, 127);
    assert.equal(new Uint8Array(b)[2], 127);
    addon.write_array_buffer_with_lock(b, 3, 255);
    assert.equal(new Uint8Array(b)[3], 255);
  });

  it("correctly writes to an ArrayBuffer using the borrow_mut API", function () {
    var b = new ArrayBuffer(4);
    addon.write_array_buffer_with_borrow_mut(b, 0, 43);
    assert.equal(new Uint8Array(b)[0], 43);
    addon.write_array_buffer_with_borrow_mut(b, 1, 100);
    assert.equal(new Uint8Array(b)[1], 100);
    addon.write_array_buffer_with_borrow_mut(b, 2, 22);
    assert.equal(new Uint8Array(b)[2], 22);
    addon.write_array_buffer_with_borrow_mut(b, 3, 243);
    assert.equal(new Uint8Array(b)[3], 243);
  });

  it("gets a 16-byte, uninitialized Buffer", function () {
    var b = addon.return_uninitialized_buffer();
    assert.ok(b.length === 16);
  });

  it("gets a 16-byte, zeroed Buffer", function () {
    var b = addon.return_buffer();
    assert.ok(b.equals(Buffer.alloc(16)));
  });

  it("gets an external Buffer", function () {
    var expected = "String to copy";
    var buf = addon.return_external_buffer(expected);
    assert.instanceOf(buf, Buffer);
    assert.strictEqual(buf.toString(), expected);
  });

  it("gets an external ArrayBuffer", function () {
    var expected = "String to copy";
    var buf = addon.return_external_array_buffer(expected);
    assert.instanceOf(buf, ArrayBuffer);
    assert.strictEqual(Buffer.from(buf).toString(), expected);
  });

  it("correctly reads a Buffer using the lock API", function () {
    var b = Buffer.allocUnsafe(16);
    b.writeUInt8(147, 0);
    b.writeUInt8(113, 1);
    b.writeUInt8(109, 2);
    b.writeUInt8(189, 3);
    assert.equal(addon.read_buffer_with_lock(b, 0), 147);
    assert.equal(addon.read_buffer_with_lock(b, 1), 113);
    assert.equal(addon.read_buffer_with_lock(b, 2), 109);
    assert.equal(addon.read_buffer_with_lock(b, 3), 189);
  });

  it("correctly reads a Buffer using the borrow API", function () {
    var b = Buffer.from([149, 224, 70, 229]);
    assert.equal(addon.read_buffer_with_borrow(b, 0), 149);
    assert.equal(addon.read_buffer_with_borrow(b, 1), 224);
    assert.equal(addon.read_buffer_with_borrow(b, 2), 70);
    assert.equal(addon.read_buffer_with_borrow(b, 3), 229);
  });

  it("correctly writes to a Buffer using the lock API", function () {
    var b = Buffer.allocUnsafe(16);
    b.fill(0);
    addon.write_buffer_with_lock(b, 0, 6);
    assert.equal(b.readUInt8(0), 6);
    addon.write_buffer_with_lock(b, 1, 61);
    assert.equal(b.readUInt8(1), 61);
    addon.write_buffer_with_lock(b, 2, 45);
    assert.equal(b.readUInt8(2), 45);
    addon.write_buffer_with_lock(b, 3, 216);
    assert.equal(b.readUInt8(3), 216);
  });

  it("correctly writes to a Buffer using the borrow_mut API", function () {
    var b = Buffer.alloc(4);
    addon.write_buffer_with_borrow_mut(b, 0, 16);
    assert.equal(b[0], 16);
    addon.write_buffer_with_borrow_mut(b, 1, 100);
    assert.equal(b[1], 100);
    addon.write_buffer_with_borrow_mut(b, 2, 232);
    assert.equal(b[2], 232);
    addon.write_buffer_with_borrow_mut(b, 3, 55);
    assert.equal(b[3], 55);
  });

  it("returns only own properties from get_own_property_names", function () {
    var superObject = {
      a: 1,
    };

    var childObject = Object.create(superObject);
    childObject.b = 2;

    assert.deepEqual(
      addon.get_own_property_names(childObject),
      Object.getOwnPropertyNames(childObject)
    );
  });

  it("does not return Symbols from get_own_property_names", function () {
    var object = {};
    object["this should be a thing"] = 0;
    object[Symbol("this should not be a thing")] = 1;

    assert.deepEqual(
      addon.get_own_property_names(object),
      Object.getOwnPropertyNames(object)
    );
    assert.equal(addon.get_own_property_names(object).length, 1);
  });

  it("data borrowed on the heap can be held longer than the handle", function () {
    const msg = "Hello, World!";
    const buf = Buffer.from(msg);

    assert.strictEqual(addon.byte_length(msg), buf.length);
    assert.strictEqual(addon.byte_length(buf), buf.length);
  });

  it("calling Object::call_with() properly calls object methods", function () {
    const obj = {
      value: 42,
      nullary() {
        return this.value;
      },
      unary(x) {
        return this.value + x;
      },
    };

    assert.strictEqual(addon.call_nullary_method(obj), 42);
    assert.strictEqual(addon.call_unary_method(obj, 17), 59);
  });

  it("calling Object::call_with() with a symbol method name works", function () {
    const sym = Symbol.for("mySymbol");
    const obj = {
      [sym]() {
        return "hello";
      },
    };

    assert.strictEqual(addon.call_symbol_method(obj, sym), "hello");
  });
});
<|endoftext|>


<commit_before>require                 = require('@std/esm')(module, { cjs: true, esm: 'js' });
const test              = require("tape");
const ObservableObject  = require("../src/jsutils/ObservableObject").default;
const delay             = (ms) => new Promise(resolve => setTimeout(resolve, ms || 2));

test("ObservableObject", t => {
    t.equal(typeof ObservableObject, "function", "is defined");
    t.equal(typeof ObservableObject.create, "function", "has .create() method");
    t.equal(typeof ObservableObject.mixin, "function", "has .mixin() method");
    t.equal(typeof ObservableObject.createComputed, "function", "has .createComputed() method");

    t.test("Instance Methods", st => {
        const model = ObservableObject.create({name: "paul"});

        st.equal(model.name, "paul", "is instantiated with provided input");

        st.equal(typeof model.emit, "function", "has .emit() method");
        st.equal(typeof model.on, "function", "has .on() method");
        st.equal(typeof model.once, "function", "has .once() method");
        st.equal(typeof model.destroy, "function", "has .destroy() method");

        model.destroy();
        st.equal(model.isDestroyed, true, ".isDestroy is true");

        st.end();
    });

    t.test("Emits Events", st => {
        st.plan(3);

        const model = ObservableObject.create({name: "paul"});

        model.once("name", () => {
            st.ok(true, "change event triggered");
        });

        st.equal(model.name, "paul", "has original value after calling .on()");
        model.name = "paul 1";

        const changeCallback = () => changeCallback.count++;
        changeCallback.count = 0;

        model.on("name", changeCallback);
        [1, 2, 3].forEach(n => model.name = `paul-${n}`);

        setTimeout(() => {
            st.equal(changeCallback.count, 1, "Callbacks are executed only once per event loop");
        }, 40);
    });

    t.test("Supports existing getters and setters", st => {
        st.plan(4);

        let obj = ObservableObject.create();
        let value = "paul";
        let getter = () => {
            getter.count++;
            return value;
        };
        let setter = (val) => {
            setter.count++;
            return value = val;
        };

        getter.count = 0;
        setter.count = 0;

        Object.defineProperty(obj, "name", {
            configurable: true,
            get: getter,
            set: setter
        });

        obj.once("name", () => {
            st.equal(setter.count, 1, "Original Setter is called");
        });

        st.equal(getter.count, 0, "getter has not been called");
        st.equal(obj.name, "paul", "getter return original value");
        st.equal(getter.count, 1, "Original Getter is called");

        obj.name = "paul1";
    });

    t.test("Computed properties", st => {
        st.plan(8);

        let obj = ObservableObject.create({
            firstName: "Paul",
            lastName: "Tavares"
        });

        let generateFullName = function() {
            generateFullName.count++;
            return `${ this.firstName } ${ this.lastName }`;
        };
        generateFullName.count = 0;

        ObservableObject.createComputed(obj, "fullName", generateFullName);

        st.equal(generateFullName.count, 0, "Value Generator does not run on create of computed");
        st.equal(obj.fullName, "Paul Tavares", "creates Computed value");
        st.equal(generateFullName.count, 1, "value generated called once");

        obj.firstName = "Paul1";
        delay()
            .then(() => {
                st.equal(obj.fullName, "Paul1 Tavares", "change to dependency prop updates Computed value");
                st.equal(generateFullName.count, 2, "Value generator called twice");

                obj.firstName = "john";
                obj.lastName = "smith";

                return delay();
            })
            .then(() => {
                st.equal(generateFullName.count, 2, "Value generated NOT called yet after dependency change");
                st.equal(obj.fullName, "john smith", "change to multiple depdencies props updates computed");
                st.equal(generateFullName.count, 3, "Value generator called three times");
            })
            .catch(e => console.log(e));
    });

    t.test("Computed Emits Events", st => {
        st.plan(8);

        let obj = ObservableObject.create({
            firstName: "Paul",
            lastName: "Tavares"
        });

        let generateFullName = function() {
            generateFullName.count++;
            return `${ this.firstName } ${ this.lastName }`;
        };
        generateFullName.count = 0;

        ObservableObject.createComputed(obj, "fullName", generateFullName);

        let fullNameChgListener = () => fullNameChgListener.count++;
        fullNameChgListener.count = 0;

        obj.on("fullName", fullNameChgListener);

        st.equal(fullNameChgListener.count, 0, "computed change event not yet triggered");

        obj.fullName;

        delay()
            .then(() => {
                st.equal(fullNameChgListener.count, 0, "computed changed event not triggered after valueGenerated 1st run");
                obj.firstName = "john";
                return delay();
            })
            .then(() => {
                st.equal(fullNameChgListener.count, 1, "Computed change Event fired");
                st.equal(generateFullName.count, 1, "Computed prop value generator not called after dependency change");

                st.equal(obj.fullName, "john Tavares", "New computed value generated");
                obj.fullName;
                st.equal(generateFullName.count, 2, "New computed value was cached on subsequent calls to getter");

                obj.lastName = "Smith";
                return delay();
            })
            .then(() => {
                st.equal(fullNameChgListener.count, 2, "Computed change Event fired on 2nd dependency change");
                st.equal(obj.fullName, "john Smith", "new Computed value return after 2nd dependency change");
            });
    });

    t.test("Computed properties with dependencies on other Computed", st => {

    });

    t.end();
});<commit_msg>ObservableObject - Test cases for Computed prop that depends on other computed.<commit_after>require                 = require('@std/esm')(module, { cjs: true, esm: 'js' });
const test              = require("tape");
const ObservableObject  = require("../src/jsutils/ObservableObject").default;
const delay             = (ms) => new Promise(resolve => setTimeout(resolve, ms || 2));

test("ObservableObject", t => {
    t.equal(typeof ObservableObject, "function", "is defined");
    t.equal(typeof ObservableObject.create, "function", "has .create() method");
    t.equal(typeof ObservableObject.mixin, "function", "has .mixin() method");
    t.equal(typeof ObservableObject.createComputed, "function", "has .createComputed() method");

    t.test("Instance Methods", st => {
        const model = ObservableObject.create({name: "paul"});

        st.equal(model.name, "paul", "is instantiated with provided input");

        st.equal(typeof model.emit, "function", "has .emit() method");
        st.equal(typeof model.on, "function", "has .on() method");
        st.equal(typeof model.once, "function", "has .once() method");
        st.equal(typeof model.destroy, "function", "has .destroy() method");

        model.destroy();
        st.equal(model.isDestroyed, true, ".isDestroy is true");

        st.end();
    });

    t.test("Emits Events", st => {
        st.plan(3);

        const model = ObservableObject.create({name: "paul"});

        model.once("name", () => {
            st.ok(true, "change event triggered");
        });

        st.equal(model.name, "paul", "has original value after calling .on()");
        model.name = "paul 1";

        const changeCallback = () => changeCallback.count++;
        changeCallback.count = 0;

        model.on("name", changeCallback);
        [1, 2, 3].forEach(n => model.name = `paul-${n}`);

        setTimeout(() => {
            st.equal(changeCallback.count, 1, "Callbacks are executed only once per event loop");
        }, 40);
    });

    t.test("Supports existing getters and setters", st => {
        st.plan(4);

        let obj = ObservableObject.create();
        let value = "paul";
        let getter = () => {
            getter.count++;
            return value;
        };
        let setter = (val) => {
            setter.count++;
            return value = val;
        };

        getter.count = 0;
        setter.count = 0;

        Object.defineProperty(obj, "name", {
            configurable: true,
            get: getter,
            set: setter
        });

        obj.once("name", () => {
            st.equal(setter.count, 1, "Original Setter is called");
        });

        st.equal(getter.count, 0, "getter has not been called");
        st.equal(obj.name, "paul", "getter return original value");
        st.equal(getter.count, 1, "Original Getter is called");

        obj.name = "paul1";
    });

    t.test("Computed properties", st => {
        st.plan(8);

        let obj = ObservableObject.create({
            firstName: "Paul",
            lastName: "Tavares"
        });

        let generateFullName = function() {
            generateFullName.count++;
            return `${ this.firstName } ${ this.lastName }`;
        };
        generateFullName.count = 0;

        ObservableObject.createComputed(obj, "fullName", generateFullName);

        st.equal(generateFullName.count, 0, "Value Generator does not run on create of computed");
        st.equal(obj.fullName, "Paul Tavares", "creates Computed value");
        st.equal(generateFullName.count, 1, "value generated called once");

        obj.firstName = "Paul1";
        delay()
            .then(() => {
                st.equal(obj.fullName, "Paul1 Tavares", "change to dependency prop updates Computed value");
                st.equal(generateFullName.count, 2, "Value generator called twice");

                obj.firstName = "john";
                obj.lastName = "smith";

                return delay();
            })
            .then(() => {
                st.equal(generateFullName.count, 2, "Value generated NOT called yet after dependency change");
                st.equal(obj.fullName, "john smith", "change to multiple depdencies props updates computed");
                st.equal(generateFullName.count, 3, "Value generator called three times");
            })
            .catch(e => console.log(e));
    });

    t.test("Computed Emits Events", st => {
        st.plan(8);

        let obj = ObservableObject.create({
            firstName: "Paul",
            lastName: "Tavares"
        });

        let generateFullName = function() {
            generateFullName.count++;
            return `${ this.firstName } ${ this.lastName }`;
        };
        generateFullName.count = 0;

        ObservableObject.createComputed(obj, "fullName", generateFullName);

        let fullNameChgListener = () => fullNameChgListener.count++;
        fullNameChgListener.count = 0;

        obj.on("fullName", fullNameChgListener);

        st.equal(fullNameChgListener.count, 0, "computed change event not yet triggered");

        obj.fullName;

        delay()
            .then(() => {
                st.equal(fullNameChgListener.count, 0, "computed changed event not triggered after valueGenerated 1st run");
                obj.firstName = "john";
                return delay();
            })
            .then(() => {
                st.equal(fullNameChgListener.count, 1, "Computed change Event fired");
                st.equal(generateFullName.count, 1, "Computed prop value generator not called after dependency change");

                st.equal(obj.fullName, "john Tavares", "New computed value generated");
                obj.fullName;
                st.equal(generateFullName.count, 2, "New computed value was cached on subsequent calls to getter");

                obj.lastName = "Smith";
                return delay();
            })
            .then(() => {
                st.equal(fullNameChgListener.count, 2, "Computed change Event fired on 2nd dependency change");
                st.equal(obj.fullName, "john Smith", "new Computed value return after 2nd dependency change");
            });
    });

    t.test("Computed properties with dependencies on other Computed", st => {
        st.plan(6);

        let obj = ObservableObject.create({
            firstName: "Paul",
            lastName: "Tavares",
            location: "NJ"
        });

        let generateFullName = function() {
            generateFullName.count++;
            return `${ this.firstName } ${ this.lastName }`;
        };
        generateFullName.count = 0;

        ObservableObject.createComputed(obj, "fullName", generateFullName);

        let generateNameAndLocation = function() {
            generateNameAndLocation.count++;
            return `${ this.fullName } (${ this.location })`;
        };
        generateNameAndLocation.count = 0;

        ObservableObject.createComputed(obj, "nameAndLocation", generateNameAndLocation);

        let nameAndLocationChgListener = () => nameAndLocationChgListener.count++;
        nameAndLocationChgListener.count = 0;

        //------------------------------------------------------------
        st.equal(obj.nameAndLocation, "Paul Tavares (NJ)", "Computed is generated");

        obj.firstName = "John";
        delay()
            .then(() => {
                st.equal(obj.nameAndLocation, "John Tavares (NJ)", "Computed has change from computed dependency");

                obj.location = "NY";
                return delay();
            })
            .then(() => {
                st.equal(obj.nameAndLocation, "John Tavares (NY)", "Computed reflects changes dependency change");

                obj.on("nameAndLocation", nameAndLocationChgListener);
                obj.location = "NJ";
                return delay();
            })
            .then(() => {
                st.equal(nameAndLocationChgListener.count, 1, "Change of direct dependency trigger event");

                obj.firstName = "Paul";
                return delay();
            })
            .then(() => {
                // FIXME: the event should only have triggered 2 times at this point. Root cause: fact that events are not batched globally?
                st.equal(nameAndLocationChgListener.count, 3, "Change of computed dependency trigger event");
                st.equal(obj.nameAndLocation, "Paul Tavares (NJ)", "Computed has expected value");
            });
    });

    t.end();
});<|endoftext|>


<commit_before>// ==UserScript==
// @name        Smart QR
// @namespace   http://tampermonkey.net/
// @description Detect links to phone numbers and locations and display a QR code on mouseover.
// @include     http://*
// @include     https://*
// @version     0.1
// ==/UserScript==

(function () {

  function addQR(value) {
    //console.log(value);
    if (value == '') {
      return;
    }
    i.src = 'https://chart.apis.google.com/chart?cht=qr&chs=100x100&chld=L|0&chl=' + encodeURIComponent(value);
    i.style.visibility = "visible";
  };

  function removeQR() {
    i.style.visibility = "hidden";
  };

  function addListeners(element, value) {
    element.addEventListener('mouseover', function() { addQR(value); } );
    element.addEventListener('mouseout', function() { removeQR(); });
    element.smartQRvalue = value;
  }

  function handleTextSelect(event) {
    var selection = document.getSelection();
    var text = selection.toString();
    if (text == '') {
      removeQR();
    }
    var re = new RegExp('^[^a-zA-Z]*$');
    var matches = text.match(re);
    if ((matches) && (matches[0] != '')) {
      var number = matches[0].replace(/[^0-9\+]/g, '');
      if (number == '') {
        return;
      }
      number = 'tel:' + number;
      addQR(number);
    }
  }

  function parseAndAddListeners() {
    var elements = document.getElementsByTagName('a');
    for(var i=0, len=elements.length; i < len; i++){
      var element = elements[i];
      if (element.href) {
        // elements with href="tel:..."
        var re = new RegExp('^tel:(.*)$');
        var matches = element.href.match(re);
        if (matches) {
          addListeners(element, element.href);
        }
        // elements with href="maps.google.com/?.*q=12,34"
        var re = new RegExp('maps.google.com.*q=([0-9\.]*),([0-9\.]*)');
        var matches = element.href.match(re);
        if (matches) {
          var value = 'geo:' + matches[1] + ',' + matches[2];
          addListeners(element, value);
        }
        // Wikipedia location elements.
        if (currentURL.hostname.match(/wikipedia\.org/)) {
          var re = new RegExp('tools.wmflabs.org/geohack/geohack\.php.*params=([0-9\.]*)_._([0-9\.]*)');
          var matches = element.href.match(re);
          if (matches) {
            var value = 'geo:' + matches[1] + ',' + matches[2];
            addListeners(element, value);
          }
        }
        // Openstreetmap coordinate link.
        if (currentURL.hostname.match(/openstreetmap\.org/)) {
          var re = new RegExp('/#map=[0-9]+/([0-9\.]+)/([0-9\.]+)');
          var matches = element.href.match(re);
          if (matches) {
            var value = 'geo:' + matches[1] + ',' + matches[2];
            addListeners(element, value);
          }
        }
        // Any "geo:" link.
        var re = new RegExp('geo:([0-9\.]+),([0-9\.]+)');
        var matches = element.href.match(re);
        if (matches) {
          var value = 'geo:' + matches[1] + ',' + matches[2];
          addListeners(element, value);
        }
      }
      // elements with data-number.
      if (element.dataset) {
        if (element.dataset.number) {
          addListeners(element, element.dataset.number);
        }
      }
    }
    // Facebook events, location.
    if (currentURL.hostname.match(/facebook\.com/)) {
      var elements = document.getElementsByTagName('img');
      for(var i=0, len=elements.length; i < len; i++){
        var element = elements[i];
        if (element.src) {
          var re = new RegExp('markers=([0-9\.]+)%2C([0-9\.]+)');
          var matches = element.src.match(re);
          if (matches) {
        console.log(element);
            var value = 'geo:' + matches[1] + ',' + matches[2];
            addListeners(element, value);
          }
        }
      }
    }
    // Google maps, info sidebar, phone numbers
    if (currentURL.hostname.match(/google/)) {
      var elements = document.getElementsByTagName('button');
      for(var i=0, len=elements.length; i < len; i++){
        var element = elements[i];
        if (element.dataset) {
          if (element.dataset.href) {
            addListeners(element, element.dataset.href);
          }
        }
      }
    }
    // Text selections.
    document.addEventListener('mouseup', handleTextSelect);
  }

  function manualTriggerWithDblClick() {
    window.addEventListener('dblclick', parseAndAddListeners, true);
  }

  function parseURL(url) {
    var parser = document.createElement('a');
    parser.href = url;
    return parser;
  }

  "use strict";

  var currentURL = parseURL(window.location.href);

  // Create code and add to body.
  var i = document.createElement("img");
  i.style.width = "100px";
  i.style.height = "100px";
  i.style.position = "fixed";
  i.style.margin = "20px";
  i.style.bottom = "0";
  i.style.zIndex = "999";
  i.style.visibility = "hidden";
  i.alt = "QR Code";

  document.body.appendChild(i);

  parseAndAddListeners();
  manualTriggerWithDblClick();

}());

<commit_msg>Bottom and left.<commit_after>// ==UserScript==
// @name        Smart QR
// @namespace   http://tampermonkey.net/
// @description Detect links to phone numbers and locations and display a QR code on mouseover.
// @include     http://*
// @include     https://*
// @version     0.1
// ==/UserScript==

(function () {

  function addQR(value) {
    //console.log(value);
    if (value == '') {
      return;
    }
    i.src = 'https://chart.apis.google.com/chart?cht=qr&chs=100x100&chld=L|0&chl=' + encodeURIComponent(value);
    i.style.visibility = "visible";
  };

  function removeQR() {
    i.style.visibility = "hidden";
  };

  function addListeners(element, value) {
    element.addEventListener('mouseover', function() { addQR(value); } );
    element.addEventListener('mouseout', function() { removeQR(); });
    element.smartQRvalue = value;
  }

  function handleTextSelect(event) {
    var selection = document.getSelection();
    var text = selection.toString();
    if (text == '') {
      removeQR();
    }
    var re = new RegExp('^[^a-zA-Z]*$');
    var matches = text.match(re);
    if ((matches) && (matches[0] != '')) {
      var number = matches[0].replace(/[^0-9\+]/g, '');
      if (number == '') {
        return;
      }
      number = 'tel:' + number;
      addQR(number);
    }
  }

  function parseAndAddListeners() {
    var elements = document.getElementsByTagName('a');
    for(var i=0, len=elements.length; i < len; i++){
      var element = elements[i];
      if (element.href) {
        // elements with href="tel:..."
        var re = new RegExp('^tel:(.*)$');
        var matches = element.href.match(re);
        if (matches) {
          addListeners(element, element.href);
        }
        // elements with href="maps.google.com/?.*q=12,34"
        var re = new RegExp('maps.google.com.*q=([0-9\.]*),([0-9\.]*)');
        var matches = element.href.match(re);
        if (matches) {
          var value = 'geo:' + matches[1] + ',' + matches[2];
          addListeners(element, value);
        }
        // Wikipedia location elements.
        if (currentURL.hostname.match(/wikipedia\.org/)) {
          var re = new RegExp('tools.wmflabs.org/geohack/geohack\.php.*params=([0-9\.]*)_._([0-9\.]*)');
          var matches = element.href.match(re);
          if (matches) {
            var value = 'geo:' + matches[1] + ',' + matches[2];
            addListeners(element, value);
          }
        }
        // Openstreetmap coordinate link.
        if (currentURL.hostname.match(/openstreetmap\.org/)) {
          var re = new RegExp('/#map=[0-9]+/([0-9\.]+)/([0-9\.]+)');
          var matches = element.href.match(re);
          if (matches) {
            var value = 'geo:' + matches[1] + ',' + matches[2];
            addListeners(element, value);
          }
        }
        // Any "geo:" link.
        var re = new RegExp('geo:([0-9\.]+),([0-9\.]+)');
        var matches = element.href.match(re);
        if (matches) {
          var value = 'geo:' + matches[1] + ',' + matches[2];
          addListeners(element, value);
        }
      }
      // elements with data-number.
      if (element.dataset) {
        if (element.dataset.number) {
          addListeners(element, element.dataset.number);
        }
      }
    }
    // Facebook events, location.
    if (currentURL.hostname.match(/facebook\.com/)) {
      var elements = document.getElementsByTagName('img');
      for(var i=0, len=elements.length; i < len; i++){
        var element = elements[i];
        if (element.src) {
          var re = new RegExp('markers=([0-9\.]+)%2C([0-9\.]+)');
          var matches = element.src.match(re);
          if (matches) {
        console.log(element);
            var value = 'geo:' + matches[1] + ',' + matches[2];
            addListeners(element, value);
          }
        }
      }
    }
    // Google maps, info sidebar, phone numbers
    if (currentURL.hostname.match(/google/)) {
      var elements = document.getElementsByTagName('button');
      for(var i=0, len=elements.length; i < len; i++){
        var element = elements[i];
        if (element.dataset) {
          if (element.dataset.href) {
            addListeners(element, element.dataset.href);
          }
        }
      }
    }
    // Text selections.
    document.addEventListener('mouseup', handleTextSelect);
  }

  function manualTriggerWithDblClick() {
    window.addEventListener('dblclick', parseAndAddListeners, true);
  }

  function parseURL(url) {
    var parser = document.createElement('a');
    parser.href = url;
    return parser;
  }

  "use strict";

  var currentURL = parseURL(window.location.href);

  // Create code and add to body.
  var i = document.createElement("img");
  i.style.width = "100px";
  i.style.height = "100px";
  i.style.position = "fixed";
  i.style.left = "10px";
  i.style.bottom = "30px";
  i.style.zIndex = "999";
  i.style.visibility = "hidden";
  i.alt = "QR Code";

  document.body.appendChild(i);

  parseAndAddListeners();
  manualTriggerWithDblClick();

}());

<|endoftext|>