<commit_before>import Vue from "vue"
import Util from "./Util"
import Station from "./Station"

export default () => {
    Vue.component("station-editor", {
        template: "
<md-dialog ref='dialog' v-on:close='validationError=undefined'>" +
        "        <md-dialog-title v-if='selectedStation'>" +
        "
{{selectedStation.isNew ? 'Add Station' : 'Edit Station'}}
" +
        "            <md-button style='position: absolute; right: 10px; top: 10px;' class='md-icon-button'" +
        "                v-on:click='$refs.dialog.close()'><md-icon>close</md-icon></md-button></md-dialog-title>" +
        "        <md-dialog-content v-if='selectedStation'>" +
        "           <md-input-container>" +
        "               Name" +
        "               <md-input required maxlength='20' v-model=\"selectedStation.title\"></md-input>" +
        "           </md-input-container>" +
        "            <md-input-container>" +
        "                Description" +
        "                <md-textarea v-model=\"selectedStation.description\"></md-textarea>" +
        "            </md-input-container>" +
        "
" +
        "
Sources
" +
        "                <md-button " +
        "                        v-on:click='selectedStation.source.push({src: ``, type: ``})' " +
        "                        class='md-icon-button md-raised md-dense'><md-icon>add</md-icon></md-button>" +
        "
" +
        "            <md-whiteframe v-for='source in selectedStation.source' key='{{source.src}}' md-elevation='2'" +
        "                style='padding: 10px 20px; margin-bottom: 5px;'>" +
        "                <md-input-container md-clearable>" +
        "                    URL" +
        "                    <md-input required v-model='source.src'></md-input>" +
        "                </md-input-container>" +
        "                <md-input-container>" +
        "                    Type" +
        "                    <md-input v-model='source.type'></md-input>" +
        "                </md-input-container>" +
        "                <md-button style='' v-show='selectedStation.source.length > 1' " +
        "                    v-on:click='selectedStation.source.splice(selectedStation.source.indexOf(source),1)' " +
        "                    class='md-icon-button md-dense'><md-icon>delete</md-icon></md-button>" +
        "            </md-whiteframe>" +
        "            {{validationError}}" +
        "            <md-dialog-actions>" +
        "                <md-button v-show='!selectedStation.isNew' class='md-accent' v-on:click='deleteHandler()'>delete</md-button>" +
        "                <md-button v-show='!selectedStation.isNew' class='md-primary md-raised' v-on:click='saveStationEdit()'>save</md-button> " +
        "                <md-button v-show='selectedStation.isNew' class='md-primary md-raised' v-on:click='addStation()'>add</md-button> " +
        "            </md-dialog-actions>" +
        "        </md-dialog-content>" +
        "    </md-dialog>
",
        props: {
            stations: {
                default: [],
                type: Array
            }
        },
        data() {
            return {
                selectedStation: undefined,
                validationError: undefined
            }
        },
        methods: {

            /**
             * Opens station editor dialog. Creates new station if id not given
             * @param {String} id - Id of station to show
             * @returns {undefined}
             */
            open(id) {
                if (id) {
                    const stationIndex = Util.getStationIndex(this.stations, id);
                    if (stationIndex === -1) {
                        throw new Error("StationEditor: Open called with invalid station id");
                    }
                    const station = this.stations[stationIndex].clone();
                    // this.selectedStation = station.clone();
                    this.selectedStation = {
                        id: station.id,
                        title: station.title,
                        description: station.description,
                        source: station.source
                    }
                }
                else {
                    // Provisional station object structure to be validated only on addStation trigger by user
                    this.selectedStation = {
                        isNew: true,
                        id: "",
                        title: "",
                        description: "",
                        source: [
                            {
                                src: "",
                                type: ""
                            }
                        ]

                    };
                }
                this.$refs.dialog.open();
            },

            /**
             * Add selectedStation to stations array
             * Called by add button
             * @returns {undefined}
             */
            addStation() {
                // Reset error field
                this.validationError = undefined;
                // Create station id by lowercasing string and replacing space with underscore
                this.selectedStation.id = this.selectedStation.title.replace(" ", "_").toLowerCase();

                // Attempt to add it to stations array
                try {
                    Util.addStation(this.stations, this.selectedStation.id, this.selectedStation.title,
                        this.selectedStation.description, this.selectedStation.source);
                }
                catch (error) {
                    log.error("User attempted to add station but it failed", this.selectedStation, error);
                    this.validationError = error.message;
                    return;
                }
                this.$refs.dialog.close();
            },

            /**
             * Validates changes to currently selected station and applies them if valid
             * Called by save button
             * @returns {undefined}
             */
            saveStationEdit() {
                //FIXME: Vuejs doesn't detect object change because we replace it, we should rather copy fields one by one to *old* object or manually trigger change
                // Reset error field
                this.validationError = undefined;

                try {
                    const stationIndex = Util.getStationIndex(this.stations, this.selectedStation.id);
                    if (stationIndex === -1) {
                        log.error("Attempted to save station data but could not find station in array by id");
                    }
                    this.stations[stationIndex] = new Station(this.selectedStation.id, this.selectedStation.title,
                        this.selectedStation.description, this.selectedStation.source);
                }
                catch (error) {
                    log.error("User attempted to edit station but it failed", this.selectedStation, error);
                    this.validationError = error.message;
                    return;
                }
                this.$refs.dialog.close();
            },

            deleteHandler() {
                const index = Util.getStationIndex(this.stations, this.selectedStation.id);
                if (index !== -1) {
                    this.stations.splice(index, 1);
                    this.$refs.dialog.close();
                }
            },
        }
    })
}<commit_msg>StationEditor: Fixed station save issue on edit<commit_after>import Vue from "vue"
import Util from "./Util"
import Station from "./Station"

export default () => {
    Vue.component("station-editor", {
        template: "
<md-dialog ref='dialog' v-on:close='validationError=undefined'>" +
        "        <md-dialog-title v-if='selectedStation'>" +
        "
{{selectedStation.isNew ? 'Add Station' : 'Edit Station'}}
" +
        "            <md-button style='position: absolute; right: 10px; top: 10px;' class='md-icon-button'" +
        "                v-on:click='$refs.dialog.close()'><md-icon>close</md-icon></md-button></md-dialog-title>" +
        "        <md-dialog-content v-if='selectedStation'>" +
        "           <md-input-container>" +
        "               Name" +
        "               <md-input required maxlength='20' v-model=\"selectedStation.title\"></md-input>" +
        "           </md-input-container>" +
        "            <md-input-container>" +
        "                Description" +
        "                <md-textarea v-model=\"selectedStation.description\"></md-textarea>" +
        "            </md-input-container>" +
        "
" +
        "
Sources
" +
        "                <md-button " +
        "                        v-on:click='selectedStation.source.push({src: ``, type: ``})' " +
        "                        class='md-icon-button md-raised md-dense'><md-icon>add</md-icon></md-button>" +
        "
" +
        "            <md-whiteframe v-for='source in selectedStation.source' key='{{source.src}}' md-elevation='2'" +
        "                style='padding: 10px 20px; margin-bottom: 5px;'>" +
        "                <md-input-container md-clearable>" +
        "                    URL" +
        "                    <md-input required v-model='source.src'></md-input>" +
        "                </md-input-container>" +
        "                <md-input-container>" +
        "                    Type" +
        "                    <md-input v-model='source.type'></md-input>" +
        "                </md-input-container>" +
        "                <md-button style='' v-show='selectedStation.source.length > 1' " +
        "                    v-on:click='selectedStation.source.splice(selectedStation.source.indexOf(source),1)' " +
        "                    class='md-icon-button md-dense'><md-icon>delete</md-icon></md-button>" +
        "            </md-whiteframe>" +
        "            {{validationError}}" +
        "            <md-dialog-actions>" +
        "                <md-button v-show='!selectedStation.isNew' class='md-accent' v-on:click='deleteHandler()'>delete</md-button>" +
        "                <md-button v-show='!selectedStation.isNew' class='md-primary md-raised' v-on:click='saveStationEdit()'>save</md-button> " +
        "                <md-button v-show='selectedStation.isNew' class='md-primary md-raised' v-on:click='addStation()'>add</md-button> " +
        "            </md-dialog-actions>" +
        "        </md-dialog-content>" +
        "    </md-dialog>
",
        props: {
            stations: {
                default: [],
                type: Array
            }
        },
        data() {
            return {
                selectedStation: undefined,
                validationError: undefined
            }
        },
        methods: {

            /**
             * Opens station editor dialog. Creates new station if id not given
             * @param {String} id - Id of station to show
             * @returns {undefined}
             */
            open(id) {
                if (id) {
                    const stationIndex = Util.getStationIndex(this.stations, id);
                    if (stationIndex === -1) {
                        throw new Error("StationEditor: Open called with invalid station id");
                    }
                    const station = this.stations[stationIndex].clone();
                    // this.selectedStation = station.clone();
                    this.selectedStation = {
                        id: station.id,
                        title: station.title,
                        description: station.description,
                        source: station.source
                    }
                }
                else {
                    // Provisional station object structure to be validated only on addStation trigger by user
                    this.selectedStation = {
                        isNew: true,
                        id: "",
                        title: "",
                        description: "",
                        source: [
                            {
                                src: "",
                                type: ""
                            }
                        ]

                    };
                }
                this.$refs.dialog.open();
            },

            /**
             * Add selectedStation to stations array
             * Called by add button
             * @returns {undefined}
             */
            addStation() {
                // Reset error field
                this.validationError = undefined;
                // Create station id by lowercasing string and replacing space with underscore
                this.selectedStation.id = this.selectedStation.title.replace(" ", "_").toLowerCase();

                // Attempt to add it to stations array
                try {
                    Util.addStation(this.stations, this.selectedStation.id, this.selectedStation.title,
                        this.selectedStation.description, this.selectedStation.source);
                }
                catch (error) {
                    log.error("User attempted to add station but it failed", this.selectedStation, error);
                    this.validationError = error.message;
                    return;
                }
                this.$refs.dialog.close();
            },

            /**
             * Validates changes to currently selected station and applies them if valid
             * Called by save button
             * @returns {undefined}
             */
            saveStationEdit() {
                // Reset error field
                this.validationError = undefined;
                try {
                    const stationIndex = Util.getStationIndex(this.stations, this.selectedStation.id);
                    if (stationIndex === -1) {
                        log.error("Attempted to save station data but could not find station in array by id");
                    }
                    const dst = this.stations[stationIndex];
                    const src = this.selectedStation;
                    dst.id = src.id;
                    dst.title = src.title;
                    dst.description = src.description;
                    dst.source = src.source;
                }
                catch (error) {
                    log.error("User attempted to edit station but it failed", this.selectedStation, error);
                    this.validationError = error.message;
                    return;
                }
                this.$refs.dialog.close();
            },

            deleteHandler() {
                const index = Util.getStationIndex(this.stations, this.selectedStation.id);
                if (index !== -1) {
                    this.stations.splice(index, 1);
                    this.$refs.dialog.close();
                }
            },
        }
    })
}<|endoftext|>


<commit_before>var inherits = require('util').inherits;
var PassThrough = require('stream').PassThrough;
var Transform = require('stream').Transform;
var ShorthandList = require('./shorthandlist');
var optimist = require('./tools').optimist;
var FixedQueue = require('./fixedqueue');
var csv = require('csv');
var _ = require('underscore');

var operators = {
  incsv: function(args) {
    var argv = optimist()
      .options({
        d: {alias: 'delimiter', default: ',', string: true},
        p: {alias: 'escapechar', default: '\\', string: true},
        q: {alias: 'quotechar', default: '\"', string: true},
        t: {alias: 'tabs', boolean: true},
        H: {alias: 'no-header-row', boolean: true},
        S: {alias: 'skipinitialspace', boolean: true},
      })
      .string(['delimiter', 'escapechar', 'quotechar'])
      .parse(args)
    ;

    // by default, assume there is a header row
    headerrow = true;
    if (argv.H || argv['header-row'] === false) {
      headerrow = false;
    }

    if (argv.t) {
      // tabs switch overrides delimiter opt
      argv.d = '\t';
    }
    return csv()
      .from.options({
        delimiter: argv.d,
        escape: argv.p,
        quote: argv.q,
        ltrim: argv.S,
      })
      .transform(function(row, index) {
        return JSON.stringify({'row': row, 'index': index});
      });
  },

  // none transformation
  none: function(args) {
    PassThrough.call(this);
  },

  // return our HEAD transformation
  head: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .options('n', {
        default: 10,
      })
      .parse(args)
    ;

    this._number = argv.n;
    this._lines = 0;

    this._pushheaderrow = headerrow;
  },

  // return our tail transformation
  tail: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .options('n', {
        default: '10',
        string: true
      })
      .parse(args)
    ;

    var number = argv.n;

    if (number.charAt(0) == '+') {
      // relative to the beginning of the stream
      this._rel = 'beginning';
      this._lines = 0;
      this._number = number;
    } else {
      // relative to the end of the stream
      this._rel = 'end';
      if (number.charAt(0) == '-') {
        number = -number;
      }
      this._fixedqueue = new FixedQueue(number);
    }

    this._pushheaderrow = headerrow;
  },

  // return a transformation that will cut columns.
  // Accepts a comma separated list of column positions to remove.
  // This is 0-indexed.
  cut: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .boolean('complement')
      .parse(args)
    ;

    if (argv._.length < 1) {
      throw 'Error: cut requires at least 1 argument.';
    }

    var columns = argv._[0].toString();

    this._complement = argv.complement;

    this._cols = ShorthandList(columns);
  },

  // transformation that will grep for a pattern
  grep: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .options({
        c: {alias: 'columns', string: true},
        i: {alias: 'case-insensitive', boolean: true},
        e: {alias: 'regexp', string: true},
        v: {alias: 'invert-match', boolean: true},
      })
      .string(['columns', 'regexp'])
      .parse(args)
    ;

    if (argv.c !== undefined) {
      this._cols = ShorthandList(argv.c);
    }

    var flags = '';
    if (argv.i) {
      flags = 'i';
    }
    var regex;
    if (argv.e !== undefined) {
      regex = argv.e;
    } else {
      regex = argv._[0];
    }
    this._pattern = new RegExp(regex, flags);

    this._invert = argv.v;

    this._pushheaderrow = headerrow;
  },

  // return a transformation that will find and replace all
  // occurrences of a string
  replace: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .options({
        c: {alias: 'columns', string: true},
        r: {alias: 'regexp', boolean: true},
      })
      .string('columns')
      .parse(args)
    ;

    if (argv._.length < 1) {
      throw 'Error: replace requires at least 1 argument.';
    }

    if (argv.c !== undefined) {
      this._cols = ShorthandList(argv.c);
    }

    if (argv.r) {
      this._find = new RegExp(argv._[0], 'g');
    } else {
      this._find = argv._[0];
    }

    this._replace = '';
    if (argv._.length > 1) {
      this._replace = argv._[1];
    }

    this._pushheaderrow = headerrow;
  },

  // return a transformation that will delete empty rows
  strip: function(args) {
    Transform.call(this, {objectMode: true});

    this._pushheaderrow = headerrow;
  },

  // transformation that will delete rows
  delete: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .parse(args)
    ;

    if (argv._.length < 1) {
      throw 'Error: delete requires at least 1 argument.';
    }

    var shorthand = argv._[0].toString();
    this._shorthandlist = ShorthandList(shorthand);
    this._index = 0;

    this._pushheaderrow = headerrow;
  },

  outcsv: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .options({
        d: {alias: 'delimiter', default: ',', string: true},
        p: {alias: 'escapechar', default: '\\', string: true},
        q: {alias: 'quotechar', default: '\"', string: true},
        t: {alias: 'tabs', boolean: true},
        S: {alias: 'skipinitialspace', boolean: true},
      })
      .string(['delimiter', 'escapechar', 'quotechar'])
      .parse(args)
    ;
    if (argv.t) {
      // tabs switch overrides delimiter opt
      argv.d = '\t';
    }
    this._options = {
      delimiter: argv.d,
      escape: argv.p,
      quote: argv.q,
    };
    this._trimInitialSpace = argv.skipinitialspace;
  },

  // return our HTML transformation
  outhtml: function(args) {
    Transform.call(this, {objectMode: true});

    this._initialHtmlSent = false;
  },
};

inherits(operators.none, PassThrough);

inherits(operators.head, Transform);

operators.head.prototype._transform = function(chunk, encoding, done) {
  if (this._pushheaderrow) {
    this.push(chunk);
    this._pushheaderrow = false;
    return done();
  }

  if (this._lines < this._number) {
    this._lines += 1;
    this.push(chunk);
  } else {
    this.push(null);
  }

  done();
};

inherits(operators.tail, Transform);

operators.tail.prototype._transform = function(chunk, encoding, done) {
  if (this._pushheaderrow) {
    this.push(chunk);
    this._pushheaderrow = false;
    return done();
  }

  if (this._rel == 'end') {
    this._fixedqueue.push(chunk);
  } else {
    if (this._lines < this._number) {
      this._lines += 1;
    } else {
      this.push(chunk);
    }
  }
  done();
};

operators.tail.prototype._flush = function(done) {
  if (this._rel == 'end') {
    var chunk = this._fixedqueue.shift();
    while (chunk !== undefined) {
      this.push(chunk);
      chunk = this._fixedqueue.shift();
    }
  }
  done();
};

inherits(operators.cut, Transform);

operators.cut.prototype._transform = function(chunk, encoding, done) {
  var json = JSON.parse(chunk);

  if (this._expandedCols === undefined) {
    this._expandedCols = this._cols.expand(json.row.length);
    if (this._complement) {
      this._expandedCols = _.difference(_.range(json.row.length), this._expandedCols);
    }
  }

  _.each(this._expandedCols, function(position) {
    delete json.row[position];
  });

  json.row = _.without(json.row, undefined);

  this.push(JSON.stringify(json));

  done();
};

inherits(operators.grep, Transform);

operators.grep.prototype._transform = function(chunk, encoding, done) {
  if (this._pushheaderrow) {
    this.push(chunk);
    this._pushheaderrow = false;
    return done();
  }

  var json = JSON.parse(chunk);

  var self = this;

  var expandedCols;
  if (this._cols !== undefined) {
    expandedCols = this._cols.expand(json.row.length);
  } else {
    expandedCols = _.range(json.row.length);
  }
  var match = _.any(expandedCols, function(idx) {
    return self._pattern.test(json.row[idx]);
  });

  if (this._invert) {
    match = !match;
  }

  if (match) {
    this.push(chunk);
  }

  done();
};

inherits(operators.replace, Transform);

operators.replace.prototype._transform = function(chunk, encoding, done) {
  if (this._pushheaderrow) {
    this.push(chunk);
    this._pushheaderrow = false;
    return done();
  }

  var json = JSON.parse(chunk);

  var self = this;

  var expandedCols;
  if (this._cols !== undefined) {
    expandedCols = this._cols.expand(json.row.length);
  } else {
    expandedCols = _.range(json.row.length);
  }
  _.each(expandedCols, function(idx) {
    json.row[idx] = json.row[idx].replace(self._find, self._replace);
  });

  this.push(JSON.stringify(json));

  done();
};

inherits(operators.strip, Transform);

operators.strip.prototype._transform = function(chunk, encoding, done) {
  if (this._pushheaderrow) {
    this.push(chunk);
    this._pushheaderrow = false;
    return done();
  }

  var json = JSON.parse(chunk);

  var keep = _.some(json.row, function(val) {
    return val !== '';
  });

  if (keep) {
    this.push(chunk);
  }

  done();
};

inherits(operators.delete, Transform);

operators.delete.prototype._transform = function(chunk, encoding, done) {
  if (this._pushheaderrow) {
    this.push(chunk);
    this._pushheaderrow = false;
    return done();
  }

  if(this._shorthandlist === undefined || !this._shorthandlist.includes(this._index)) {
    this.push(chunk);
  }

  this._index += 1;
  done();
};

inherits(operators.outcsv, Transform);

operators.outcsv.prototype._transform = function(chunk, encoding, done) {
  var self = this;
  var row = JSON.parse(chunk).row;

  if (this._trimInitialSpace) {
    row = _.each(function(item) {
      return item.trimLeft(item);
    });
  }

  csv()
    .from.array([row])
    .to.string(function(data){
      self.push(data + '\n');
      done();
    }, self._options)
  ;
};

operators.outcsv.prototype.contentType = function() {
  return "text/plain; charset=utf-8";
};

inherits(operators.outhtml, Transform);

operators.outhtml.prototype._transform = function(chunk, encoding, done) {
  var json = JSON.parse(chunk);
  var self = this;

  if (!this._initialHtmlSent) {
    this.push('');
    this.push('');
    this.push('<link rel="stylesheet" href="/css/style.css" />');
    this.push('');
    this.push('');
    this.push('
');
    this.push('
');
    if (headerrow) {
      this.push('
	');
      json.row.forEach(function(item) {
        self.push('%s	'.replace(/%s/g, item));
      });
      this.push('

');

      this._initialHtmlSent = true;

      return done();
    } else {
      this.push('

');
      this._initialHtmlSent = true;
    }
  }

  var index = json.index;
  if (headerrow) {
    // the header row was passed straight through
    // (without incrementing any counters) so we have
    // to correct for that here.
    index--;
  }

  this.push('
%s	'.replace(/%s/g, index));
  json.row.forEach(function(item) {
    self.push('
' + item + '
	');
  });
  this.push('
');

  done();
};

operators.outhtml.prototype._flush = function(done) {
  this.push('
');
  this.push('
');
  this.push('');
  this.push('');
  this.push('');
  this.push('');

  done();
};

operators.outhtml.prototype.contentType = function() {
  return "text/html; charset=utf-8";
};

module.exports = operators;
<commit_msg>Add some javascripty shorthands<commit_after>var inherits = require('util').inherits;
var PassThrough = require('stream').PassThrough;
var Transform = require('stream').Transform;
var ShorthandList = require('./shorthandlist');
var optimist = require('./tools').optimist;
var FixedQueue = require('./fixedqueue');
var csv = require('csv');
var _ = require('underscore');

var operators = {
  incsv: function(args) {
    var argv = optimist()
      .options({
        d: {alias: 'delimiter', default: ',', string: true},
        p: {alias: 'escapechar', default: '\\', string: true},
        q: {alias: 'quotechar', default: '\"', string: true},
        t: {alias: 'tabs', boolean: true},
        H: {alias: 'no-header-row', boolean: true},
        S: {alias: 'skipinitialspace', boolean: true},
      })
      .string(['delimiter', 'escapechar', 'quotechar'])
      .parse(args)
    ;

    // by default, assume there is a header row
    headerrow = true;
    if (argv.H || argv['header-row'] === false) {
      headerrow = false;
    }

    if (argv.t) {
      // tabs switch overrides delimiter opt
      argv.d = '\t';
    }
    return csv()
      .from.options({
        delimiter: argv.d,
        escape: argv.p,
        quote: argv.q,
        ltrim: argv.S,
      })
      .transform(function(row, index) {
        return JSON.stringify({'row': row, 'index': index});
      });
  },

  // none transformation
  none: function(args) {
    PassThrough.call(this);
  },

  // return our HEAD transformation
  head: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .options('n', {
        default: 10,
      })
      .parse(args)
    ;

    this._number = argv.n;
    this._lines = 0;

    this._pushheaderrow = headerrow;
  },

  // return our tail transformation
  tail: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .options('n', {
        default: '10',
        string: true
      })
      .parse(args)
    ;

    var number = argv.n;

    if (number.charAt(0) == '+') {
      // relative to the beginning of the stream
      this._rel = 'beginning';
      this._lines = 0;
      this._number = number;
    } else {
      // relative to the end of the stream
      this._rel = 'end';
      if (number.charAt(0) == '-') {
        number = -number;
      }
      this._fixedqueue = new FixedQueue(number);
    }

    this._pushheaderrow = headerrow;
  },

  // return a transformation that will cut columns.
  // Accepts a comma separated list of column positions to remove.
  // This is 0-indexed.
  cut: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .boolean('complement')
      .parse(args)
    ;

    if (argv._.length < 1) {
      throw 'Error: cut requires at least 1 argument.';
    }

    var columns = argv._[0].toString();

    this._complement = argv.complement;

    this._cols = ShorthandList(columns);
  },

  // transformation that will grep for a pattern
  grep: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .options({
        c: {alias: 'columns', string: true},
        i: {alias: 'case-insensitive', boolean: true},
        e: {alias: 'regexp', string: true},
        v: {alias: 'invert-match', boolean: true},
      })
      .string(['columns', 'regexp'])
      .parse(args)
    ;

    if (argv.c !== undefined) {
      this._cols = ShorthandList(argv.c);
    }

    var flags = (argv.i) ? 'i' : '';
    var regex = argv.e || argv._[0];
    this._pattern = new RegExp(regex, flags);

    this._invert = argv.v;

    this._pushheaderrow = headerrow;
  },

  // return a transformation that will find and replace all
  // occurrences of a string
  replace: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .options({
        c: {alias: 'columns', string: true},
        r: {alias: 'regexp', boolean: true},
      })
      .string('columns')
      .parse(args)
    ;

    if (argv._.length < 1) {
      throw 'Error: replace requires at least 1 argument.';
    }

    if (argv.c !== undefined) {
      this._cols = ShorthandList(argv.c);
    }

    this._find = (argv.r) ? new RegExp(argv._[0], 'g') : argv._[0];
    this._replace = (argv._.length > 1) ? argv._[1] : '';

    this._pushheaderrow = headerrow;
  },

  // return a transformation that will delete empty rows
  strip: function(args) {
    Transform.call(this, {objectMode: true});

    this._pushheaderrow = headerrow;
  },

  // transformation that will delete rows
  delete: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .parse(args)
    ;

    if (argv._.length < 1) {
      throw 'Error: delete requires at least 1 argument.';
    }

    var shorthand = argv._[0].toString();
    this._shorthandlist = ShorthandList(shorthand);
    this._index = 0;

    this._pushheaderrow = headerrow;
  },

  outcsv: function(args) {
    Transform.call(this, {objectMode: true});

    var argv = optimist()
      .options({
        d: {alias: 'delimiter', default: ',', string: true},
        p: {alias: 'escapechar', default: '\\', string: true},
        q: {alias: 'quotechar', default: '\"', string: true},
        t: {alias: 'tabs', boolean: true},
        S: {alias: 'skipinitialspace', boolean: true},
      })
      .string(['delimiter', 'escapechar', 'quotechar'])
      .parse(args)
    ;
    if (argv.t) {
      // tabs switch overrides delimiter opt
      argv.d = '\t';
    }
    this._options = {
      delimiter: argv.d,
      escape: argv.p,
      quote: argv.q,
    };
    this._trimInitialSpace = argv.skipinitialspace;
  },

  // return our HTML transformation
  outhtml: function(args) {
    Transform.call(this, {objectMode: true});

    this._initialHtmlSent = false;
  },
};

inherits(operators.none, PassThrough);

inherits(operators.head, Transform);

operators.head.prototype._transform = function(chunk, encoding, done) {
  if (this._pushheaderrow) {
    this.push(chunk);
    this._pushheaderrow = false;
    return done();
  }

  if (this._lines < this._number) {
    this._lines += 1;
    this.push(chunk);
  } else {
    this.push(null);
  }

  done();
};

inherits(operators.tail, Transform);

operators.tail.prototype._transform = function(chunk, encoding, done) {
  if (this._pushheaderrow) {
    this.push(chunk);
    this._pushheaderrow = false;
    return done();
  }

  if (this._rel == 'end') {
    this._fixedqueue.push(chunk);
  } else {
    if (this._lines < this._number) {
      this._lines += 1;
    } else {
      this.push(chunk);
    }
  }
  done();
};

operators.tail.prototype._flush = function(done) {
  if (this._rel == 'end') {
    var chunk = this._fixedqueue.shift();
    while (chunk !== undefined) {
      this.push(chunk);
      chunk = this._fixedqueue.shift();
    }
  }
  done();
};

inherits(operators.cut, Transform);

operators.cut.prototype._transform = function(chunk, encoding, done) {
  var json = JSON.parse(chunk);

  if (this._expandedCols === undefined) {
    this._expandedCols = this._cols.expand(json.row.length);
    if (this._complement) {
      this._expandedCols = _.difference(_.range(json.row.length), this._expandedCols);
    }
  }

  _.each(this._expandedCols, function(position) {
    delete json.row[position];
  });

  json.row = _.without(json.row, undefined);

  this.push(JSON.stringify(json));

  done();
};

inherits(operators.grep, Transform);

operators.grep.prototype._transform = function(chunk, encoding, done) {
  if (this._pushheaderrow) {
    this.push(chunk);
    this._pushheaderrow = false;
    return done();
  }

  var json = JSON.parse(chunk);

  var self = this;

  var expandedCols = (this._cols !== undefined) ? this._cols.expand(json.row.length) : _.range(json.row.length);
  var match = _.any(expandedCols, function(idx) {
    return self._pattern.test(json.row[idx]);
  });

  if (this._invert) match = !match;

  if (match) this.push(chunk);

  done();
};

inherits(operators.replace, Transform);

operators.replace.prototype._transform = function(chunk, encoding, done) {
  if (this._pushheaderrow) {
    this.push(chunk);
    this._pushheaderrow = false;
    return done();
  }

  var json = JSON.parse(chunk);

  var self = this;

  var expandedCols;
  if (this._cols !== undefined) {
    expandedCols = this._cols.expand(json.row.length);
  } else {
    expandedCols = _.range(json.row.length);
  }
  _.each(expandedCols, function(idx) {
    json.row[idx] = json.row[idx].replace(self._find, self._replace);
  });

  this.push(JSON.stringify(json));

  done();
};

inherits(operators.strip, Transform);

operators.strip.prototype._transform = function(chunk, encoding, done) {
  if (this._pushheaderrow) {
    this.push(chunk);
    this._pushheaderrow = false;
    return done();
  }

  var json = JSON.parse(chunk);

  var keep = _.some(json.row, function(val) {
    return val !== '';
  });

  if (keep) this.push(chunk);

  done();
};

inherits(operators.delete, Transform);

operators.delete.prototype._transform = function(chunk, encoding, done) {
  if (this._pushheaderrow) {
    this.push(chunk);
    this._pushheaderrow = false;
    return done();
  }

  if(this._shorthandlist === undefined || !this._shorthandlist.includes(this._index)) {
    this.push(chunk);
  }

  this._index += 1;
  done();
};

inherits(operators.outcsv, Transform);

operators.outcsv.prototype._transform = function(chunk, encoding, done) {
  var self = this;
  var row = JSON.parse(chunk).row;

  if (this._trimInitialSpace) {
    row = _.each(function(item) {
      return item.trimLeft(item);
    });
  }

  csv()
    .from.array([row])
    .to.string(function(data){
      self.push(data + '\n');
      done();
    }, self._options)
  ;
};

operators.outcsv.prototype.contentType = function() {
  return "text/plain; charset=utf-8";
};

inherits(operators.outhtml, Transform);

operators.outhtml.prototype._transform = function(chunk, encoding, done) {
  var json = JSON.parse(chunk);
  var self = this;

  if (!this._initialHtmlSent) {
    this.push('');
    this.push('');
    this.push('<link rel="stylesheet" href="/css/style.css" />');
    this.push('');
    this.push('');
    this.push('
');
    this.push('
');
    if (headerrow) {
      this.push('
	');
      json.row.forEach(function(item) {
        self.push('%s	'.replace(/%s/g, item));
      });
      this.push('

');

      this._initialHtmlSent = true;

      return done();
    } else {
      this.push('

');
      this._initialHtmlSent = true;
    }
  }

  var index = json.index;
  if (headerrow) {
    // the header row was passed straight through
    // (without incrementing any counters) so we have
    // to correct for that here.
    index--;
  }

  this.push('
%s	'.replace(/%s/g, index));
  json.row.forEach(function(item) {
    self.push('
' + item + '
	');
  });
  this.push('
');

  done();
};

operators.outhtml.prototype._flush = function(done) {
  this.push('
');
  this.push('
');
  this.push('');
  this.push('');
  this.push('');
  this.push('');

  done();
};

operators.outhtml.prototype.contentType = function() {
  return "text/html; charset=utf-8";
};

module.exports = operators;
<|endoftext|>


<commit_before>$.getJSON("assets/english.json", function(data){
  var treasures = data.treasures;
  $(document).ready(function(){
    for (var i in treasures)
    {
      opt = $('<option/>', {value: treasures[i].id, text: treasures[i].name});
      $('#select-item').append(opt);
    }
  });
});
<commit_msg>Update index.js<commit_after>$.getJSON("assets/english.json", function(data){
  var treasures = data.treasures;
  $(document).ready(function(){
    treasures.sort(function(a, b) {
        return a.name - b.name;
    });
    for (var i in treasures)
    {
      names.push(treasures[i].name);
      opt = $('<option/>', {value: treasures[i].id, text: treasures[i].name});
      $('#select-item').append(opt);
    }
  });
});
<|endoftext|>


<commit_before>var selectASharedMapChoiceText = 'Select a shared map';

$(document).ready(function() {
    initialiseAddMapPopup();
});

var initialiseAddMapPopup = function() {
    var $mapSelectionContainer = $('#map-selection-container');
    var $sharedMapSelectionButton = $('#sharedMapSelectionButton');
    $sharedMapSelectionButton.text(selectASharedMapChoiceText);
    $sharedMapSelectionButton.click(handleSharedMapSelectionButton);
    var $actionLink = $('#action-a');
    $actionLink.click(handleSharedMapSelectionLink);
    var $actionLinks = $('.shared-map-choice');
    $actionLinks.click(handleSharedMapSelectionLink);
    $mapSelectionContainer.removeClass('hidden');

    var teamKey = 'demo';
    getFreshMapInfo(teamKey)
    .then(function(sharedMapInfo) {
        //var sharedMapInfoJson = JSON.stringify(sharedMapInfo);
        if (sharedMapInfo) {
            var $sharedMapsDropdown = $('#shared-maps-dropdown');
            for (var index = 0; index < sharedMapInfo.mapNames.length; index++) {
                var mapName = sharedMapInfo.mapNames[index];
                // None
                var $mapOption = $('a', {
                    //id: "foo",
                    "class": "dropdown-item shared-map-choice",
                    href: '#',
                    text: mapName
                });
                $mapOption.appendTo($sharedMapsDropdown);

            }
            $sharedMapSelectionButton.attr('aria-expanded', 'false');
            $sharedMapsDropdown.dropdown();
        }
    });
};

var handleSharedMapSelectionLink = function(event) {
    event.preventDefault();
    var $sharedMapSelectionButton = $('#sharedMapSelectionButton');
    var sharedMapOption = event.target;
    var sharedMapName = sharedMapOption.innerText;
    $sharedMapSelectionButton.text(sharedMapName);

    getSharedMapPopupItemsUsingPromise().then(function() {

    });

};

var handleSharedMapSelectionButton = function(event) {

};
<commit_msg>update<commit_after>var selectASharedMapChoiceText = 'Select a shared map';

$(document).ready(function() {
    initialiseAddMapPopup();
});

var initialiseAddMapPopup = function() {
    var $mapSelectionContainer = $('#map-selection-container');
    var $sharedMapSelectionButton = $('#sharedMapSelectionButton');
    $sharedMapSelectionButton.text(selectASharedMapChoiceText);
    $sharedMapSelectionButton.click(handleSharedMapSelectionButton);
    var $actionLink = $('#action-a');
    $actionLink.click(handleSharedMapSelectionLink);
    var $actionLinks = $('.shared-map-choice');
    $actionLinks.click(handleSharedMapSelectionLink);
    $mapSelectionContainer.removeClass('hidden');

    var teamKey = 'demo';
    getFreshMapInfo(teamKey)
    .then(function(sharedMapInfo) {
        //var sharedMapInfoJson = JSON.stringify(sharedMapInfo);
        if (sharedMapInfo) {
            var $sharedMapsDropdown = $('#shared-maps-dropdown');
            for (var index = 0; index < sharedMapInfo.mapNames.length; index++) {
                var mapName = sharedMapInfo.mapNames[index];
                // None
                var $mapOption = $('', {
                    //id: "foo",
                    "class": "dropdown-item shared-map-choice",
                    href: '#',
                    text: mapName
                });
                //$mapOption.appendTo($sharedMapsDropdown);
                $sharedMapsDropdown.append($mapOption);

            }
            $sharedMapSelectionButton.attr('aria-expanded', 'false');
            $sharedMapsDropdown.dropdown();
        }
    });
};

var handleSharedMapSelectionLink = function(event) {
    event.preventDefault();
    var $sharedMapSelectionButton = $('#sharedMapSelectionButton');
    var sharedMapOption = event.target;
    var sharedMapName = sharedMapOption.innerText;
    $sharedMapSelectionButton.text(sharedMapName);

    getSharedMapPopupItemsUsingPromise().then(function() {

    });

};

var handleSharedMapSelectionButton = function(event) {

};
<|endoftext|>


<commit_before>const rootVm = new Vue({
    el: '#app-sudoku',
    template: `

 event.target.focus()"
    >

                        {{ cell.value }}
                        {{ cell.value }}

 set ? i + 1 : ''), 3)">
                            {{ n }}

        <historical-event :event="command" :key="index" :active="history.commands.length - index === history.current + 1"
        v-for="(command, index) in Array.from(history.commands).reverse()" />

initial

`,
    components: {
        'historical-event': {
            props: { event: Object, active: Boolean, },
            template: '
{{ place + " " + event.type + " " + event.value }}
',
            computed: {
                place: function() {
                    const where = this.event.where;
                    return '(' + (~~(where / 9) + 1) + ',' + (where % 9 + 1) + ')';
                },
            },
        }
    },
    computed: {
        affectedIndices: function() {
            const get = array => {
                const index = array.findIndex(x => x === this.pointed);
                const start = 9 * ~~(index / 9);
                return array.slice(start, start + (index !== -1 ? 9 : 0));
            };
            return [get(this.indices.row), get(this.indices.col), get(this.indices.box)]
            .reduce((a, b) => a.concat(b)) // flatten array
            .filter(value => value !== this.pointed) // remove pointed itself
            .filter((value, index, array) => array.indexOf(value) === index); // remove duplicates
        },
        boxes: function() {
            return this.slice(this.indices.box.map(index => this.cells[index]), 9);
        },
        cells: function() {
            /* manage each number cell by the object: {
                {Boolean} given, -- true if the number is given for clue, otherwise false.
                {Number} value, -- a number between 0 to 9.
                {Array} memo, -- array of Boolean, each element represent the number those of which can be placed, user considers.
            } */
            const initial = Array.from(
                '060003001200500600007090500000400090800000006010005000002010700004009003700200040' // 朝日新聞beパズル 2017/10/07 掲載分
            ).map(Number).map(n => new Object({given: Boolean(n), value: n, memo: Array(9), }));
            const commands = this.history.commands.slice(0, this.history.current + 1);
            return commands.reduce((board, command) => {
                const target = board[command.where];
                switch(command.type) {
                case 'place':
                    target.value = command.value;
                    break;
                case 'remark':
                    target.memo[command.value - 1] = true;
                    break;
                case 'unmark':
                    target.memo[command.value - 1] = false;
                    break;
                case 'flush':
                    command.where.forEach(index => board[index].memo[command.value - 1] = false);
                    break;
                }
                return board;
            }, initial);
        },
        missPlacedIndices: function() {
            const unique = (value, index, array) => array.indexOf(value) === index;
            const indices = [this.indices.row, this.indices.col, this.indices.box].reduce((a, b) => a.concat(b));
            const dupOrNot = this.slice(indices.map(index => this.cells[index].value), 9) // for each 9...
            .map(vs => vs.map((v, i, self) => v && self.slice(0, i).concat(self.slice(i+1)).some(x => x === v))) // flag non-zero and duplicates
            .reduce((a, b) => a.concat(b));
            return dupOrNot.map((v, i) => v ? indices[i] : NaN).filter(v => v !== NaN).filter(unique);
        },
        indices: (() => {
            const colNr = index => index % 9;
            const boxNr = index => 3 * ~~(index / 27) + ~~((index % 9) / 3);
            const row = Array.from({length: 81, }).map((_, index) => index);
            const col = Array.from(row).sort((a, b) => colNr(a) - colNr(b));
            const box = Array.from(row).sort((a, b) => boxNr(a) - boxNr(b));
            const indices = { row: row, col: col, box: box, };
            return () => indices;
        })(),
        pointedCell: function() { return this.cells[this.pointed] || {given: true, }; },
    },
    data: () => new Object({
        pointed: -1,
        /* user operations. this backs undo/redo up */
        history: {commands: [], current: -1, },
    }),
    methods: {
        slice: (() => {
            const splitAt = (array, n) => [array.slice(0, n), array.slice(n)];
            const reduceConcat = a => a.length ? a.reduce((a, b) => [a].concat(reduceConcat(b))) : [];

            return (array, n) => {
                const splitted = splitAt(array, n);
                var p = splitted;
                while (p[1].length !== 0) {
                    p[1] = splitAt(p[1], n);
                    p = p[1];
                }
                return reduceConcat(splitted);
            };
        })(),
        onKey: function(event) {
            const match = /^Digit(.)$/.exec(event.code);
            if (match && this.pointed !== -1) {
                const value = Number(match[1]);
                if (event.altKey) {
                    const type = event.shiftKey ? 'unmark' : 'remark';
                    this.history.commands.push({type: type, value: value, where: this.pointed, when: new Date(), });
                    this.history.current += 1;
                } else if (!this.pointedCell.given) {
                    this.history.commands.push({type: 'place', value: value, where: this.pointed, when: new Date(), });
                    this.history.current += 1;
                }
            } else if (event.code === 'Backspace' && !this.pointedCell.given) {
                this.history.commands.push({type: 'place', value: 0, where: this.pointed, when: new Date(), });
                this.history.current += 1;
            }
        },
        flush: function() {
            const history = this.history;
            history.commands.push({type: 'flush', value: this.pointedCell.value, where: this.affectedIndices, when: new Date(), });
            history.current += 1;
        },
    },
});
<commit_msg>Implement Undo/Redo.<commit_after>const rootVm = new Vue({
    el: '#app-sudoku',
    template: `

 event.target.focus()"
    >

                        {{ cell.value }}
                        {{ cell.value }}

 set ? i + 1 : ''), 3)">
                            {{ n }}

        <historical-event :event="command" :key="index" :active="history.commands.length - index === history.current + 1"
        v-for="(command, index) in Array.from(history.commands).reverse()" />

initial

`,
    components: {
        'historical-event': {
            props: { event: Object, active: Boolean, },
            template: '
{{ place + " " + event.type + " " + event.value }}
',
            computed: {
                place: function() {
                    const where = this.event.where;
                    return '(' + (~~(where / 9) + 1) + ',' + (where % 9 + 1) + ')';
                },
            },
        }
    },
    computed: {
        affectedIndices: function() {
            const get = array => {
                const index = array.findIndex(x => x === this.pointed);
                const start = 9 * ~~(index / 9);
                return array.slice(start, start + (index !== -1 ? 9 : 0));
            };
            return [get(this.indices.row), get(this.indices.col), get(this.indices.box)]
            .reduce((a, b) => a.concat(b)) // flatten array
            .filter(value => value !== this.pointed) // remove pointed itself
            .filter((value, index, array) => array.indexOf(value) === index); // remove duplicates
        },
        boxes: function() {
            return this.slice(this.indices.box.map(index => this.cells[index]), 9);
        },
        cells: function() {
            /* manage each number cell by the object: {
                {Boolean} given, -- true if the number is given for clue, otherwise false.
                {Number} value, -- a number between 0 to 9.
                {Array} memo, -- array of Boolean, each element represent the number those of which can be placed, user considers.
            } */
            const initial = Array.from(
                '060003001200500600007090500000400090800000006010005000002010700004009003700200040' // 朝日新聞beパズル 2017/10/07 掲載分
            ).map(Number).map(n => new Object({given: Boolean(n), value: n, memo: Array(9), }));
            const commands = this.history.commands.slice(0, this.history.current + 1);
            return commands.reduce((board, command) => {
                const target = board[command.where];
                switch(command.type) {
                case 'place':
                    target.value = command.value;
                    break;
                case 'remark':
                    target.memo[command.value - 1] = true;
                    break;
                case 'unmark':
                    target.memo[command.value - 1] = false;
                    break;
                case 'flush':
                    command.where.forEach(index => board[index].memo[command.value - 1] = false);
                    break;
                }
                return board;
            }, initial);
        },
        missPlacedIndices: function() {
            const unique = (value, index, array) => array.indexOf(value) === index;
            const indices = [this.indices.row, this.indices.col, this.indices.box].reduce((a, b) => a.concat(b));
            const dupOrNot = this.slice(indices.map(index => this.cells[index].value), 9) // for each 9...
            .map(vs => vs.map((v, i, self) => v && self.slice(0, i).concat(self.slice(i+1)).some(x => x === v))) // flag non-zero and duplicates
            .reduce((a, b) => a.concat(b));
            return dupOrNot.map((v, i) => v ? indices[i] : NaN).filter(v => v !== NaN).filter(unique);
        },
        indices: (() => {
            const colNr = index => index % 9;
            const boxNr = index => 3 * ~~(index / 27) + ~~((index % 9) / 3);
            const row = Array.from({length: 81, }).map((_, index) => index);
            const col = Array.from(row).sort((a, b) => colNr(a) - colNr(b));
            const box = Array.from(row).sort((a, b) => boxNr(a) - boxNr(b));
            const indices = { row: row, col: col, box: box, };
            return () => indices;
        })(),
        pointedCell: function() { return this.cells[this.pointed] || {given: true, }; },
    },
    data: () => new Object({
        pointed: -1,
        /* user operations. this backs undo/redo up */
        history: {commands: [], current: -1, },
    }),
    methods: {
        slice: (() => {
            const splitAt = (array, n) => [array.slice(0, n), array.slice(n)];
            const reduceConcat = a => a.length ? a.reduce((a, b) => [a].concat(reduceConcat(b))) : [];

            return (array, n) => {
                const splitted = splitAt(array, n);
                var p = splitted;
                while (p[1].length !== 0) {
                    p[1] = splitAt(p[1], n);
                    p = p[1];
                }
                return reduceConcat(splitted);
            };
        })(),
        onKey: function(event) {
            const undo = event.code === 'KeyZ' && event.altKey && !event.shiftKey;
            const redo = (event.code === 'KeyZ' && event.altKey && event.shiftKey) || (event.code === 'KeyY' && event.altKey && !event.shiftKey);
            const match = /^Digit(.)$/.exec(event.code);
            if (match && this.pointed !== -1) {
                const value = Number(match[1]);
                if (event.altKey) {
                    const type = event.shiftKey ? 'unmark' : 'remark';
                    this.history.commands.push({type: type, value: value, where: this.pointed, when: new Date(), });
                    this.history.current += 1;
                } else if (!this.pointedCell.given) {
                    this.history.commands.push({type: 'place', value: value, where: this.pointed, when: new Date(), });
                    this.history.current += 1;
                }
            } else if (event.code === 'Backspace' && !this.pointedCell.given) {
                this.history.commands.push({type: 'place', value: 0, where: this.pointed, when: new Date(), });
                this.history.current += 1;
            } else if (undo && this.history.current !== -1) {
                this.history.current -= 1;
            } else if (redo && this.history.current != this.history.commands.length - 1) {
                this.history.current += 1;
            }
        },
        flush: function() {
            const history = this.history;
            history.commands.push({type: 'flush', value: this.pointedCell.value, where: this.affectedIndices, when: new Date(), });
            history.current += 1;
        },
    },
});
<|endoftext|>


<commit_before>"use strict";

const ccxt = require ('ccxt')
    , WsClient = require ('./WsClient')
    , {
        OrderBook,
        LimitedOrderBook,
        IndexedOrderBook,
        LimitedIndexedOrderBook,
        LimitedCountedOrderBook,
        CountedOrderBook,
    } = require ('./OrderBook')

module.exports = class Exchange extends ccxt.Exchange {

    orderbook (snapshot = {}) {
        return new OrderBook (snapshot)
    }

    limitedOrderBook (snapshot = {}, depth = undefined) {
        return new LimitedOrderBook (snapshot, depth)
    }

    indexedOrderBook (snapshot = {}) {
        return new IndexedOrderBook (snapshot)
    }

    limitedIndexedOrderBook (snapshot = {}, depth = undefined) {
        return new LimitedIndexedOrderBook (snapshot, depth)
    }

    limitedCountedOrderBook (snapshot = {}, depth = undefined) {
        return new LimitedCountedOrderBook (snapshot, depth)
    }

    countedOrderBook (snapshot = {}) {
        return new CountedOrderBook (snapshot)
    }

    client (url) {
        this.clients = this.clients || {}
        if (!this.clients[url]) {
            const onMessage = this.handleMessage.bind (this)
            const onError = this.onError.bind (this)
            const onClose = this.onClose.bind (this)
            // decide client type here: ws / signalr / socketio
            this.clients[url] = new WsClient (url, onMessage, onError, onClose)
        }
        return this.clients[url]
    }

    async after (future, method, ... args) {
        const result = await future
        // call it as an instance method on this
        return method.apply (this, [ result, ... args ])
    }

    watch (url, messageHash, message = undefined, subscribeHash = undefined) {
        //
        // Without comments the code of this method is short and easy:
        //
        //     const client = this.client (url)
        //     const backoffDelay = 0
        //     const future = client.future (messageHash)
        //     const connected = client.connect (backoffDelay)
        //     connected.then (() => {
        //         if (message && !client.subscriptions[subscribeHash]) {
        //             client.subscriptions[subscribeHash] = true
        //             message = this.signMessage (client, messageHash, message)
        //             client.send (message)
        //         }
        //     }).catch ((error) => {})
        //     return future
        //
        // The following is a longer version of this method with comments
        //
        const client = this.client (url)
        // todo: calculate the backoff using the clients cache
        const backoffDelay = 0
        //
        //  watchOrderBook ---- future ----+---------------+----→ user
        //                                 |               |
        //                                 ↓               ↑
        //                                 |               |
        //                              connect ......→ resolve
        //                                 |               |
        //                                 ↓               ↑
        //                                 |               |
        //                             subscribe -----→ receive
        //
        const future = client.future (messageHash)
        // we intentionally do not use await here to avoid unhandled exceptions
        // the policy is to make sure that 100% of promises are resolved or rejected
        // either with a call to client.resolve or client.reject with
        //  a proper exception class instance
        const connected = client.connect (backoffDelay)
        // the following is executed only if the catch-clause does not
        // catch any connection-level exceptions from the client
        // (connection established successfully)
        connected.then (() => {
            if (message && !client.subscriptions[subscribeHash]) {
                client.subscriptions[subscribeHash] = true
                // todo: decouple signing from subscriptions
                message = this.signMessage (client, messageHash, message)
                client.send (message)
            }
        }).catch ((error) => {
            // we do nothing and don't return a resolvable value from here
            // we leave it in a rejected state to avoid triggering the
            // then-clauses that will follow (if any)
            // removing this catch will raise UnhandledPromiseRejection in JS
            // upon connection failure
        })
        return future
    }

    onError (client, error) {
        if ((client.url in this.clients) && (this.clients[client.url].error)) {
            delete this.clients[client.url]
        }
    }

    onClose (client, error) {
        if (client.error) {
            // connection closed due to an error, do nothing
        } else {
            // server disconnected a working connection
            if (this.clients[client.url]) {
                delete this.clients[client.url]
            }
        }
    }

    async close () {
        const clients = Object.values (this.clients || {})
        for (let i = 0; i < clients.length; i++) {
            const client = clients[i]
            delete this.clients[client.url]
            await client.close ()
        }
    }
}
<commit_msg>Exchange.js .call, .callAsync<commit_after>"use strict";

const ccxt = require ('ccxt')
    , WsClient = require ('./WsClient')
    , {
        OrderBook,
        LimitedOrderBook,
        IndexedOrderBook,
        LimitedIndexedOrderBook,
        LimitedCountedOrderBook,
        CountedOrderBook,
    } = require ('./OrderBook')

module.exports = class Exchange extends ccxt.Exchange {

    orderbook (snapshot = {}) {
        return new OrderBook (snapshot)
    }

    limitedOrderBook (snapshot = {}, depth = undefined) {
        return new LimitedOrderBook (snapshot, depth)
    }

    indexedOrderBook (snapshot = {}) {
        return new IndexedOrderBook (snapshot)
    }

    limitedIndexedOrderBook (snapshot = {}, depth = undefined) {
        return new LimitedIndexedOrderBook (snapshot, depth)
    }

    limitedCountedOrderBook (snapshot = {}, depth = undefined) {
        return new LimitedCountedOrderBook (snapshot, depth)
    }

    countedOrderBook (snapshot = {}) {
        return new CountedOrderBook (snapshot)
    }

    client (url) {
        this.clients = this.clients || {}
        if (!this.clients[url]) {
            const onMessage = this.handleMessage.bind (this)
            const onError = this.onError.bind (this)
            const onClose = this.onClose.bind (this)
            // decide client type here: ws / signalr / socketio
            this.clients[url] = new WsClient (url, onMessage, onError, onClose)
        }
        return this.clients[url]
    }

    call (method, ... args) {
        return method.apply (this, args)
    }

    callAsync (method, ... args) {
        return method.apply (this, args)
    }

    async after (future, method, ... args) {
        const result = await future
        // call it as an instance method on this
        return method.apply (this, [ result, ... args ])
    }

    watch (url, messageHash, message = undefined, subscribeHash = undefined) {
        //
        // Without comments the code of this method is short and easy:
        //
        //     const client = this.client (url)
        //     const backoffDelay = 0
        //     const future = client.future (messageHash)
        //     const connected = client.connect (backoffDelay)
        //     connected.then (() => {
        //         if (message && !client.subscriptions[subscribeHash]) {
        //             client.subscriptions[subscribeHash] = true
        //             message = this.signMessage (client, messageHash, message)
        //             client.send (message)
        //         }
        //     }).catch ((error) => {})
        //     return future
        //
        // The following is a longer version of this method with comments
        //
        const client = this.client (url)
        // todo: calculate the backoff using the clients cache
        const backoffDelay = 0
        //
        //  watchOrderBook ---- future ----+---------------+----→ user
        //                                 |               |
        //                                 ↓               ↑
        //                                 |               |
        //                              connect ......→ resolve
        //                                 |               |
        //                                 ↓               ↑
        //                                 |               |
        //                             subscribe -----→ receive
        //
        const future = client.future (messageHash)
        // we intentionally do not use await here to avoid unhandled exceptions
        // the policy is to make sure that 100% of promises are resolved or rejected
        // either with a call to client.resolve or client.reject with
        //  a proper exception class instance
        const connected = client.connect (backoffDelay)
        // the following is executed only if the catch-clause does not
        // catch any connection-level exceptions from the client
        // (connection established successfully)
        connected.then (() => {
            if (message && !client.subscriptions[subscribeHash]) {
                client.subscriptions[subscribeHash] = true
                // todo: decouple signing from subscriptions
                message = this.signMessage (client, messageHash, message)
                client.send (message)
            }
        }).catch ((error) => {
            // we do nothing and don't return a resolvable value from here
            // we leave it in a rejected state to avoid triggering the
            // then-clauses that will follow (if any)
            // removing this catch will raise UnhandledPromiseRejection in JS
            // upon connection failure
        })
        return future
    }

    onError (client, error) {
        if ((client.url in this.clients) && (this.clients[client.url].error)) {
            delete this.clients[client.url]
        }
    }

    onClose (client, error) {
        if (client.error) {
            // connection closed due to an error, do nothing
        } else {
            // server disconnected a working connection
            if (this.clients[client.url]) {
                delete this.clients[client.url]
            }
        }
    }

    async close () {
        const clients = Object.values (this.clients || {})
        for (let i = 0; i < clients.length; i++) {
            const client = clients[i]
            delete this.clients[client.url]
            await client.close ()
        }
    }
}
<|endoftext|>


<commit_before>$(function(){
 FastClick.attach(document.body);

  //二维数组
  var matrixArray = new Array();
  var count = 2;
  refreshUI();
  refreshMatrix();

  //倒计时
  var countdown = 2,
  $time = $('#countdown');

  function timedCount()
  {
    $time.html(countdown);
    countdown--;
    if(countdown > 0)
      setTimeout(timedCount, 1000);
    else{
      $time.html(countdown);
      isSuccess = true;
      $('#result').html(count-1);
      $('body').addClass('modal-open');
      $('#myModal').addClass('in');
      $('.modal-backdrop').addClass('in');
    }
  }

  timedCount();

  var row, col, isSuccess = false;

  $('#panel').on('click', 'td', function(e){
    row = parseInt($(this).data('y')) + 1;
    col = parseInt($(this).data('x')) + 1;

    if(!isSuccess){
      for(var i = 0; i < count; i++){
        matrixArray[col-1][i]++;
        matrixArray[i][row-1]++;
      }
      matrixArray[col-1][row-1]--;
      $(this).trigger('modify');
    }
  });

  $('#panel').on('modify', function(e){
    //修改表象
    if(!isSuccess){
      modifyColor.call(e.target, matrixArray[col-1][row-1]);

      for(var i = 1; i < count; i++){
        (function(j) {
          setTimeout(function(){
            if((row-1-j) >= 0){
              modifyColor.call($('tr').eq(col-1).children('td').eq(row-1-j), matrixArray[col-1][row-1-j]);
            }
            if((row-1+j) < count){
              modifyColor.call($('tr').eq(col-1).children('td').eq(row-1+j), matrixArray[col-1][row-1+j]);
            }
            if(col-1-j >= 0){
              modifyColor.call($('tr').eq(col-1-j).children('td').eq(row-1), matrixArray[col-1-j][row-1]);
            }
            if(col-1+j < count){
              modifyColor.call($('tr').eq(col-1+j).children('td').eq(row-1), matrixArray[col-1+j][row-1]);
            }

          }, 100);
        }(i));
      }
    }

  });

  $('#panel').on('modify', function(e){
    //判断是否结束
    var oddCount = 0;
    for(var i = 0; i < count; i++){
      for(var j = 0; j < count; j++){
        if(matrixArray[i][j] % 2 != 0){
          oddCount++;
        }
      }
    }
    if(oddCount == count*count){
      isSuccess = true;
      count++;
      refreshUI();
      refreshMatrix();
      resetStatus();
    }
  });

  function modifyColor(num){
    if(num % 2 === 0){
      $(this).removeClass('cell-red');
      $(this).addClass('cell-black');
    }
    else if(num % 2 != 0){
      $(this).removeClass('cell-black');
      $(this).addClass('cell-red');
    }
  }

  function refreshUI(){
    $('#panel').empty();
    for(var i = 0; i < count; i++){
      $('#panel').append('

');
      for(var j = 0; j < count; j++){
        $('tr').eq(i).append('	');
      }
    }
    var score = count - 1;
    $('#score').html(score);
  }

  function refreshMatrix(){
    matrixArray = new Array();
    for(var i = 0; i < count; i++){
      matrixArray[i] = new Array();
      for(var j = 0; j < count; j++){
        matrixArray[i][j] = 0;
      }
    }
  }

  function resetStatus(){
    isSuccess = false;
  }

  $('.btn-default').on('click', function(event) {
    event.preventDefault();
    /* Act on the event */
    $('body').removeClass('modal-open');
    $('#myModal').removeClass('in');
  });

  $('.btn-again').on('click', function(event) {
    event.preventDefault();
    /* Act on the event */
    $('body').removeClass('modal-open');
    $('#myModal').removeClass('in');
    count = 2;
    refreshUI();
    refreshMatrix();
    resetStatus();
    countdown = 2;
    timedCount();
  });
});<commit_msg>removeClass in after click button<commit_after>$(function(){
 FastClick.attach(document.body);

  //二维数组
  var matrixArray = new Array();
  var count = 2;
  refreshUI();
  refreshMatrix();

  //倒计时
  var countdown = 60,
  $time = $('#countdown');

  function timedCount()
  {
    $time.html(countdown);
    countdown--;
    if(countdown > 0)
      setTimeout(timedCount, 1000);
    else{
      $time.html(countdown);
      isSuccess = true;
      $('#result').html(count-1);
      $('body').addClass('modal-open');
      $('#myModal').addClass('in');
      $('.modal-backdrop').addClass('in');
    }
  }

  timedCount();

  var row, col, isSuccess = false;

  $('#panel').on('click', 'td', function(e){
    row = parseInt($(this).data('y')) + 1;
    col = parseInt($(this).data('x')) + 1;

    if(!isSuccess){
      for(var i = 0; i < count; i++){
        matrixArray[col-1][i]++;
        matrixArray[i][row-1]++;
      }
      matrixArray[col-1][row-1]--;
      $(this).trigger('modify');
    }
  });

  $('#panel').on('modify', function(e){
    //修改表象
    if(!isSuccess){
      modifyColor.call(e.target, matrixArray[col-1][row-1]);

      for(var i = 1; i < count; i++){
        (function(j) {
          setTimeout(function(){
            if((row-1-j) >= 0){
              modifyColor.call($('tr').eq(col-1).children('td').eq(row-1-j), matrixArray[col-1][row-1-j]);
            }
            if((row-1+j) < count){
              modifyColor.call($('tr').eq(col-1).children('td').eq(row-1+j), matrixArray[col-1][row-1+j]);
            }
            if(col-1-j >= 0){
              modifyColor.call($('tr').eq(col-1-j).children('td').eq(row-1), matrixArray[col-1-j][row-1]);
            }
            if(col-1+j < count){
              modifyColor.call($('tr').eq(col-1+j).children('td').eq(row-1), matrixArray[col-1+j][row-1]);
            }

          }, 100);
        }(i));
      }
    }

  });

  $('#panel').on('modify', function(e){
    //判断是否结束
    var oddCount = 0;
    for(var i = 0; i < count; i++){
      for(var j = 0; j < count; j++){
        if(matrixArray[i][j] % 2 != 0){
          oddCount++;
        }
      }
    }
    if(oddCount == count*count){
      isSuccess = true;
      count++;
      refreshUI();
      refreshMatrix();
      resetStatus();
    }
  });

  function modifyColor(num){
    if(num % 2 === 0){
      $(this).removeClass('cell-red');
      $(this).addClass('cell-black');
    }
    else if(num % 2 != 0){
      $(this).removeClass('cell-black');
      $(this).addClass('cell-red');
    }
  }

  function refreshUI(){
    $('#panel').empty();
    for(var i = 0; i < count; i++){
      $('#panel').append('

');
      for(var j = 0; j < count; j++){
        $('tr').eq(i).append('	');
      }
    }
    var score = count - 1;
    $('#score').html(score);
  }

  function refreshMatrix(){
    matrixArray = new Array();
    for(var i = 0; i < count; i++){
      matrixArray[i] = new Array();
      for(var j = 0; j < count; j++){
        matrixArray[i][j] = 0;
      }
    }
  }

  function resetStatus(){
    isSuccess = false;
  }

  $('.btn-default').on('click', function(event) {
    event.preventDefault();
    /* Act on the event */
    $('body').removeClass('modal-open');
    $('#myModal').removeClass('in');
    $('.modal-backdrop').removeClass('in');
  });

  $('.btn-again').on('click', function(event) {
    event.preventDefault();
    /* Act on the event */
    $('body').removeClass('modal-open');
    $('#myModal').removeClass('in');
    $('.modal-backdrop').removeClass('in');
    count = 2;
    refreshUI();
    refreshMatrix();
    resetStatus();
    countdown = 60;
    timedCount();
  });
});<|endoftext|>


<commit_before>$(".tile").on("click", function(){

  var draw = 0;
  var tileOrder = document.querySelectorAll(".tile");

  if( $(this).hasClass("unavailable") || $(".popup").hasClass("no-more-clicks") ){
    return false; //first thing we do is check if the click is valid (tile must NOT have something already)
  }

  $(this).append("X").addClass("unavailable"); //if click is valid, we can fill the tile with X

  //then we check if there is a victory
  checkVictory();

  //if not, the machine can make its move
  addO();

  //then we check for a draw -> must happen BEFORE checkVictory(), otherwise it overwrites it
  checkDraw();

  //then we check if the machine won
  checkVictory();

  function addO(){
    var selection = [];
    //check all available (empty) tiles and make a random selection out of them
    $( ".tile" ).each(function( index ) {

     if( $(this).hasClass("unavailable")){
        draw++;
        console.log(draw);
      }else{
        selection.push(this);
      }

    });

    var machineMove = Math.floor(Math.random() * (selection.length - 0));
    $(selection[machineMove]).append("O").addClass("unavailable");
  }

  function checkVictory(){//current issue: need to get out of this function after first showPopUp is called
    //covers the tiles that share the same position in each row (1st, 2nd or 3rd) have the same html
    for(var m = 0; m < 3; m++){
      if($(tileOrder[m]).html() === $(tileOrder[m+3]).html() &&
         $(tileOrder[m]).html() === $(tileOrder[m+6]).html() && $(tileOrder[m]).hasClass("unavailable") ){
        console.log("1");
        if($(tileOrder[m]).html() === "O"){
            showPopUp(".defeats");
          }else{
            showPopUp(".victories");
          }

      }
    }

    // this covers the middle tile of the middle row has the same html as its extreme opposites
    if($(tileOrder[4]).html() === $(tileOrder[8]).html() &&
       $(tileOrder[4]).html() === $(tileOrder[0]).html() && $(tileOrder[0]).hasClass("unavailable") ||
       $(tileOrder[4]).html() === $(tileOrder[6]).html() &&
       $(tileOrder[4]).html() === $(tileOrder[2]).html() && $(tileOrder[2]).hasClass("unavailable")){
      console.log("2");
      if($(tileOrder[4]).html() === "O"){
            showPopUp(".defeats");

          }else{
            showPopUp(".victories");
          }
    }

    var count = 0;

    //all tiles in the same row have the same html
    var onePerRow = [tileOrder[0], tileOrder[3], tileOrder[6]];
    for(var i = 0; i < onePerRow.length; i++){
      var answer = $(onePerRow[i]).html();
      $(onePerRow[i]).siblings().each( function () {
        if(answer !== $(this).html() || answer == ""){
          //alert("not this time");
          return false;
        }
        count++;
        if(count > 2){
          console.log("3");
          if($(this).html() === "O"){
            showPopUp(".defeats");
          }else{
            showPopUp(".victories");
          }
        }
      });
    }
  }

  function checkDraw(){
    //checks if there's a draw
    if(draw > 7){
      showPopUp(".draws");
    }
  }

  function showPopUp(result){
    var oldValue = $(result).find("p");
    var updatedValue = parseInt(oldValue.html())+1;
    if(result == ".victories"){
      $(".popup h3").empty().append("You Win!");
    }else if(result == ".defeats"){
      $(".popup h3").empty().append("You Lose!");
    }else{
      $(".popup h3").empty().append("Draw!");
    }
    $(".popup").show().addClass("no-more-clicks");
    $(result).find("p").empty().append( updatedValue );
  }

  $("button").on("click", function(){
    $(".popup").hide().removeClass("no-more-clicks");
    victory();
  });

  function victory(){
    console.log( $(".popup h3").html());
    for(var j= 0; j < tileOrder.length; j++){
      $(tileOrder[j]).removeClass("unavailable");
      $(tileOrder[j]).empty();
    }
  }

});<commit_msg>solved problem that fired ShowPopUp more than once - draws score still incorrect<commit_after>$(".tile").on("click", function(){

  var draw = 0;
  var tileOrder = document.querySelectorAll(".tile");

  if( $(this).hasClass("unavailable") || $(".popup").hasClass("no-more-clicks") ){
    return false; //first thing we do is check if the click is valid (tile must NOT have something already)
  }

  $(this).append("X").addClass("unavailable"); //if click is valid, we can fill the tile with X

  //then we check for a draw -> must happen BEFORE checkVictory(), otherwise it overwrites it
  checkDraw();

   //then we check if there is a victory
  checkVictoryFirstScenario();
  checkVictorySecondScenario();
  checkVictoryThirdScenario();

   //if not, the machine can make its move
  addO();

  function addO(){
    var selection = [];
    //check all available (empty) tiles and make a random selection out of them
    $( ".tile" ).each(function( index ) {

     if( $(this).hasClass("unavailable")){
        draw++;
        console.log(draw);
      }else{
        selection.push(this);
      }

    });

    var machineMove = Math.floor(Math.random() * (selection.length - 0));
    $(selection[machineMove]).append("O").addClass("unavailable");
  }

  function checkVictoryFirstScenario(){
    //covers the tiles that share the same position in each row (1st, 2nd or 3rd) have the same html
    for(var m = 0; m < 3; m++){
      if($(tileOrder[m]).html() === $(tileOrder[m+3]).html() &&
         $(tileOrder[m]).html() === $(tileOrder[m+6]).html() && $(tileOrder[m]).hasClass("unavailable") ){
        console.log("1");
        if($(tileOrder[m]).html() === "O"){
            showPopUp(".defeats");
          }else{
            showPopUp(".victories");
          }

      }
    }
  }

  function checkVictorySecondScenario(){
     if($(tileOrder[4]).html() === $(tileOrder[8]).html() &&
       $(tileOrder[4]).html() === $(tileOrder[0]).html() && $(tileOrder[0]).hasClass("unavailable") ||
       $(tileOrder[4]).html() === $(tileOrder[6]).html() &&
       $(tileOrder[4]).html() === $(tileOrder[2]).html() && $(tileOrder[2]).hasClass("unavailable")){
      console.log("2");
      if($(tileOrder[4]).html() === "O"){
            showPopUp(".defeats");

          }else{
            showPopUp(".victories");
          }
    }
  }

    // this covers the middle tile of the middle row has the same html as its extreme opposites

  function checkVictoryThirdScenario(){
    var count = 0;

    //all tiles in the same row have the same html
    var onePerRow = [tileOrder[0], tileOrder[3], tileOrder[6]];
    for(var i = 0; i < onePerRow.length; i++){
      var answer = $(onePerRow[i]).html();
      $(onePerRow[i]).siblings().each( function () {
        if(answer !== $(this).html() || answer == ""){
          //alert("not this time");
          return false;
        }
        count++;
        if(count > 2){
          console.log("3");
          if($(this).html() === "O"){
            showPopUp(".defeats");
          }else{
            showPopUp(".victories");
          }
        }
      });
    }
  }

  function checkDraw(){
    //checks if there's a draw
    if(draw > 7){
      showPopUp(".draws");
    }
  }

  function showPopUp(result){
    var oldValue = $(result).find("p");
    var updatedValue = parseInt(oldValue.html())+1;
    if(result == ".victories"){
      $(".popup h3").empty().append("You Win!");
    }else if(result == ".defeats"){
      $(".popup h3").empty().append("You Lose!");
    }else{
      $(".popup h3").empty().append("Draw!");
    }
    $(".popup").show().addClass("no-more-clicks");
    $(result).find("p").empty().append( updatedValue );
  }

  $("button").on("click", function(){
    $(".popup").hide().removeClass("no-more-clicks");
    victory();
  });

  function victory(){
    console.log( $(".popup h3").html());
    for(var j= 0; j < tileOrder.length; j++){
      $(tileOrder[j]).removeClass("unavailable");
      $(tileOrder[j]).empty();
    }
  }

});<|endoftext|>


<commit_before>width = 900;
woff = 20;
height = 500;
hoff = 20;

var overlay = document.getElementById("dotPlotOverlay")
var overlayCtx = overlay.getContext("2d");
var canvas = document.getElementById("dotPlotCanvas")
var ctx = canvas.getContext("2d");
canvas.height = height + hoff * 2;
overlay.height = height + hoff * 2;
canvas.width = width + woff * 2;
overlay.width = width + woff * 2;

var delta = new DeltaParser("example-data/stago_lepto.delta");
//var delta = new DeltaParser("example-data/yeasts.delta");
var refSeqs = delta.refs().sort(function(a,b) {return b.length - a.length;});
var qrySeqs = delta.qrys().sort(function(a,b) {return b.length - a.length;});

var refOffSets = {};
var qryOffSets = {};
var cumulativeRefLength = refSeqs.reduce(function(p,c,i,a) {
  refOffSets[c.name] = p;
  return p + c.length;
}, 0);
var cumulativeQryLength = qrySeqs.reduce(function(p,c,i,a) {
  qryOffSets[c.name] = p;
  return p + c.length;
}, 0);

function ViewExtents(x,y,w,h) {
  this.x = x;
  this.y = y;
  this.width = w;
  this.height = h;
}

var viewStack = [new ViewExtents(0,0,cumulativeRefLength,cumulativeQryLength)];

var requestAnimationFrame =
        window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        function(callback) {
          return setTimeout(callback, 1);
        };

var scaleX;
var scaleY;

function render() {
  refScaleFactor = width / viewStack[0].width;
  qryScaleFactor = height / viewStack[0].height;

  scaleX = function(name, position) {
    return Math.floor((refOffSets[name] + position - viewStack[0].x) * refScaleFactor);
  }
  scaleY = function(name, position) {
    return Math.floor(height - (qryOffSets[name] + position - viewStack[0].y) * qryScaleFactor);
  }

  ctx.clearRect(0, 0, canvas.width, canvas.height);
  drawGrid(ctx);
  drawHits(ctx);
}

clipPaths(ctx);
clipPaths(overlayCtx);
d3.timer(render);

function drawGrid(c) {
  c.save();
  c.translate(woff + 0.5, hoff + 0.5);
  c.strokeStyle = "rgba(90,90,90,0.1)";
  c.beginPath();

  for(var name in refOffSets) {
    var offset = scaleX(name, 0);
    c.moveTo(offset,0);
    c.lineTo(offset,height);
  }
  for (var name in qryOffSets) {
    var offset = scaleY(name, 0);
    c.moveTo(0,offset);
    c.lineTo(width,offset);
  }

  var x = Math.floor(cumulativeRefLength * refScaleFactor)
  var y = Math.floor(cumulativeQryLength * qryScaleFactor)

  c.moveTo(x, 0);
  c.lineTo(x, height);

  c.moveTo(0, y);
  c.lineTo(width, y);

  c.stroke();
  c.restore();
}

function drawHits(c) {
  c.save();
  c.translate(woff + 0.5, hoff + 0.5);

  delta.hits.forEach(function(hit) {
    c.beginPath();
    c.moveTo(scaleX(hit.rname,hit.rstart) - 0.25, scaleY(hit.qname, hit.qstart) - 0.25);
    c.lineTo(scaleX(hit.rname,hit.rend) + 0.25 , scaleY(hit.qname, hit.qend) + 0.25);
    c.strokeStyle = hit.qend > hit.qstart ? "blue" : "red";
    c.stroke();
  });

  c.restore();
}

function clipPaths(c) {
  c.beginPath();
  c.rect(woff, hoff, width + 0.5, height + 0.5);
  c.clip();
}

function getCursorPosition(e) {
  var rect = canvas.getBoundingClientRect();
  var x = e.clientX - rect.left - woff - 1;
  var y = e.clientY - rect.top - hoff - 1;
  return {x:x,y:y};
}

function getRefName(xPos) {
  var refName;
  var refStart;
  if(xPos < 0 || xPos > cumulativeRefLength * refScaleFactor) {
    return ;
  }
  for(var name in refOffSets) {
    var oldName = refName;
    var oldStart = refStart;
    refName = name;
    refStart = scaleX(name, 0);
    if(refStart > xPos) {
      return {name: oldName, x: oldStart, width: refStart - oldStart};
    }
  }
  return {name: refName, x: refStart, width: cumulativeRefLength * refScaleFactor - refStart};
}

function getQryName(yPos) {
  var qryName;
  var qryStart;
  var oldName;
  var oldStart = 0;
  if(yPos < 0 || yPos > cumulativeQryLength * qryScaleFactor) {
    return ;
  }
  for(var name in qryOffSets) {
    oldName = qryName;
    oldStart = qryStart;
    qryName = name;
    qryStart = scaleY(name, 0);
    if(qryStart < yPos) {
      return {name: oldName, y: oldStart, height: qryStart - oldStart};
    }
  }
  return {name: qryName, y: qryStart, height: cumulativeQryLength * qryScaleFactor - qryStart};
}

function mouseMoveListener(e) {
  if(dragging) {
    var mousePos = getCursorPosition(e);
    drag.moved = true;
    console.log("DRAG");
  } else {
    var mousePos = getCursorPosition(e);
    var ref = getRefName(mousePos.x);
    var qry = getQryName(mousePos.y);
    selectedRegions.update(overlayCtx);
    if(ref && qry) {
      var x = scaleX(ref.name, 0) + woff;
      var y = scaleY(qry.name, 0) + hoff;
      var w = ref.width;
      var h = qry.height;
      overlayCtx.fillStyle = "rgba(90,90,90,0.075)";
      overlayCtx.fillRect(x,y,w,h);
    }
  }
}

function SelectedSet() {
  this.regions = []
}

SelectedSet.prototype.addBox = function(x,y,w,h,refName, qryName){
  this.regions.push({x:x, y:y, width:w, height:h, refName:refName, qryName:qryName});
}

SelectedSet.prototype.removeBox = function(x,y) {
  this.regions = this.regions.filter(function(box) {
    return !(box.x == x && box.y == y);
  })
}

SelectedSet.prototype.toggleBox = function(x,y,w,h,refName,qryName) {
  var present = this.regions.some(function(box, i, a) {
    return box.x == x && box.y == y;
  })

  if(present) {
    this.removeBox(x,y);
  } else {
    this.addBox(x,y,w,h,refName, qryName);
  }
}
SelectedSet.prototype.update = function(c) {
  overlayCtx.clearRect(0, 0, overlay.width, overlay.height);
  overlayCtx.fillStyle = "rgba(90,90,90,0.075)";
  overlayCtx.strokeStyle = "rgba(0,0,0,0.5)";
  overlayCtx.lineWidth = 1;
  this.regions.forEach(function(r) {
    var x = scaleX(r.refName, 0) + woff + 0.5;
    var y = scaleY(r.qryName, 0) + hoff + 0.5;
    var w = r.width;
    var h = r.height;
    overlayCtx.fillRect(x,y,w,h);
    overlayCtx.strokeRect(x,y,w,h);
  })
}

function mouseDownListener(e) {
  var mousePos = getCursorPosition(e);
  console.log("DRAG START: ("  + (mousePos.x / refScaleFactor) + "," + (mousePos.y / qryScaleFactor) + ")");
  dragging = true;
  drag = {};
  drag.x = mousePos.x;
  drag.y = mousePos.y;
}

function mouseUpListener(e) {
  var mousePos = getCursorPosition(e);
  if(!drag.moved) {
    var ref = getRefName(mousePos.x);
    var qry = getQryName(mousePos.y);
    selectedRegions.toggleBox(ref.x, qry.y, ref.width, qry.height, ref.name, qry.name);
  } else {
    var x = viewStack[0].x + Math.min(drag.x, mousePos.x) / refScaleFactor;
    var w = Math.abs(mousePos.x - drag.x) / refScaleFactor;
    var y = viewStack[0].y + (height - Math.max(drag.y, mousePos.y)) / qryScaleFactor;
    var h = Math.abs(mousePos.y - drag.y) / qryScaleFactor;
    viewStack.unshift(new ViewExtents(x,y,w,h));
    console.log(Math.min(drag.y, mousePos.y) / qryScaleFactor)
  }
  selectedRegions.update(overlayCtx);
  console.log("DRAG END: ("  + (mousePos.x / refScaleFactor) + "," + (mousePos.y / qryScaleFactor) + ")");
  dragging = false;
}

var selectedRegions = new SelectedSet();
var dragging = false;
var drag = {};

canvas.addEventListener("mousemove", mouseMoveListener);
canvas.addEventListener("mousedown", mouseDownListener);
canvas.addEventListener("mouseup", mouseUpListener);

<commit_msg>Zooming now works with selected regions.<commit_after>width = 900;
woff = 20;
height = 500;
hoff = 20;

var overlay = document.getElementById("dotPlotOverlay")
var overlayCtx = overlay.getContext("2d");
var canvas = document.getElementById("dotPlotCanvas")
var ctx = canvas.getContext("2d");
canvas.height = height + hoff * 2;
overlay.height = height + hoff * 2;
canvas.width = width + woff * 2;
overlay.width = width + woff * 2;

var delta = new DeltaParser("example-data/stago_lepto.delta");
//var delta = new DeltaParser("example-data/yeasts.delta");
var refSeqs = delta.refs().sort(function(a,b) {return b.length - a.length;});
var qrySeqs = delta.qrys().sort(function(a,b) {return b.length - a.length;});

var refOffSets = {};
var qryOffSets = {};
var cumulativeRefLength = refSeqs.reduce(function(p,c,i,a) {
  refOffSets[c.name] = p;
  return p + c.length;
}, 0);
refOffSets["final"] = cumulativeRefLength;
var cumulativeQryLength = qrySeqs.reduce(function(p,c,i,a) {
  qryOffSets[c.name] = p;
  return p + c.length;
}, 0);
qryOffSets["final"] = cumulativeQryLength;

var requestAnimationFrame =
        window.requestAnimationFrame || window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame || window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        function(callback) {
          return setTimeout(callback, 1);
        };

function ViewExtents(x,y,w,h) {
  this.x = x;
  this.y = y;
  this.width = w;
  this.height = h;
}

var currentViewExtents = new ViewExtents(0,0,cumulativeRefLength,cumulativeQryLength)
var viewStack = [currentViewExtents];
var AnimLength = 20
var animationCountDown = 0;

var render = function() {
  if(animationCountDown <= 0) {return true;}

  var animFrac = animationCountDown--/AnimLength;
  var easedFrac = d3.ease('cubic')(animFrac);

  currentViewExtents.x = d3.interpolate(viewStack[0].x,viewStack[1].x)(easedFrac);
  currentViewExtents.y = d3.interpolate(viewStack[0].y,viewStack[1].y)(easedFrac);
  currentViewExtents.width = d3.interpolate(viewStack[0].width,viewStack[1].width)(easedFrac);
  currentViewExtents.height = d3.interpolate(viewStack[0].height,viewStack[1].height)(easedFrac);

  refScaleFactor = width / currentViewExtents.width;
  qryScaleFactor = height / currentViewExtents.height;
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  drawGrid(ctx);
  drawHits(ctx);
  selectedRegions.update(overlayCtx);
  return false;
}

clipPaths(ctx);
clipPaths(overlayCtx);

refScaleFactor = width / currentViewExtents.width;
qryScaleFactor = height / currentViewExtents.height;
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawGrid(ctx);
drawHits(ctx);

d3.timer(render);

function scaleX(name, position) {
  return Math.floor((refOffSets[name] + position - currentViewExtents.x) * refScaleFactor);
};

function scaleY(name, position) {
    return Math.floor(height - (qryOffSets[name] + position - currentViewExtents.y) * qryScaleFactor);
}

function drawGrid(c) {
  c.save();
  c.translate(woff + 0.5, hoff + 0.5);
  c.strokeStyle = "rgba(90,90,90,0.1)";
  c.beginPath();

  var zeroX = currentViewExtents.x * refScaleFactor * -1;
  var zeroY = height - (cumulativeQryLength - currentViewExtents.y) * qryScaleFactor;

  var maxX = width - (currentViewExtents.x + currentViewExtents.width - cumulativeRefLength) * refScaleFactor;
  var maxY = height + currentViewExtents.y * qryScaleFactor;

  for(var name in refOffSets) {
    var offset = scaleX(name, 0);
    c.moveTo(offset,zeroY);
    c.lineTo(offset,maxY);
  }
  for (var name in qryOffSets) {
    var offset = scaleY(name, 0);
    c.moveTo(zeroX,offset);
    c.lineTo(maxX,offset);
  }

  console.log(currentViewExtents.y);

  c.moveTo(maxX, zeroY);
  c.lineTo(maxX, height);

  c.moveTo(zeroX, maxY);
  c.lineTo(maxX, maxY);

  c.stroke();
  c.restore();
}

function drawHits(c) {
  c.save();
  c.translate(woff + 0.5, hoff + 0.5);

  delta.hits.forEach(function(hit) {
    c.beginPath();
    c.moveTo(scaleX(hit.rname,hit.rstart) - 0.25, scaleY(hit.qname, hit.qstart) - 0.25);
    c.lineTo(scaleX(hit.rname,hit.rend) + 0.25 , scaleY(hit.qname, hit.qend) + 0.25);
    c.strokeStyle = hit.qend > hit.qstart ? "blue" : "red";
    c.stroke();
  });

  c.restore();
}

function clipPaths(c) {
  c.beginPath();
  c.rect(woff, hoff, width + 0.5, height + 0.5);
  c.clip();
}

function getCursorPosition(e) {
  var rect = canvas.getBoundingClientRect();
  var x = e.clientX - rect.left - woff - 1;
  var y = e.clientY - rect.top - hoff - 1;
  return {x:x,y:y};
}

function getRefName(xPos) {
  var refName;
  var refStart;
  // Switch to base pair coords
  xPos = xPos / refScaleFactor + currentViewExtents.x;
  if(xPos < 0 || xPos > cumulativeRefLength) {
    return ;
  }
  for(var name in refOffSets) {
    var oldName = refName;
    var oldStart = refStart;
    refName = name;
    refStart = refOffSets[name];
    if(refStart > xPos) {
      return {name: oldName, x: oldStart, width: refStart - oldStart};
    }
  }
  return {name: refName, x: refStart, width: cumulativeRefLength - refStart};
}

function getQryName(yPos) {
  var qryName;
  var qryStart;
  var oldName;
  var oldStart = 0;
  // Switch to base pair coords
  yPos = (height - yPos) / qryScaleFactor + currentViewExtents.y;
  if(yPos < 0 || yPos > cumulativeQryLength) {
    return ;
  }
  for(var name in qryOffSets) {
    oldName = qryName;
    oldStart = qryStart;
    qryName = name;
    qryStart = qryOffSets[name];
    if(qryStart > yPos) {
      return {name: oldName, y: oldStart, height: qryStart - oldStart};
    }
  }
  return {name: qryName, y: qryStart, height: cumulativeQryLength - qryStart};
}

function SelectedSet() {
  this.regions = []
}

SelectedSet.prototype.addBox = function(x,y,w,h,refName, qryName){
  this.regions.push({x:x, y:y, width:w, height:h, refName:refName, qryName:qryName});
}

SelectedSet.prototype.removeBox = function(x,y) {
  this.regions = this.regions.filter(function(box) {
    return !(box.x == x && box.y == y);
  })
}

SelectedSet.prototype.toggleBox = function(x,y,w,h,refName,qryName) {
  var present = this.regions.some(function(box, i, a) {
    return box.x == x && box.y == y;
  })

  if(present) {
    this.removeBox(x,y);
  } else {
    this.addBox(x,y,w,h,refName, qryName);
  }
}
SelectedSet.prototype.update = function(c) {
  c.save();
  c.translate(woff + 0.5, hoff + 0.5);
  overlayCtx.clearRect(0, 0, overlay.width, overlay.height);
  overlayCtx.fillStyle = "rgba(90,90,90,0.075)";
  overlayCtx.strokeStyle = "rgba(0,0,0,0.5)";
  overlayCtx.lineWidth = 1;
  this.regions.forEach(function(r) {
    var x = scaleX(r.refName, 0);
    var y = scaleY(r.qryName, 0);
    var w = r.width * refScaleFactor;
    var h = r.height * qryScaleFactor * -1;
    overlayCtx.fillRect(x,y,w,h);
    overlayCtx.strokeRect(x,y,w,h);
  })
  c.restore();
}

function mouseDownListener(e) {
  var mousePos = getCursorPosition(e);
  console.log("DRAG START: ("  + (mousePos.x / refScaleFactor) + "," + (mousePos.y / qryScaleFactor) + ")");
  dragging = true;
  drag = {};
  drag.x = mousePos.x;
  drag.y = mousePos.y;
}

function mouseMoveListener(e) {
  if(dragging) {
    drag.moved = true;
    //var mousePos = getCursorPosition(e);

  } else {
    var mousePos = getCursorPosition(e);
    var ref = getRefName(mousePos.x);
    var qry = getQryName(mousePos.y);
    selectedRegions.update(overlayCtx);
    if(ref && qry) {
      var x = scaleX(ref.name, 0) + woff;
      var y = scaleY(qry.name, 0) + hoff;
      var w = ref.width * refScaleFactor;
      var h = qry.height * qryScaleFactor * -1;
      overlayCtx.fillStyle = "rgba(90,90,90,0.075)";
      overlayCtx.fillRect(x,y,w,h);
    }
  }
}

function mouseUpListener(e) {
  var mousePos = getCursorPosition(e);
  if(!drag.moved) {
    // Clicking on a box.
    var ref = getRefName(mousePos.x);
    var qry = getQryName(mousePos.y);
    selectedRegions.toggleBox(ref.x, qry.y, ref.width, qry.height, ref.name, qry.name);
  } else if (e.button == 1) {
    // Initiate zoom
    animationCountDown = AnimLength;
    var x = viewStack[0].x + Math.min(drag.x, mousePos.x) / refScaleFactor;
    var w = Math.abs(mousePos.x - drag.x) / refScaleFactor;
    var y = viewStack[0].y + (height - Math.max(drag.y, mousePos.y)) / qryScaleFactor;
    var h = Math.abs(mousePos.y - drag.y) / qryScaleFactor;
    viewStack.unshift(new ViewExtents(x,y,w,h));
    d3.timer(render);
  }
  selectedRegions.update(overlayCtx);
  console.log("DRAG END: ("  + (mousePos.x / refScaleFactor) + "," + (mousePos.y / qryScaleFactor) + ")");
  dragging = false;
}

var selectedRegions = new SelectedSet();
var dragging = false;
var drag = {};

canvas.addEventListener("mousemove", mouseMoveListener);
canvas.addEventListener("mousedown", mouseDownListener);
canvas.addEventListener("mouseup", mouseUpListener);

<|endoftext|>