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

import AxiomError from 'axiom/core/error';
import Completer from 'axiom/core/completer';
import Path from 'axiom/fs/path';

/** @typedef JsExecuteContext$$module$axiom$fs$js$execute_context */
var JsExecuteContext;

/**
 * @param {JsExecuteContext} cx
 * @return {void}
 */
export var main = function(cx) {
  cx.ready();
  // We might need to read from stdin: pause it so no data is lost.
  cx.stdin.pause();

  var list = cx.getArg('_', []);
  if (!list.length || cx.getArg('help')) {
    cx.stdout.write([
      'usage: cat <file>|-...',
      'Reads files sequentially, writing them to the standard output.',
      'The file operands are processed in command-line order.',
      'If file is a single dash (`-`) or absent, cat reads from the standard',
      'input.'
    ].join('\r\n') + '\r\n');
    cx.closeOk();
    return;
  }

  var fileSystem = cx.fileSystemManager;
  var errors = false;
  var readStdin = false;
  var stdinReader;

  var catNext = function() {
    if (!list.length) {
      return null;
    }

    /** @type {Promise} */
    var promise;
    /** @type {string} */
    var pathSpec = list.shift();

    if (pathSpec === '-') {
      if (!stdinReader) {
        stdinReader = new Completer();
        var stdinInput = '';
        cx.stdin.onData.addListener(function(data) {
          stdinInput += data;
        });
        cx.stdin.onEnd.listenOnce(function() {
          stdinReader.resolve(stdinInput);
        });
      }
      promise = stdinReader.promise;
    } else {
      promise = fileSystem.readFile(Path.abs(cx.getPwd(), pathSpec))
        .then(function(result) {
          return result.data;
        });
    }

    return promise.then(function(data) {
      cx.stdout.write(data);
      return catNext();
    }).catch(function(e) {
      errors = true;
      cx.stdout.write('cat ' + pathSpec + ': ' + e.toString() + '\n');
      return catNext();
    });
  };

  catNext().then(function() {
    if (errors) {
      cx.closeError(new AxiomError.Runtime('Some files could not be read'));
    } else {
      cx.closeOk();
    }
  });

  // Delay resuming on purpose to support possible multiple occurances of '-':
  //   `echo something | cat - foo.txt - bar.txt`.
  if (stdinReader) {
    cx.stdin.resume();
  }
};

export default main;

main.signature = {
  'help|h': '?',
  "_": '@'
};
<commit_msg>Fix case `echo abc | cat foo.txt -`, which used to hang<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import AxiomError from 'axiom/core/error';
import Completer from 'axiom/core/completer';
import Path from 'axiom/fs/path';

/** @typedef JsExecuteContext$$module$axiom$fs$js$execute_context */
var JsExecuteContext;

/**
 * @param {JsExecuteContext} cx
 * @return {void}
 */
export var main = function(cx) {
  cx.ready();

  var list = cx.getArg('_', []);
  if (!list.length || cx.getArg('help')) {
    cx.stdout.write([
      'usage: cat <file>|-...',
      'Reads files sequentially, writing them to the standard output.',
      'The file operands are processed in command-line order.',
      'If file is a single dash (`-`) or absent, cat reads from the standard',
      'input.'
    ].join('\r\n') + '\r\n');
    cx.closeOk();
    return;
  }

  var errors = false;
  var stdinReader = new Completer();

  if (list.indexOf('-') !== -1) {
    var stdinInput = '';
    cx.stdin.pause();
    cx.stdin.onData.addListener(function(data) {
      stdinInput += data;
    });
    cx.stdin.onEnd.listenOnce(function() {
      stdinReader.resolve(stdinInput);
    });
    cx.stdin.resume();
  }

  var catNext = function() {
    if (!list.length) {
      return null;
    }

    /** @type {Promise} */
    var promise;
    /** @type {string} */
    var pathSpec = list.shift();

    if (pathSpec === '-') {
      promise = stdinReader.promise;
    } else {
      promise = cx.fileSystemManager.readFile(Path.abs(cx.getPwd(), pathSpec))
        .then(function(result) {
          return result.data;
        });
    }

    return promise.then(function(data) {
      cx.stdout.write(data);
      return catNext();
    }).catch(function(e) {
      errors = true;
      cx.stdout.write('cat ' + pathSpec + ': ' + e.toString() + '\n');
      return catNext();
    });
  };

  catNext().then(function() {
    if (errors) {
      cx.closeError(new AxiomError.Runtime('Some files could not be read'));
    } else {
      cx.closeOk();
    }
  });
};

export default main;

main.signature = {
  'help|h': '?',
  "_": '@'
};
<|endoftext|>


<commit_before>/**
 * WebdriverCSS
 */

var fs    = require('fs'),
    gm    = require('gm'),
    async = require('async');

var WebdriverCSS = module.exports = function(webdriverInstance, options) {
    options = options || {};

    if(!webdriverInstance) {
        throw new Error('A WebdriverJS instance is needed to initialise WebdriverCSS');
    }

    var that = this;

    this.webdriverInstance = webdriverInstance;
    this.screenshotRoot = options.screenshotRoot || 'webdrivercss';
    this.failedComparisonsRoot = options.failedComparisonsRoot || this.screenshotRoot + '/failures';
    this.mismatchTolerance = options.mismatchTolerance || 0.05;

    /**
     * create directory structure
     */
     if(!fs.existsSync('./' + this.screenshotRoot)){
        fs.mkdirSync('./' + this.screenshotRoot, 0766, function(err){
            if(err) throw new Error("ERROR! Can't make the directory! \n");
        });
     }

    /**
     * add WebdriverCSS command to WebdriverJS instance
     */
    this.webdriverInstance.addCommand('webdrivercss', function(id,args,cb) {
        var filename = id + '.png',
            screenshot, pageInfo;

        async.waterfall([

            /**
             * create screenshot via [GET] /session/:sessionId/screenshot
             */
            function(done) {
                that.webdriverInstance.screenshot(done)
            },

            /**
             * get page informations
             */
            function(res,done) {
                screenshot = new Buffer(res.value, 'base64');

                if(!args.elem) {
                    return done(null);
                }

                that.webdriverInstance.execute(function() {
                    return {
                        screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
                        screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0),
                        elemBounding: (arguments[0] && document.querySelector(arguments[0])) ? document.querySelector(arguments[0]).getBoundingClientRect() : null,
                        url: window.location.href
                    }
                },args.elem,done)
            },

            function(res, done) {

                if(res.value.elemBounding) {

                    var cropDim = {
                        x: args.x || (res.value.elemBounding.left + (res.value.elemBounding.width / 2)),
                        y: args.y || (res.value.elemBounding.top + (res.value.elemBounding.height / 2)),
                        width: args.width || res.value.elemBounding.width,
                        height: args.height || res.value.elemBounding.height
                    }

                    gm(screenshot)
                        .quality(100)
                        .crop(cropDim.width, cropDim.height, cropDim.x - (cropDim.width / 2), cropDim.y - (cropDim.height / 2))
                        .write(filename, done);

                } else {
                    gm(screenshot).quality(100).write(filename, done);
                }
            }
        ])
    })

}

WebdriverCSS.prototype<commit_msg>improved creation of directory structure<commit_after>/**
 * WebdriverCSS
 */

var fs    = require('fs'),
    gm    = require('gm'),
    async = require('async');

var WebdriverCSS = module.exports = function(webdriverInstance, options) {
    options = options || {};

    if(!webdriverInstance) {
        throw new Error('A WebdriverJS instance is needed to initialise WebdriverCSS');
    }

    var that = this;

    this.webdriverInstance = webdriverInstance;
    this.screenshotRoot = options.screenshotRoot || 'webdrivercss';
    this.failedComparisonsRoot = options.failedComparisonsRoot || (this.screenshotRoot + '/diff');
    this.mismatchTolerance = options.mismatchTolerance || 0.05;

    /**
     * create directory structure
     */
    fs.exists('./' + this.screenshotRoot, function(exists) {
        // TODO catch error
        if(!exists) {
            fs.mkdir('./' + that.screenshotRoot, 0766, function() {
                fs.mkdir('./' + that.failedComparisonsRoot, 0766);
            });
        }
    });

    /**
     * add WebdriverCSS command to WebdriverJS instance
     */
    this.webdriverInstance.addCommand('webdrivercss', function(id,args,cb) {
        var filename = that.screenshotRoot + '/' + id + '.current.png',
            screenshot, pageInfo;

        async.waterfall([

            /**
             * check if file with id already exists
             */
            function(done) {
                fs.exists(filename, function(exists) {
                    if(exists) {
                        filename = that.screenshotRoot + '/' + id + '.new.png'
                    }

                    return done(null)
                })
            },

            /**
             * create screenshot via [GET] /session/:sessionId/screenshot
             */
            function(done) {
                that.webdriverInstance.screenshot(done)
            },

            /**
             * get page informations
             */
            function(res,done) {
                screenshot = new Buffer(res.value, 'base64');

                if(!args.elem) {
                    return done(null);
                }

                that.webdriverInstance.execute(function() {
                    return {
                        screenWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
                        screenHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0),
                        elemBounding: (arguments[0] && document.querySelector(arguments[0])) ? document.querySelector(arguments[0]).getBoundingClientRect() : null,
                        url: window.location.href
                    }
                },args.elem,done)
            },

            /**
             * crop image according to user arguments and its position on screen and save it
             */
            function(res, done) {

                /**
                 * if image was found use bounding values to crop image
                 */
                if(res.value.elemBounding) {

                    var cropDim = {
                        x: args.x || (res.value.elemBounding.left + (res.value.elemBounding.width / 2)),
                        y: args.y || (res.value.elemBounding.top + (res.value.elemBounding.height / 2)),
                        width: args.width || res.value.elemBounding.width,
                        height: args.height || res.value.elemBounding.height
                    }

                    gm(screenshot)
                        .quality(100)
                        .crop(cropDim.width, cropDim.height, cropDim.x - (cropDim.width / 2), cropDim.y - (cropDim.height / 2))
                        .write(filename, done);

                } else {
                /**
                 * else save whole screenshot
                 * TODO return error that image wasn't found
                 */
                    gm(screenshot).quality(100).write(filename, done);

                }

            }
        ])
    })

}

WebdriverCSS.prototype<|endoftext|>


<commit_before>/* Copyright (c) 2012-2013 LevelUP contributors
 * See list at <https://github.com/rvagg/node-levelup#contributing>
 * MIT +no-false-attribs License
 * <https://github.com/rvagg/node-levelup/blob/master/LICENSE>
 */

var Stream       = require('stream').Stream
  , inherits     = require('util').inherits
  , extend       = require('xtend')
  , concatStream = require('concat-stream')

  , setImmediate = require('./util').setImmediate

  , getOptions   = require('./util').getOptions

  , defaultOptions = {}

function WriteStream (options, db) {
  Stream.call(this)
  this._options = extend(defaultOptions, getOptions(db, options))
  this._db      = db
  this._buffer  = []
  this._status  = 'init'
  this._end     = false
  this.writable = true
  this.readable = false

  var ready = function () {
    if (!this.writable)
      return
    this._status = 'ready'
    this.emit('ready')
    this._process()
  }.bind(this)

  if (db.isOpen())
    setImmediate(ready)
  else
    db.once('ready', ready)
}

inherits(WriteStream, Stream)

WriteStream.prototype.write = function (data) {
  if (!this.writable)
    return false
  this._buffer.push(data)
  if (this._status != 'init')
    this._processDelayed()
  if (this._options.maxBufferLength &&
      this._buffer.length > this._options.maxBufferLength) {
    this._writeBlock = true
    return false
  }
  return true
}

WriteStream.prototype.end = function() {
  setImmediate(function () {
    this._end = true
    this._process()
  }.bind(this))
}

WriteStream.prototype.destroy = function() {
  this.writable = false
  this.end()
}

WriteStream.prototype.destroySoon = function() {
  this.end()
}

WriteStream.prototype.add = function(entry) {
  if (!entry.props)
    return
  if (entry.props.Directory)
    entry.pipe(this._db.writeStream(this._options))
  else if (entry.props.File || entry.File || entry.type == 'File')
    this._write(entry)
  return true
}

WriteStream.prototype._processDelayed = function() {
  setImmediate(this._process.bind(this))
}

WriteStream.prototype._process = function() {
  var buffer
    , entry
    , options
    , self = this
    , type

    , cb = function (err) {
        if (!this.writable)
          return
        if (this._status != 'closed')
          this._status = 'ready'
        if (err) {
          this.writable = false
          return this.emit('error', err)
        }
        this._process()
      }.bind(this)

  if (this._status != 'ready' && this.writable) {
    if (this._buffer.length && this._status != 'closed')
      this._processDelayed()
    return
  }

  if (this._buffer.length && this.writable) {
    this._status = 'writing'
    buffer = this._buffer
    this._buffer = []

    if (buffer.length == 1) {
      entry = buffer.pop()
      type = entry.type || "put"
      if (entry.key !== undefined && entry.value !== undefined)
        options = {
            keyEncoding: entry.keyEncoding
                            || this._options.keyEncoding
          , valueEncoding: entry.valueEncoding
                            || entry.encoding
                            || this._options.valueEncoding
        }
        this._db[type](entry.key, entry.value, options, cb)
    } else {
      this._db.batch(buffer.map(function (d) {
        return {
            type: d.type || "put"
          , key: d.key
          , value: d.value
          , keyEncoding: d.keyEncoding
                          || self._options.keyEncoding
          , valueEncoding: d.valueEncoding
                            || d.encoding
                            || self._options.valueEncoding
        }
      }), cb)
    }

    if (this._writeBlock) {
      this._writeBlock = false
      this.emit('drain')
    }

    // don't allow close until callback has returned
    return
  }

  if (this._end && this._status != 'closed') {
    this._status = 'closed'
    this.writable = false
    this.emit('close')
  }
}

WriteStream.prototype._write = function (entry) {
  var key = entry.path || entry.props.path
  if (!key)
    return
  entry.pipe(concatStream(function (err, data) {
    if (err) {
      this.writable = false
      return this.emit('error', err)
    }
    if (this._options.fstreamRoot &&
        key.indexOf(this._options.fstreamRoot) > -1)
      key = key.substr(this._options.fstreamRoot.length + 1)
    this.write({ key: key, value: data })
  }.bind(this)))
}

WriteStream.prototype.toString = function () {
  return 'LevelUP.WriteStream'
}

module.exports.create = function (options, db) {
  return new WriteStream(options, db)
}
<commit_msg>Changed " into '.<commit_after>/* Copyright (c) 2012-2013 LevelUP contributors
 * See list at <https://github.com/rvagg/node-levelup#contributing>
 * MIT +no-false-attribs License
 * <https://github.com/rvagg/node-levelup/blob/master/LICENSE>
 */

var Stream       = require('stream').Stream
  , inherits     = require('util').inherits
  , extend       = require('xtend')
  , concatStream = require('concat-stream')

  , setImmediate = require('./util').setImmediate

  , getOptions   = require('./util').getOptions

  , defaultOptions = {}

function WriteStream (options, db) {
  Stream.call(this)
  this._options = extend(defaultOptions, getOptions(db, options))
  this._db      = db
  this._buffer  = []
  this._status  = 'init'
  this._end     = false
  this.writable = true
  this.readable = false

  var ready = function () {
    if (!this.writable)
      return
    this._status = 'ready'
    this.emit('ready')
    this._process()
  }.bind(this)

  if (db.isOpen())
    setImmediate(ready)
  else
    db.once('ready', ready)
}

inherits(WriteStream, Stream)

WriteStream.prototype.write = function (data) {
  if (!this.writable)
    return false
  this._buffer.push(data)
  if (this._status != 'init')
    this._processDelayed()
  if (this._options.maxBufferLength &&
      this._buffer.length > this._options.maxBufferLength) {
    this._writeBlock = true
    return false
  }
  return true
}

WriteStream.prototype.end = function() {
  setImmediate(function () {
    this._end = true
    this._process()
  }.bind(this))
}

WriteStream.prototype.destroy = function() {
  this.writable = false
  this.end()
}

WriteStream.prototype.destroySoon = function() {
  this.end()
}

WriteStream.prototype.add = function(entry) {
  if (!entry.props)
    return
  if (entry.props.Directory)
    entry.pipe(this._db.writeStream(this._options))
  else if (entry.props.File || entry.File || entry.type == 'File')
    this._write(entry)
  return true
}

WriteStream.prototype._processDelayed = function() {
  setImmediate(this._process.bind(this))
}

WriteStream.prototype._process = function() {
  var buffer
    , entry
    , options
    , self = this
    , type

    , cb = function (err) {
        if (!this.writable)
          return
        if (this._status != 'closed')
          this._status = 'ready'
        if (err) {
          this.writable = false
          return this.emit('error', err)
        }
        this._process()
      }.bind(this)

  if (this._status != 'ready' && this.writable) {
    if (this._buffer.length && this._status != 'closed')
      this._processDelayed()
    return
  }

  if (this._buffer.length && this.writable) {
    this._status = 'writing'
    buffer = this._buffer
    this._buffer = []

    if (buffer.length == 1) {
      entry = buffer.pop()
      type = entry.type || 'put'
      if (entry.key !== undefined && entry.value !== undefined)
        options = {
            keyEncoding: entry.keyEncoding
                            || this._options.keyEncoding
          , valueEncoding: entry.valueEncoding
                            || entry.encoding
                            || this._options.valueEncoding
        }
        this._db[type](entry.key, entry.value, options, cb)
    } else {
      this._db.batch(buffer.map(function (d) {
        return {
            type: d.type || 'put'
          , key: d.key
          , value: d.value
          , keyEncoding: d.keyEncoding
                          || self._options.keyEncoding
          , valueEncoding: d.valueEncoding
                            || d.encoding
                            || self._options.valueEncoding
        }
      }), cb)
    }

    if (this._writeBlock) {
      this._writeBlock = false
      this.emit('drain')
    }

    // don't allow close until callback has returned
    return
  }

  if (this._end && this._status != 'closed') {
    this._status = 'closed'
    this.writable = false
    this.emit('close')
  }
}

WriteStream.prototype._write = function (entry) {
  var key = entry.path || entry.props.path
  if (!key)
    return
  entry.pipe(concatStream(function (err, data) {
    if (err) {
      this.writable = false
      return this.emit('error', err)
    }
    if (this._options.fstreamRoot &&
        key.indexOf(this._options.fstreamRoot) > -1)
      key = key.substr(this._options.fstreamRoot.length + 1)
    this.write({ key: key, value: data })
  }.bind(this)))
}

WriteStream.prototype.toString = function () {
  return 'LevelUP.WriteStream'
}

module.exports.create = function (options, db) {
  return new WriteStream(options, db)
}
<|endoftext|>


<commit_before>var path = require('path')
var ldjson = require('ldjson-stream')
var csv = require('csv-parser')
var byteStream = require('byte-stream')
var through = require('through2')
var byteStream = require('byte-stream')
var headStream = require('head-stream')
var combiner = require('stream-combiner')
var peek = require('peek-stream')
var mbstream = require('multibuffer-stream')
var debug = require('debug')('write-stream')
var docUtils = require('./document.js')

var OBJ_STREAM_OPTS = {highWaterMark: 16}

module.exports = function writeStream(dat, opts) {
  if (!opts) opts = {}

  var parser = parseStream(dat, opts)

  var pipeline = []
  if (opts.columns) pipeline.push(headStream(mergeColumns, {includeHead: true}))

  pipeline = pipeline.concat(parser)
  pipeline.push(
    byteStream({ limit: dat.dbOptions.writeBufferSize, time: opts.batchTime || 3000 }),
    through.obj(OBJ_STREAM_OPTS, writeBatch)
  )

  var processor = combine(pipeline)

  // progress data
  processor.documents = 0
  processor.bytes = 0
  processor.conflicts = 0

  return processor

  function mergeColumns(doc, cb) {
    dat.schema.merge(dat.schema.normalize([].concat(opts.columns)), cb)
  }

  function writeBatch(structs, enc, cb) {
    var len = structs.length
    var pending = len
    var struct
    var self = this

    debug('writeBatch', structs.length)

    function batchDone() {
      debug('writeBatch finished', len)
      cb()
    }

    function rowDone(err, updated) {
      if (err) {
        if (opts.results) self.push(err)
        if (err.conflict) {
          debug('conflict', err.key)
          processor.emit('conflict', err)
          processor.conflicts++
          processor.emit('update')
        } else {
          self.emit('error', err)
        }
      }
      if (opts.results && !err) self.push(updated)
      pending--
      if (pending === 0) batchDone()
    }

    for (var i = 0; i < len; i++) {
      struct = structs[i]
      var putOpts = {force: opts.force, skipSchemaCheck: true }
      putOpts.version = struct.version
      processor.bytes += struct.length
      processor.documents++
      processor.emit('update')
      dat.put(struct.key, struct.value, putOpts, rowDone)
    }
  }
}

function parseStream(dat, opts) {
  if (opts.csv || opts.f === 'csv') return parseCSV()
  if (opts.json || opts.f === 'json') return parseJSON()
  if (opts.protobuf || opts.f === 'protobuf') return parseProtobuf()
  if (opts.objects || opts.f === 'objects') return parseObjects()

  return peek({strict:true}, function(data, swap) {
    if (!Buffer.isBuffer(data)) return swap(null, combine(parseObjects()))
    if (isJSON(data)) return swap(null, combine(parseJSON()))
    if (isCSV(data)) return swap(null, combine(parseCSV()))
    cb(new Error('Could not auto detect input type'))
  })

  function primaryKeyStream() {
    return through.obj(OBJ_STREAM_OPTS, function(row, enc, cb) {
      dat.beforePut(row, function(err, row) {
        if (err) return cb(err)
        cb(null, new KeyStruct(row.version, docUtils.extractPrimaryKey(row, opts), dat.schema.encode(row)))
      })
    })
  }

  function parseCSV() {
    return [
      csv({
        headers: opts.headerRow === false && opts.columns,
        separator: opts.separator
      }),
      headStream(updateSchema, {includeHead: true}),
      primaryKeyStream()
    ]

    function updateSchema(doc, cb) {
      dat.schema.merge(dat.schema.normalize(doc.headers), {strict: true}, cb)
    }
  }

  function parseJSON() {
    return [
      ldjson()
    ].concat(parseObjects())
  }

  function parseObjects() {
    return [
      through.obj(OBJ_STREAM_OPTS, updateSchema),
      primaryKeyStream()
    ]

    function updateSchema(doc, enc, cb) {
      dat.schema.mergeFromObject(doc, function(err) {
        if (err) return cb(err)
        cb(null, doc)
      })
    }
  }

  function parseProtobuf() {
    return [
      mbstream.unpackStream(),
      through.obj(OBJ_STREAM_OPTS, decodeWrite),
    ].concat(parseObjects())

    function decodeWrite(buff, enc, cb) {
      cb(null, dat.schema.decode(buff))
    }
  }

}

function isJSON(data) {
  try {
    JSON.parse(data)
    return true
  } catch (err) {
    return false
  }
}

function isCSV(data) {
  return data.toString().indexOf(',') > 0
}

function combine(streams) {
  return combiner.apply(combiner, streams)
}

// for mad v8 speed
function KeyStruct(version, key, data) {
  this.version = version
  this.key = key
  this.value = data
  this.length = data.length
}
<commit_msg>use leveldat in write stream<commit_after>var path = require('path')
var ldjson = require('ldjson-stream')
var csv = require('csv-parser')
var byteStream = require('byte-stream')
var through = require('through2')
var byteStream = require('byte-stream')
var headStream = require('head-stream')
var combiner = require('stream-combiner')
var peek = require('peek-stream')
var mbstream = require('multibuffer-stream')
var debug = require('debug')('write-stream')
var docUtils = require('./document.js')

var OBJ_STREAM_OPTS = {highWaterMark: 16}

module.exports = function writeStream(dat, opts) {
  if (!opts) opts = {}

  var parser = parseStream(dat, opts)

  var pipeline = []
  if (opts.columns) pipeline.push(headStream(mergeColumns, {includeHead: true}))

  pipeline = pipeline.concat(parser)
  pipeline.push(
    byteStream({ limit: dat.dbOptions.writeBufferSize, time: opts.batchTime || 3000 }),
    through.obj(OBJ_STREAM_OPTS, writeBatch)
  )

  var processor = combine(pipeline)

  // progress data
  processor.documents = 0
  processor.bytes = 0
  processor.conflicts = 0

  return processor

  function mergeColumns(doc, cb) {
    dat.schema.merge(dat.schema.normalize([].concat(opts.columns)), cb)
  }

  function writeBatch(structs, enc, cb) {
    var len = structs.length
    var pending = len
    var struct
    var self = this

    debug('writeBatch', structs.length)

    function batchDone() {
      debug('writeBatch finished', len)
      cb()
    }

    function rowDone(err, updated) {
      if (err) {
        if (opts.results) self.push(err)
        if (err.conflict) {
          debug('conflict', err.key)
          processor.emit('conflict', err)
          processor.conflicts++
          processor.emit('update')
        } else {
          self.emit('error', err)
        }
      }
      if (opts.results && !err) self.push(updated)
      pending--
      if (pending === 0) batchDone()
    }

    for (var i = 0; i < len; i++) {
      struct = structs[i]
      processor.bytes += struct.length
      processor.documents++
      processor.emit('update')
      dat.storage.put(struct.key, struct.value, {version:struct.version, force:opts.force}, rowDone)
    }
  }
}

function parseStream(dat, opts) {
  if (opts.csv || opts.f === 'csv') return parseCSV()
  if (opts.json || opts.f === 'json') return parseJSON()
  if (opts.protobuf || opts.f === 'protobuf') return parseProtobuf()
  if (opts.objects || opts.f === 'objects') return parseObjects()

  return peek({strict:true}, function(data, swap) {
    if (!Buffer.isBuffer(data)) return swap(null, combine(parseObjects()))
    if (isJSON(data)) return swap(null, combine(parseJSON()))
    if (isCSV(data)) return swap(null, combine(parseCSV()))
    cb(new Error('Could not auto detect input type'))
  })

  function primaryKeyStream() {
    return through.obj(OBJ_STREAM_OPTS, function(row, enc, cb) {
      dat.beforePut(row, function(err, row) {
        if (err) return cb(err)
        cb(null, new KeyStruct(row.version, docUtils.extractPrimaryKey(row, opts), dat.schema.encode(row)))
      })
    })
  }

  function parseCSV() {
    return [
      csv({
        headers: opts.headerRow === false && opts.columns,
        separator: opts.separator
      }),
      headStream(updateSchema, {includeHead: true}),
      primaryKeyStream()
    ]

    function updateSchema(doc, cb) {
      dat.schema.merge(dat.schema.normalize(doc.headers), {strict: true}, cb)
    }
  }

  function parseJSON() {
    return [
      ldjson()
    ].concat(parseObjects())
  }

  function parseObjects() {
    return [
      through.obj(OBJ_STREAM_OPTS, updateSchema),
      primaryKeyStream()
    ]

    function updateSchema(doc, enc, cb) {
      dat.schema.mergeFromObject(doc, function(err) {
        if (err) return cb(err)
        cb(null, doc)
      })
    }
  }

  function parseProtobuf() {
    return [
      mbstream.unpackStream(),
      through.obj(OBJ_STREAM_OPTS, decodeWrite),
    ].concat(parseObjects())

    function decodeWrite(buff, enc, cb) {
      cb(null, dat.schema.decode(buff))
    }
  }

}

function isJSON(data) {
  try {
    JSON.parse(data)
    return true
  } catch (err) {
    return false
  }
}

function isCSV(data) {
  return data.toString().indexOf(',') > 0
}

function combine(streams) {
  return combiner.apply(combiner, streams)
}

// for mad v8 speed
function KeyStruct(version, key, data) {
  this.version = version
  this.key = key
  this.value = data
  this.length = data.length
}
<|endoftext|>


<commit_before>document.addEventListener('DOMContentLoaded', function() {
  const projectContainers = document.querySelectorAll('.project-thumb-container');

  // Project Loading
  // Slides projects page out of view
  projectLoadAnimation = function() {
    const projectBelt = document.querySelector('.project-belt');
    let position = 0;
    let id = setInterval(frame, 0.003);
    function frame() {
      if (position === -100) {
        clearInterval(id);
      } else {
        position--;
        projectBelt.style.left = position + '%';
      }
    }
  }

  // On project thumbnail click
  const onProjectClick = function(event) {
    const project = event.target;
    const projectURL = project.dataset.content;
    const projectName = getProjectNameFromUrl(projectURL);

    // Update router state
    pushWindowState(projectName);
    window.onpopstate = () => onProjectReturn();

    loadProjectData(projectURL);
    projectLoadAnimation();
  }

  // Add event listeners to project thumbnails
  for (let container of projectContainers) {
    container.addEventListener('click', onProjectClick);
  }

  // If reloading or searching on a frontend route
  if (window.location.hash) {
    const projectName = window.location.hash.slice(1,window.location.hash.length);
    loadProjectData(projectMap[projectName]);

    const projectBelt = document.querySelector('.project-belt');
    projectBelt.style.left = '-100%';
  }
});

// Loading project data
function loadProjectData(projectData) {
  fetch(projectData)
    .then(function(response) {
      return response.text();
    })
    .then(function(projectHTML) {
      document.querySelector('.project-load').innerHTML = projectHTML;
    });
    window.scrollTo(0,0);
}

// Project Return
function projectReturnAnimation() {
  const projectBelt = document.querySelector('.project-belt');
  let position = -100;
  let id = setInterval(frame, 0.003);
  function frame() {
    if (position === 0) {
      clearInterval(id);
    } else {
      position++;
      projectBelt.style.left = position + '%';
    }
  }
}

function onProjectReturn() {
  window.history.pushState(
    {},
    '',
    window.location.origin
  );
  const projectBelt = document.querySelector('.project-belt');
  projectReturnAnimation();
  document.querySelector('.project-return').style.display = 'none';
}

function pushWindowState(projectName) {
  const endURL = projectName ? '/#' + projectName : '';
  window.history.pushState(
    {},
    projectName,
    window.location.origin + endURL
  );
}

function getProjectNameFromUrl(projectURL) {
  const projectPathSplit = projectURL.split('/');
  const projectHtmlName = projectPathSplit[projectPathSplit.length - 1];
  const projectName = projectHtmlName.slice(0,projectHtmlName.length - 5);
  return projectName;
}

// Project Map (Allows for frontend routing reload)
const projectMap = {
  'fightstick': 'project/0000/07/07/fightstick.html',
  'python-markov-chain': 'project/0000/08/08/python-markov-chain.html',
  'organic-robo-head': 'project/0000/09/09/organic-robo-head.html',
  'go-web-crawler': 'project/2017/10/20/go-web-crawler.html',
  'subway-shibori': 'project/2018/04/03/subway-shibori.html',
  'ocelot': 'project/2018/04/28/ocelot.html',
  'dash-borg': 'project/2018/04/30/dash-borg.html',
  'learning-go': 'project/2018/05/16/learning-go.html',
  'morrigan-fightstick': 'project/2018/12/25/morrigan-fightstick.html',
  'deep-space-visualizer': 'project/2019/04/30/deep-space-visualizer.html'
}
<commit_msg>Fix extended footer issue<commit_after>document.addEventListener('DOMContentLoaded', function() {
  const projectContainers = document.querySelectorAll('.project-thumb-container');

  // Project Loading
  // Slides projects page out of view
  projectLoadAnimation = function() {
    const projectBelt = document.querySelector('.project-belt');
    let position = 0;
    let id = setInterval(frame, 0.003);
    function frame() {
      if (position === -100) {
        clearInterval(id);
      } else {
        position--;
        projectBelt.style.left = position + '%';
      }
    }
  }

  // On project thumbnail click
  const onProjectClick = function(event) {
    const project = event.target;
    const projectURL = project.dataset.content;
    const projectName = getProjectNameFromUrl(projectURL);

    // Update router state
    pushWindowState(projectName);
    window.onpopstate = () => onProjectReturn();

    // Toggle project data visibility
    document.querySelector('.project-container').style.display = 'block';

    loadProjectData(projectURL);
    projectLoadAnimation();
  }

  // Add event listeners to project thumbnails
  for (let container of projectContainers) {
    container.addEventListener('click', onProjectClick);
  }

  // If reloading or searching on a frontend route
  if (window.location.hash) {
    const projectName = window.location.hash.slice(1,window.location.hash.length);
    loadProjectData(projectMap[projectName]);

    const projectBelt = document.querySelector('.project-belt');
    projectBelt.style.left = '-100%';
  }
});

// Loading project data
function loadProjectData(projectData) {
  fetch(projectData)
    .then(function(response) {
      return response.text();
    })
    .then(function(projectHTML) {
      document.querySelector('.project-load').innerHTML = projectHTML;
    });
    window.scrollTo(0,0);
}

// Project Return
function projectReturnAnimation() {
  const projectBelt = document.querySelector('.project-belt');
  let position = -100;
  let id = setInterval(frame, 0.003);
  function frame() {
    if (position === 0) {
      clearInterval(id);
    } else {
      position++;
      projectBelt.style.left = position + '%';
    }
  }
}

function onProjectReturn() {
  pushWindowState('');
  // Toggle project data visibility
  document.querySelector('.project-container').style.display = 'none';
  const projectBelt = document.querySelector('.project-belt');
  projectReturnAnimation();
  document.querySelector('.project-return').style.display = 'none';
}

function pushWindowState(projectName) {
  const endURL = projectName ? '/#' + projectName : '';
  window.history.pushState(
    {},
    projectName,
    window.location.origin + endURL
  );
}

function getProjectNameFromUrl(projectURL) {
  const projectPathSplit = projectURL.split('/');
  const projectHtmlName = projectPathSplit[projectPathSplit.length - 1];
  const projectName = projectHtmlName.slice(0,projectHtmlName.length - 5);
  return projectName;
}

// Project Map (Allows for frontend routing reload)
const projectMap = {
  'fightstick': 'project/0000/07/07/fightstick.html',
  'python-markov-chain': 'project/0000/08/08/python-markov-chain.html',
  'organic-robo-head': 'project/0000/09/09/organic-robo-head.html',
  'go-web-crawler': 'project/2017/10/20/go-web-crawler.html',
  'subway-shibori': 'project/2018/04/03/subway-shibori.html',
  'ocelot': 'project/2018/04/28/ocelot.html',
  'dash-borg': 'project/2018/04/30/dash-borg.html',
  'learning-go': 'project/2018/05/16/learning-go.html',
  'morrigan-fightstick': 'project/2018/12/25/morrigan-fightstick.html',
  'deep-space-visualizer': 'project/2019/04/30/deep-space-visualizer.html'
}
<|endoftext|>


<commit_before>var _ = require('underscore'),
	request = require('request');

/**
	premailer.prepare()

	Calls the Premailer API to inline css styles (and opther options), then retrieves
	the generated html and text from their respective URLs.

	You must provide either one of the html or url options.

	If both fetchHTML and fetchText options are false, the original api response is
	passed to next().

	Arguments:

		options (object)
			premailerAPI (string)
				The URL of the premailer API. If you're running your own, replace this.
			html (string)
				The html of the email to parse and inline styles.
			url (string)
				The url of the email to fetch then parse and inline styles.
			fetchHTML (boolean)
				Whether to fetch the parsed HTML (you'll usually want this). Defaults
				to true.
			fetchText (boolean)
				Whether to fetch the auto-generated text version (disable this if you
				are providing your own). Defaults to true.

			see http://premailer.dialect.ca/api for full list of options, including:
				adapter
				baseUrl || base_url
				lineLength || line_length
				linkQueryString || link_query_string
				preserveStyles || preserve_styles
				removeIds || remove_ids
				removeClasses || remove_classes
				removeComments || remove_comments

		next function(err, email)
			err (object)
				Error object
			email (object)
				html (string)
					The parsed html with inlined styles
				text (string)
					The text contents of the email
*/
exports.prepare = function(_options, next) {

	var options = _.defaults({
		premailerAPI: "http://premailer.dialect.ca/api/0.1/documents",
		fetchHTML: true,
		fetchText: true
	}, _options);

	if (_.has(options, 'baseUrl'))
		options.base_url = options.baseUrl;

	if (_.has(options, 'lineLength'))
		options.line_length = options.lineLength;

	if (_.has(options, 'linkQueryString'))
		options.link_query_string = options.linkQueryString;

	if (_.has(options, 'preserveStyles'))
		options.preserve_styles = options.preserveStyles;

	if (_.has(options, 'removeIds'))
		options.remove_ids = options.removeIds;

	if (_.has(options, 'removeClasses'))
		options.remove_classes = options.removeClasses;

	if (_.has(options, 'removeComments'))
		options.remove_comments = options.removeComments;

	var send = _.pick(options, [
		'html',
		'url',
		'adapter',
		'base_url',
		'line_length',
		'link_query_string',
		'preserve_styles',
		'remove_ids',
		'remove_classes',
		'remove_comments'
	]);

	var rtn = {};
	var apiResponse = null;

	var handlePremailerResponse = function (err, res, body) {
		if (err) {
			next(err);
			return;
		}
		try {
			apiResponse = JSON.parse(body);
		} catch(ex) {
			next(ex);
			return;
		}
		if (options.fetchHTML) {
			getHTML();
		} else if (options.fetchText) {
			getText();
		} else {
			next(false, apiResponse);
		}
	};

	var getHTML = function() {
		request.get(apiResponse.documents.html, function(err, res, body) {
			if (err) {
				next(err);
				return;
			}
			rtn.html = body;
			if (options.fetchText) {
				getText();
			} else {
				next(false, rtn);
			}
		});
	}

	var getText = function (err, res, body) {
		request.get(apiResponse.documents.txt, function(err, res, body) {
			if (err) {
				next(err);
				return;
			}
			rtn.text = body;
			next(false, rtn);
		});
	};

	request.post(options.premailerAPI, { form: send }, handlePremailerResponse);
}<commit_msg>Better Premailer server error handling<commit_after>var _ = require('underscore'),
	request = require('request');

/**
	premailer.prepare()

	Calls the Premailer API to inline css styles (and opther options), then retrieves
	the generated html and text from their respective URLs.

	You must provide either one of the html or url options.

	If both fetchHTML and fetchText options are false, the original api response is
	passed to next().

	Arguments:

		options (object)
			premailerAPI (string)
				The URL of the premailer API. If you're running your own, replace this.
			html (string)
				The html of the email to parse and inline styles.
			url (string)
				The url of the email to fetch then parse and inline styles.
			fetchHTML (boolean)
				Whether to fetch the parsed HTML (you'll usually want this). Defaults
				to true.
			fetchText (boolean)
				Whether to fetch the auto-generated text version (disable this if you
				are providing your own). Defaults to true.

			see http://premailer.dialect.ca/api for full list of options, including:
				adapter
				baseUrl || base_url
				lineLength || line_length
				linkQueryString || link_query_string
				preserveStyles || preserve_styles
				removeIds || remove_ids
				removeClasses || remove_classes
				removeComments || remove_comments

		next function(err, email)
			err (object)
				Error object
			email (object)
				html (string)
					The parsed html with inlined styles
				text (string)
					The text contents of the email
*/
exports.prepare = function(_options, next) {

	var options = _.defaults({
		premailerAPI: "http://premailer.dialect.ca/api/0.1/documents",
		fetchHTML: true,
		fetchText: true
	}, _options);

	if (_.has(options, 'baseUrl'))
		options.base_url = options.baseUrl;

	if (_.has(options, 'lineLength'))
		options.line_length = options.lineLength;

	if (_.has(options, 'linkQueryString'))
		options.link_query_string = options.linkQueryString;

	if (_.has(options, 'preserveStyles'))
		options.preserve_styles = options.preserveStyles;

	if (_.has(options, 'removeIds'))
		options.remove_ids = options.removeIds;

	if (_.has(options, 'removeClasses'))
		options.remove_classes = options.removeClasses;

	if (_.has(options, 'removeComments'))
		options.remove_comments = options.removeComments;

	var send = _.pick(options, [
		'html',
		'url',
		'adapter',
		'base_url',
		'line_length',
		'link_query_string',
		'preserve_styles',
		'remove_ids',
		'remove_classes',
		'remove_comments'
	]);

	var rtn = {};
	var apiResponse = null;

	var handlePremailerResponse = function (err, res, body) {
		if (err) {
			next(err);
			return;
		}
		try {
			apiResponse = JSON.parse(body);
		} catch(ex) {
			if (body.match(/Application Error/gi)) {
				return next('Premailer returned an unkown server error');
			}
			return next(ex);
		}
		if (options.fetchHTML) {
			getHTML();
		} else if (options.fetchText) {
			getText();
		} else {
			next(false, apiResponse);
		}
	};

	var getHTML = function() {
		request.get(apiResponse.documents.html, function(err, res, body) {
			if (err) {
				next(err);
				return;
			}
			rtn.html = body;
			if (options.fetchText) {
				getText();
			} else {
				next(false, rtn);
			}
		});
	}

	var getText = function (err, res, body) {
		request.get(apiResponse.documents.txt, function(err, res, body) {
			if (err) {
				next(err);
				return;
			}
			rtn.text = body;
			next(false, rtn);
		});
	};

	request.post(options.premailerAPI, { form: send }, handlePremailerResponse);
}
<|endoftext|>


<commit_before>// ---------------------------------------------------------------------------------------------------------------------
// Behavior for entities affected by physics
//
// @module physical
// ---------------------------------------------------------------------------------------------------------------------

var util = require('util');

var rfiPhysics = require('rfi-physics');

var BaseEntity = require('./entity');

// ---------------------------------------------------------------------------------------------------------------------

function PhysicalEntity()
{
    BaseEntity.apply(this, arguments);
} // end PhysicalEntity

util.inherits(PhysicalEntity, BaseEntity);

PhysicalEntity.prototype._init = function(babylon, physics)
{
    this.babylon = babylon;
    this.physics = physics;
    this.engine = physics.engine;
    this.body = this.engine.addBody({ mass: 1 });

    // Convert to radians/sec
    var turnRate = this.turn_rate || 2;

    // Scale thrust
    var maxSpeed = {
        x: this.max_speed.x,
        y: this.max_speed.y,
        z: this.max_speed.z
    };

    // Create a target velocity controller
    this.targetVelocityController = new rfiPhysics.TargetVelocityController(this.body, {
        maxLinearThrust: {
            x: maxSpeed.x,
            y: maxSpeed.y,
            z: maxSpeed.z
        },
        linearTargetVelocityScaling: {
            x: maxSpeed.x,
            y: maxSpeed.y,
            z: maxSpeed.z
        },
        linearResponsiveness: {
            x: 10,
            y: 10,
            z: 10
        },

        maxAngularThrust: {
            x: turnRate,
            y: turnRate,
            z: turnRate
        },
        angularTargetVelocityScaling: {
            x: turnRate,
            y: turnRate,
            z: turnRate
        },
        angularResponsiveness: {
            x: 10,
            y: 10,
            z: 10
        }
    });

    Object.defineProperties(this, {
        targetAngularVelocity: {
            get: function(){ return this.targetVelocityController.targetAngularVelocity}.bind(this),
            set: function(angVel){ this.targetVelocityController.targetAngularVelocity = angVel }.bind(this)
        },
        targetLinearVelocity: {
            get: function(){ return this.targetVelocityController.targetLinearVelocity}.bind(this),
            set: function(linVel){ this.targetVelocityController.targetLinearVelocity = linVel }.bind(this)
        }
    });

    // Register our update mesh function to the event that fires after physics has completed
    this.body.addEventListener('postStep', this.updateMesh.bind(this));
}; // end _init

PhysicalEntity.prototype.setMesh = function(mesh)
{
    this.mesh = mesh;

    // We require a rotation quaternion, as we're using physics.
    this.mesh.rotationQuaternion = this.babylon.Quaternion.RotationYawPitchRoll(
        this.mesh.rotation.y,
        this.mesh.rotation.x,
        this.mesh.rotation.z
    );
};

PhysicalEntity.prototype.updateMesh = function()
{
    if(this.mesh && this.body)
    {
        this.mesh.position.copyFrom(this.body.position);
        this.mesh.rotationQuaternion.copyFrom(this.body.quaternion);
    } // end if
}; // end updateMesh

// ---------------------------------------------------------------------------------------------------------------------

module.exports = PhysicalEntity;

angular.module('rfi-client.behaviors').factory('PhysicalEntity', [
    'babylon',
    'PhysicsService',
    function(babylon, physics)
    {
        return function(entityDef, controller)
        {
            var physical = new PhysicalEntity(entityDef, controller);
            physical._init(babylon, physics);

            return physical
        };
    }
]);

// ---------------------------------------------------------------------------------------------------------------------
<commit_msg>added linear responsiveness fro the entity's def.<commit_after>// ---------------------------------------------------------------------------------------------------------------------
// Behavior for entities affected by physics
//
// @module physical
// ---------------------------------------------------------------------------------------------------------------------

var util = require('util');

var rfiPhysics = require('rfi-physics');

var BaseEntity = require('./entity');

// ---------------------------------------------------------------------------------------------------------------------

function PhysicalEntity()
{
    BaseEntity.apply(this, arguments);
} // end PhysicalEntity

util.inherits(PhysicalEntity, BaseEntity);

PhysicalEntity.prototype._init = function(babylon, physics)
{
    this.babylon = babylon;
    this.physics = physics;
    this.engine = physics.engine;
    this.body = this.engine.addBody({ mass: 1 });

    // Create a target velocity controller
    this.targetVelocityController = new rfiPhysics.TargetVelocityController(this.body, {
        maxLinearThrust: {
            x: this.max_speed.x,
            y: this.max_speed.y,
            z: this.max_speed.z
        },
        linearTargetVelocityScaling: {
            x: this.max_speed.x,
            y: this.max_speed.y,
            z: this.max_speed.z
        },
        linearResponsiveness: {
            x: this.linear_responsiveness.x,
            y: this.linear_responsiveness.y,
            z: this.linear_responsiveness.z
        },

        maxAngularThrust: {
            x: this.turn_rate,
            y: this.turn_rate,
            z: this.turn_rate
        },
        angularTargetVelocityScaling: {
            x: this.turn_rate,
            y: this.turn_rate,
            z: this.turn_rate
        },
        angularResponsiveness: {
            x: this.angular_responsiveness.x,
            y: this.angular_responsiveness.y,
            z: this.angular_responsiveness.z
        }
    });

    Object.defineProperties(this, {
        targetAngularVelocity: {
            get: function(){ return this.targetVelocityController.targetAngularVelocity}.bind(this),
            set: function(angVel){ this.targetVelocityController.targetAngularVelocity = angVel }.bind(this)
        },
        targetLinearVelocity: {
            get: function(){ return this.targetVelocityController.targetLinearVelocity}.bind(this),
            set: function(linVel){ this.targetVelocityController.targetLinearVelocity = linVel }.bind(this)
        }
    });

    // Register our update mesh function to the event that fires after physics has completed
    this.body.addEventListener('postStep', this.updateMesh.bind(this));
}; // end _init

PhysicalEntity.prototype.setMesh = function(mesh)
{
    this.mesh = mesh;

    // We require a rotation quaternion, as we're using physics.
    this.mesh.rotationQuaternion = this.babylon.Quaternion.RotationYawPitchRoll(
        this.mesh.rotation.y,
        this.mesh.rotation.x,
        this.mesh.rotation.z
    );
};

PhysicalEntity.prototype.updateMesh = function()
{
    if(this.mesh && this.body)
    {
        this.mesh.position.copyFrom(this.body.position);
        this.mesh.rotationQuaternion.copyFrom(this.body.quaternion);
    } // end if
}; // end updateMesh

// ---------------------------------------------------------------------------------------------------------------------

module.exports = PhysicalEntity;

angular.module('rfi-client.behaviors').factory('PhysicalEntity', [
    'babylon',
    'PhysicsService',
    function(babylon, physics)
    {
        return function(entityDef, controller)
        {
            var physical = new PhysicalEntity(entityDef, controller);
            physical._init(babylon, physics);

            return physical
        };
    }
]);

// ---------------------------------------------------------------------------------------------------------------------
<|endoftext|>


<commit_before>importScripts('/js/sw/merge-responses.js');
importScripts('/js/sw/idb-keyval.js');

function promiseTimer(duration, resolution) {
  return new Promise((resolve, reject) => {
    setTimeout((resolution === 'resolve') ? resolve : reject, duration);
  });
}

const NETWORK_TIMEOUT_SHORT = 1 * 1000;
const NETWORK_TIMEOUT_LONG = 5 * 1000;
const DYNAMIC_CACHE_UPDATE_INTERVAL = (location.hostname === 'localhost') ? (10 * 1000) : (60 * 60 * 1000);
const FRAG_PAGE_PATTERN = /\/((articles|topics)\/)?/;
const NOCACHE_URL_PATTERN = /^https?\:\/\/www\.google\-analytics\.com(\/r)?\/collect/;

const responseMetaData = new Map();
let cacheUpdateInProgress = false;

// Adding `install` event listener
self.addEventListener('install', event => {
  console.log('[SW] Install');

  event.waitUntil(async function () {
    try {
      const oldCacheID = await idbKeyval.get('cacheID');
      const newCacheID = (oldCacheID || 0) + 1;
      await idbKeyval.set('cacheID', newCacheID);
      await idbKeyval.set('dynamicCacheUpdateTime', 0);
      const cache = await caches.open('v'+newCacheID+'-static');
      await cache.addAll([
        '/shell/fragments/header',
        '/shell/fragments/footer',
        '/shell/offline',
        '/js/sw/merge-responses.js',
        '/js/sw/idb-keyval.js'
      ]);
      console.info('[SW] New cache ID is '+newCacheID);
      return self.skipWaiting();
    } catch (e) {
      console.error('[SW] Failed to build SW cache', e);
    }
  }());
});

self.addEventListener('fetch', event => {
  const fragUrl = new URL(event.request.url);
  fragUrl.searchParams.set('frag', 1);
  const useFrag = (event.request.mode === 'navigate' && FRAG_PAGE_PATTERN.test(event.request.url));
  const fetchReq = useFrag ? new Request(fragUrl.toString(), { mode: 'cors', credentials: 'include' }) : event.request;

  // If the performance timings buffer fills up, no more perf data will be recorded.  Clear it on each navigation to ensure we don't hit the limit.
  if (event.request.mode === 'navigate') {
    responseMetaData.clear();
    self.performance.clearResourceTimings();
    if ('clearServerTimings' in self.performance) {
      self.performance.clearServerTimings();
    }
  }

  // Cache-first for shell, network-first for content
  event.respondWith(async function () {
    try {
      const cacheID = await idbKeyval.get('cacheID');
      const staticCache = await caches.open('v'+cacheID+'-static');
      const dynamicCache = await caches.open('v'+cacheID+'-dynamic');

      // Pending preloadResponse if it exists (could be frag or not)
      const preloadResp = await event.preloadResponse;
      if (preloadResp) return preloadResp;

      // Failing that, try static cache lookups (which should only apply if not a page navigation)
      if (event.request.mode !== 'navigate') {
        const staticResp = await Promise.all([
          staticCache.match(event.request.url),
          staticCache.match(fragUrl.toString())
        ]).then(([r1, r2]) => r1 || r2);
        if (staticResp) return staticResp;
      }

      // Failing that, try the network if online (but use dynamic cache if network is unavailable or slow)

      const cacheFetchPromise = dynamicCache.match(fetchReq).then(r => {
        r && responseMetaData.set(fetchReq.url, {source:'swCache'});
        return r;
      });

      const netResp = await (function () {
        if (navigator.onLine) {
          const netFetchPromise = fetch(fetchReq).then(resp => {

            // Pick up any static assets that should be cached eg fonts, images, CSS, JS
            const ccHeader = resp.headers.get('cache-control');
            const isUncacheable = NOCACHE_URL_PATTERN.test(fetchReq.url) || (ccHeader && ccHeader.includes('no-store'));
            if (event.request.mode !== 'navigate' && !isUncacheable) {
              console.log('Adding '+fetchReq.url+' to static cache');
              staticCache.put(fetchReq, resp.clone());
            }
            return resp;
          });
          return Promise.resolve()
            .then(() => Promise.race([netFetchPromise, promiseTimer(NETWORK_TIMEOUT_SHORT, 'reject')]))
            .catch(() => Promise.race([netFetchPromise, cacheFetchPromise, promiseTimer(NETWORK_TIMEOUT_LONG, 'reject')]))
          ;
        } else {
          return cacheFetchPromise;
        }
      }());

      // Debug requests that hit the network
      //console.log('[SW] Fetch', fetchReq, netResp);

      if (!netResp) throw new Error('No response available');

      if (!netResp.headers.get('Fragment')){
         return netResp;
      } else {

        // Construct a full page if the response was a frag
        const head = staticCache.match('/shell/fragments/header');
        const foot = staticCache.match('/shell/fragments/footer');
        const mergedResp = mergeResponses([head, netResp, foot], netResp.headers);
        console.log('[SW] Merging frag for ' + fetchReq.url);

        // TODO: Why does this error with 'event is already finished'?
        //event.waitUntil(mergedResp.done);

        return mergedResp.response;
      }

    } catch(err) {
      console.log("[SW] Fetch fail for "+fetchReq.url, fetchReq, err);
      if (event.request.mode === 'navigate') {
        return caches.match('/shell/offline');
      } else {
        return new Response('', {status: 503, statusText: 'Offline from SW'});
      }
    }
  }());

  // Update dynamic cache if needed
  if (!cacheUpdateInProgress && navigator.onLine && event.request.mode === 'navigate') {
    (async () => {
      const lastUpdateTime = await idbKeyval.get('dynamicCacheUpdateTime');
      if (!lastUpdateTime || lastUpdateTime < (Date.now() - DYNAMIC_CACHE_UPDATE_INTERVAL)) {
        console.log('[SW] Incrementally updating dynamic cache...');
        cacheUpdateInProgress = true;
        const cacheID = await idbKeyval.get('cacheID');
        const cache = await caches.open('v'+cacheID+'-dynamic');
        const newUrlList = await fetch("/shell/files/dynamic").then(resp => resp.json());
        const existingUrlList = await cache.keys().then(reqs => reqs.map(r => r.url.replace(/https?\:\/\/[^\/]+/, '')));
        const urlsToAdd = newUrlList.filter(u => !existingUrlList.includes(u));
        const urlsToDel = existingUrlList.filter(u => !newUrlList.includes(u));
        await Promise.all(urlsToDel.map(u => cache.delete(u)).concat(cache.addAll(urlsToAdd)));
        await idbKeyval.set('dynamicCacheUpdateTime', Date.now());
        console.log("[SW] Cache update complete.  Deletions: "+urlsToDel.length+", additions: "+urlsToAdd.length);
        cacheUpdateInProgress = false;
      }
    })();
  }
});

self.addEventListener('activate', (event) => {
  console.log('[SW] Activating new service worker');

  // Enable Navigation preload if it is available
  //if (self.registration.navigationPreload) {
  //  console.log('[SW] NavigationPreload enabled');
  //  self.registration.navigationPreload.enable();
  //}

  // Remove outdated caches
  event.waitUntil(async function() {
    const cacheID = await idbKeyval.get('cacheID');
    const cachePrefix = 'v'+cacheID;
    const cacheNames = await caches.keys();
    await Promise.all(cacheNames.map(existingCacheName => {
      if (!existingCacheName.startsWith(cachePrefix)) {
        console.log("[SW] Deleted outdated cache called " + existingCacheName);
        return caches.delete(existingCacheName);
      }
    }));

    // Activate current one instead of waiting for the old one to finish
    return self.clients.claim();
  }());
});

self.addEventListener('message', function(event) {
  console.log('[SW] Message', event.data);
  if (event.data.name === 'getPerfEntries') {

    const fragUrl = new URL(event.data.data.url);
    fragUrl.searchParams.set('frag', 1);

    // Use frag URL here
    const entries = self.performance.getEntriesByName(fragUrl.toString());

    // Get latest resource perf entry (there should only be one, if buffer is cleared on navigate)
    // TODO: hacky. See https://developers.google.com/web/updates/2015/07/measuring-performance-in-a-service-worker?google_comment_id=z13mv5i4vw31gjpii04cjh4rhufuzfob34w
    // TODO: No servertiming available in this context?
    const latestResourceEntry = entries.filter(entry => entry.entryType = 'resource').pop();

    // JSON dance here otherwise we get an error about not being able to clone
    const timingData = latestResourceEntry ? JSON.parse(JSON.stringify(latestResourceEntry)) : {};

    // Merge any data from the response metadata map
    const respData = responseMetaData.get(fragUrl.toString()) || {};
    const combinedData = Object.assign(timingData, respData);

    event.ports[0].postMessage({status:'ok', data: combinedData});

  } else {
    event.ports[0].postMessage({status:'error', data: { message: 'No such method name' }});
  }

});
<commit_msg>Debug source<commit_after>importScripts('/js/sw/merge-responses.js');
importScripts('/js/sw/idb-keyval.js');

function promiseTimer(duration, resolution) {
  return new Promise((resolve, reject) => {
    setTimeout((resolution === 'resolve') ? resolve : reject, duration);
  });
}

const NETWORK_TIMEOUT_SHORT = 1 * 1000;
const NETWORK_TIMEOUT_LONG = 5 * 1000;
const DYNAMIC_CACHE_UPDATE_INTERVAL = (location.hostname === 'localhost') ? (10 * 1000) : (60 * 60 * 1000);
const FRAG_PAGE_PATTERN = /\/((articles|topics)\/)?/;
const NOCACHE_URL_PATTERN = /^https?\:\/\/www\.google\-analytics\.com(\/r)?\/collect/;

const responseMetaData = new Map();
let cacheUpdateInProgress = false;

// Adding `install` event listener
self.addEventListener('install', event => {
  console.log('[SW] Install');

  event.waitUntil(async function () {
    try {
      const oldCacheID = await idbKeyval.get('cacheID');
      const newCacheID = (oldCacheID || 0) + 1;
      await idbKeyval.set('cacheID', newCacheID);
      await idbKeyval.set('dynamicCacheUpdateTime', 0);
      const cache = await caches.open('v'+newCacheID+'-static');
      await cache.addAll([
        '/shell/fragments/header',
        '/shell/fragments/footer',
        '/shell/offline',
        '/js/sw/merge-responses.js',
        '/js/sw/idb-keyval.js'
      ]);
      console.info('[SW] New cache ID is '+newCacheID);
      return self.skipWaiting();
    } catch (e) {
      console.error('[SW] Failed to build SW cache', e);
    }
  }());
});

self.addEventListener('fetch', event => {
  const fragUrl = new URL(event.request.url);
  fragUrl.searchParams.set('frag', 1);
  const useFrag = (event.request.mode === 'navigate' && FRAG_PAGE_PATTERN.test(event.request.url));
  const fetchReq = useFrag ? new Request(fragUrl.toString(), { mode: 'cors', credentials: 'include' }) : event.request;

  // If the performance timings buffer fills up, no more perf data will be recorded.  Clear it on each navigation to ensure we don't hit the limit.
  if (event.request.mode === 'navigate') {
    responseMetaData.clear();
    self.performance.clearResourceTimings();
    if ('clearServerTimings' in self.performance) {
      self.performance.clearServerTimings();
    }
  }

  // Cache-first for shell, network-first for content
  event.respondWith(async function () {
    try {
      const cacheID = await idbKeyval.get('cacheID');
      const staticCache = await caches.open('v'+cacheID+'-static');
      const dynamicCache = await caches.open('v'+cacheID+'-dynamic');

      // Pending preloadResponse if it exists (could be frag or not)
      const preloadResp = await event.preloadResponse;
      if (preloadResp) return preloadResp;

      // Failing that, try static cache lookups (which should only apply if not a page navigation)
      if (event.request.mode !== 'navigate') {
        const staticResp = await Promise.all([
          staticCache.match(event.request.url),
          staticCache.match(fragUrl.toString())
        ]).then(([r1, r2]) => r1 || r2);
        if (staticResp) return staticResp;
      }

      // Failing that, try the network if online (but use dynamic cache if network is unavailable or slow)

      const cacheFetchPromise = dynamicCache.match(fetchReq).then(r => {
        r && responseMetaData.set(fetchReq.url, {source:'swCache'}) && console.log('Recording resp from SW cache');
        return r;
      });

      const netResp = await (function () {
        if (navigator.onLine) {
          const netFetchPromise = fetch(fetchReq).then(resp => {

            // Pick up any static assets that should be cached eg fonts, images, CSS, JS
            const ccHeader = resp.headers.get('cache-control');
            const isUncacheable = NOCACHE_URL_PATTERN.test(fetchReq.url) || (ccHeader && ccHeader.includes('no-store'));
            if (event.request.mode !== 'navigate' && !isUncacheable) {
              console.log('Adding '+fetchReq.url+' to static cache');
              staticCache.put(fetchReq, resp.clone());
            }
            return resp;
          });
          return Promise.resolve()
            .then(() => Promise.race([netFetchPromise, promiseTimer(NETWORK_TIMEOUT_SHORT, 'reject')]))
            .catch(() => Promise.race([netFetchPromise, cacheFetchPromise, promiseTimer(NETWORK_TIMEOUT_LONG, 'reject')]))
          ;
        } else {
          return cacheFetchPromise;
        }
      }());

      // Debug requests that hit the network
      //console.log('[SW] Fetch', fetchReq, netResp);

      if (!netResp) throw new Error('No response available');

      if (!netResp.headers.get('Fragment')){
         return netResp;
      } else {

        // Construct a full page if the response was a frag
        const head = staticCache.match('/shell/fragments/header');
        const foot = staticCache.match('/shell/fragments/footer');
        const mergedResp = mergeResponses([head, netResp, foot], netResp.headers);
        console.log('[SW] Merging frag for ' + fetchReq.url);

        // TODO: Why does this error with 'event is already finished'?
        //event.waitUntil(mergedResp.done);

        return mergedResp.response;
      }

    } catch(err) {
      if (!navigator.onLine) {
        console.log("[SW] Fetch fail for "+fetchReq.url, fetchReq, err);
      }
      if (event.request.mode === 'navigate') {
        return caches.match('/shell/offline');
      } else {
        return new Response('', {status: 503, statusText: 'Offline from SW'});
      }
    }
  }());

  // Update dynamic cache if needed
  if (!cacheUpdateInProgress && navigator.onLine && event.request.mode === 'navigate') {
    (async () => {
      const lastUpdateTime = await idbKeyval.get('dynamicCacheUpdateTime');
      if (!lastUpdateTime || lastUpdateTime < (Date.now() - DYNAMIC_CACHE_UPDATE_INTERVAL)) {
        console.log('[SW] Incrementally updating dynamic cache...');
        cacheUpdateInProgress = true;
        const cacheID = await idbKeyval.get('cacheID');
        const cache = await caches.open('v'+cacheID+'-dynamic');
        const newUrlList = await fetch("/shell/files/dynamic").then(resp => resp.json());
        const existingUrlList = await cache.keys().then(reqs => reqs.map(r => r.url.replace(/https?\:\/\/[^\/]+/, '')));
        const urlsToAdd = newUrlList.filter(u => !existingUrlList.includes(u));
        const urlsToDel = existingUrlList.filter(u => !newUrlList.includes(u));
        await Promise.all(urlsToDel.map(u => cache.delete(u)).concat(cache.addAll(urlsToAdd)));
        await idbKeyval.set('dynamicCacheUpdateTime', Date.now());
        console.log("[SW] Cache update complete.  Deletions: "+urlsToDel.length+", additions: "+urlsToAdd.length);
        cacheUpdateInProgress = false;
      }
    })();
  }
});

self.addEventListener('activate', (event) => {
  console.log('[SW] Activating new service worker');

  // Enable Navigation preload if it is available
  //if (self.registration.navigationPreload) {
  //  console.log('[SW] NavigationPreload enabled');
  //  self.registration.navigationPreload.enable();
  //}

  // Remove outdated caches
  event.waitUntil(async function() {
    const cacheID = await idbKeyval.get('cacheID');
    const cachePrefix = 'v'+cacheID;
    const cacheNames = await caches.keys();
    await Promise.all(cacheNames.map(existingCacheName => {
      if (!existingCacheName.startsWith(cachePrefix)) {
        console.log("[SW] Deleted outdated cache called " + existingCacheName);
        return caches.delete(existingCacheName);
      }
    }));

    // Activate current one instead of waiting for the old one to finish
    return self.clients.claim();
  }());
});

self.addEventListener('message', function(event) {
  if (event.data.name === 'getPerfEntries') {

    const fragUrl = new URL(event.data.data.url);
    fragUrl.searchParams.set('frag', 1);

    // Use frag URL here
    const entries = self.performance.getEntriesByName(fragUrl.toString());

    // Get latest resource perf entry (there should only be one, if buffer is cleared on navigate)
    // TODO: hacky. See https://developers.google.com/web/updates/2015/07/measuring-performance-in-a-service-worker?google_comment_id=z13mv5i4vw31gjpii04cjh4rhufuzfob34w
    // TODO: No servertiming available in this context?
    const latestResourceEntry = entries.filter(entry => entry.entryType = 'resource').pop();

    // JSON dance here otherwise we get an error about not being able to clone
    const timingData = latestResourceEntry ? JSON.parse(JSON.stringify(latestResourceEntry)) : {};

    // Merge any data from the response metadata map
    const respData = responseMetaData.get(fragUrl.toString()) || {};
    const combinedData = Object.assign(timingData, respData);

    console.log('[SW] Message', event.data, combinedData);
    event.ports[0].postMessage({status:'ok', data: combinedData});

  } else {
    event.ports[0].postMessage({status:'error', data: { message: 'No such method name' }});
  }

});
<|endoftext|>


<commit_before>(function() {
  'use strict';
  /*
   *
   *
   * @name lineSeriesLabels
   */
  d4.feature('lineSeriesLabels', function(name) {
    var trackMouse = function(scope, data) {
      if (d4.functor(scope.accessors.trackMouse).bind(this)()) {
        var label = this.svg.select('.' + name).selectAll('.' + name + 'DataPoint').data(data);
        label.enter().append('text');
        label.exit().remove();
        label.attr('class', 'line-series-data-point')
          .text(d4.functor(scope.accessors.dataPointText).bind(this))
          .attr('data-key', function(d) {
            return d.key;
          })
          .attr('class', d4.functor(scope.accessors.classes).bind(this));

        this.svg.select('.' + name).append('rect')
          .attr('class', 'overlay')
          .style('fill', 'none')
          .attr('width', this.width)
          .attr('height', this.height)
          .on('mouseover', function() {
            label.style('display', null);
          })
          .on('mouseout', function() {
            label.style('display', 'none');
          })
          .on('mousemove', d4.functor(scope.accessors.mouseMove));
      }
    };

    return {
      accessors: {
        classes: function(d, n) {
          return 'stroke series' + n;
        },

        mouseMove: function() {
        },

        text: function(d) {
          return d.key;
        },

        trackMouse: false,

        x: function(d) {
          return this.x(d.values[d.values.length - 1][this.x.$key]);
        },

        y: function(d) {
          return this.y(d.values[d.values.length - 1][this.y.$key]);
        }
      },
      render: function(scope, data, selection) {
        selection.append('g').attr('class', name);
        var label = this.svg.select('.' + name).selectAll('.' + name).data(data);
        label.enter().append('text');
        label.exit().remove();
        label.attr('class', 'line-series-label')
          .text(d4.functor(scope.accessors.text).bind(this))
          .attr('x', d4.functor(scope.accessors.x).bind(this))
          .attr('y', d4.functor(scope.accessors.y).bind(this))
          .attr('data-key', function(d) {
            return d.key;
          })
          .attr('class', d4.functor(scope.accessors.classes).bind(this));
        trackMouse.bind(this)(scope, data, selection);

        return label;
      }
    };
  });
}).call(this);
<commit_msg>additional work on the line series labels.<commit_after>(function() {
  'use strict';
  /*
   *
   *
   * @name lineSeriesLabels
   */
  d4.feature('lineSeriesLabels', function(name) {
    var trackMouse = function(scope, data) {
      if (d4.functor(scope.accessors.trackMouse).bind(this)()) {
        var label = this.svg.select('.' + name).selectAll('.' + name + 'DataPoint').data(data);
        label.enter().append('text');
        label.exit().remove();
        label.attr('class', 'line-series-data-point')
          .text(d4.functor(scope.accessors.dataPointText).bind(this))
          .attr('data-key', function(d) {
            return d.key;
          })
          .attr('class', d4.functor(scope.accessors.classes).bind(this));

        this.svg.select('.' + name).append('rect')
          .attr('class', 'overlay')
          .style('fill', 'none')
          .attr('width', this.width)
          .attr('height', this.height)
          .on('mouseover', function() {
            label.style('display', null);
          })
          .on('mouseout', function() {
            label.style('display', 'none');
          })
          .on('mousemove', d4.functor(scope.accessors.mouseMove));
      }
    };

    return {
      accessors: {
        classes: function(d, n) {
          return 'stroke series' + n;
        },

        mouseMove: function() {
        },

        text: function(d) {
          return d.key;
        },

        // Fixme: This should be renamed to something more expressive.
        trackMouse: false,

        x: function(d) {
          return this.x(d.values[d.values.length - 1][this.x.$key]);
        },

        y: function(d) {
          return this.y(d.values[d.values.length - 1][this.y.$key]);
        }
      },
      render: function(scope, data, selection) {
        selection.append('g').attr('class', name);
        var label = this.svg.select('.' + name).selectAll('.' + name).data(data);
        label.enter().append('text');
        label.exit().remove();
        label.attr('class', 'line-series-label')
          .text(d4.functor(scope.accessors.text).bind(this))
          .attr('x', d4.functor(scope.accessors.x).bind(this))
          .attr('y', d4.functor(scope.accessors.y).bind(this))
          .attr('data-key', function(d) {
            return d.key;
          })
          .attr('class', d4.functor(scope.accessors.classes).bind(this));
        trackMouse.bind(this)(scope, data, selection);

        return label;
      }
    };
  });
}).call(this);
<|endoftext|>


<commit_before>foam.CLASS({
  package: 'foam.build',
  name: 'DirectoryTreeDAO',
  extends: 'foam.dao.AbstractDAO',
  documentation: `Stores objects in a directory tree.`,
  requires: [
    'foam.json2.Serializer',
    'foam.json2.Deserializer'
  ],
  properties: [
    {
      class: 'String',
      name: 'root'
    },
    {
      name: 'serializer',
      factory: function() {
        return this.Serializer.create();
      }
    },
    {
      name: 'deserializer',
      factory: function() {
        return this.Deserializer.create();
      }
    },
    {
      name: 'sep',
      factory: function() { return require('path').sep; }
    },
    {
      name: 'fs',
      factory: function() {
        return require('fs')
      },
    }
  ],
  methods: [
    {
      name: 'pathFor_',
      args: [
        { name: 'id', type: 'String' },
      ],
      code: function(id) {
        return this.root + this.sep + id.replace(/\./g, this.sep) + '.js';
      }
    },
    {
      name: 'pathsAt_',
      args: [
        { name: 'root', type: 'String' },
      ],
      code: function(root) {
        var fs = this.fs;
        var sep = this.sep;
        var dirs = [root];
        var files = [];
        while ( dirs.length ) {
          var dir = dirs.pop();
          fs.readdirSync(dir).forEach(function(f) {
            var path = dir + sep + f;
            var lstat = fs.lstatSync(path);
            if ( lstat.isDirectory() ) dirs.push(path)
            else files.push(path)
          })
        }
        return files;
      }
    },
    {
      name: 'loadFile_',
      args: [
        { name: 'x', type: 'Context' },
        { name: 'path', type: 'Stirng' }
      ],
      type: 'foam.core.FObject',
      async: true,
      code: function(x, path) {
        var s = this.fs.readFileSync(path, 'utf-8');
        return this.deserializer.aparseString(x, s)
      }
    },
    {
      name: 'select_',
      code: function(x, sink, skip, limit, order, predicate) {
        sink = sink || this.ArraySink.create();

        var mysink = this.decorateSink_(sink, skip, limit, order, predicate);

        var detached = false;
        var sub = foam.core.FObject.create();
        sub.onDetach(function() { detached = true; });

        return Promise.all(this.pathsAt_(this.root).map(function(path) {
          return this.loadFile_(x, path).then(function(obj) {
            if ( detached ) return;
            mysink.put(obj, sub);
          });
        }.bind(this))).then(function(objs) {
          mysink.eof();
          return sink;
        });
      }
    },
    {
      name: 'find_',
      code: function(x, id) {
        return this.loadFile_(x, this.pathFor_(id));
      }
    },
    {
      name: 'put_',
      code: function(x, obj) {
        var path = this.pathFor_(obj.id);

        var dirs = path.split(this.sep);
        dirs.pop(); // Don't want a dir with the filename.
        var p = '';
        while ( dirs.length ) {
          p = p + dirs.shift() + this.sep;
          if ( ! this.fs.existsSync(p) ) this.fs.mkdirSync(p);
        }

        var s = this.serializer.stringify(x, obj)
        this.fs.writeFileSync(path, s, 'utf8');
        return Promise.resolve(obj);
      }
    },
    {
      name: 'remove_',
      code: function(x, obj) {
        return this.find(obj.id).then(function(o) {
          if ( o == null ) return obj;

        }.bind(this));
      }
    },
    {
      name: 'execute',
      code: function() {
        // TODO remove all this. It's just for testing for now.
        var self = this;
        self.root = '/tmp/foam2out/';
        var classloader = self.__context__.classloader;

        Promise.all([
          classloader.load('foam.build.DirCrawlModelDAO'),
          classloader.load('foam.dao.DAOSink'),
        ]).then(function() {
          var sink = foam.dao.DAOSink.create({ dao: self })
          var dao = foam.build.DirCrawlModelDAO.create()
          return dao.select(sink);
        }).then(function() {
          return self.find('foam.apploader.Classloader')
        }).then(function(o) {
          console.log(o.id);
          /*
        }).then(function() {
          return self.select();
        }).then(function(sink) {
          sink.array.forEach(function(model) {
            console.log(model.id);
          })
          */
        });
      },
    },
  ]
});
<commit_msg>Fix typo.<commit_after>foam.CLASS({
  package: 'foam.build',
  name: 'DirectoryTreeDAO',
  extends: 'foam.dao.AbstractDAO',
  documentation: `Stores objects in a directory tree.`,
  requires: [
    'foam.json2.Serializer',
    'foam.json2.Deserializer'
  ],
  properties: [
    {
      class: 'String',
      name: 'root'
    },
    {
      name: 'serializer',
      factory: function() {
        return this.Serializer.create();
      }
    },
    {
      name: 'deserializer',
      factory: function() {
        return this.Deserializer.create();
      }
    },
    {
      name: 'sep',
      factory: function() { return require('path').sep; }
    },
    {
      name: 'fs',
      factory: function() {
        return require('fs')
      },
    }
  ],
  methods: [
    {
      name: 'pathFor_',
      args: [
        { name: 'id', type: 'String' },
      ],
      code: function(id) {
        return this.root + this.sep + id.replace(/\./g, this.sep) + '.js';
      }
    },
    {
      name: 'pathsAt_',
      args: [
        { name: 'root', type: 'String' },
      ],
      code: function(root) {
        var fs = this.fs;
        var sep = this.sep;
        var dirs = [root];
        var files = [];
        while ( dirs.length ) {
          var dir = dirs.pop();
          fs.readdirSync(dir).forEach(function(f) {
            var path = dir + sep + f;
            var lstat = fs.lstatSync(path);
            if ( lstat.isDirectory() ) dirs.push(path)
            else files.push(path)
          })
        }
        return files;
      }
    },
    {
      name: 'loadFile_',
      args: [
        { name: 'x', type: 'Context' },
        { name: 'path', type: 'String' }
      ],
      type: 'foam.core.FObject',
      async: true,
      code: function(x, path) {
        var s = this.fs.readFileSync(path, 'utf-8');
        return this.deserializer.aparseString(x, s)
      }
    },
    {
      name: 'select_',
      code: function(x, sink, skip, limit, order, predicate) {
        sink = sink || this.ArraySink.create();

        var mysink = this.decorateSink_(sink, skip, limit, order, predicate);

        var detached = false;
        var sub = foam.core.FObject.create();
        sub.onDetach(function() { detached = true; });

        return Promise.all(this.pathsAt_(this.root).map(function(path) {
          return this.loadFile_(x, path).then(function(obj) {
            if ( detached ) return;
            mysink.put(obj, sub);
          });
        }.bind(this))).then(function(objs) {
          mysink.eof();
          return sink;
        });
      }
    },
    {
      name: 'find_',
      code: function(x, id) {
        return this.loadFile_(x, this.pathFor_(id));
      }
    },
    {
      name: 'put_',
      code: function(x, obj) {
        var path = this.pathFor_(obj.id);

        var dirs = path.split(this.sep);
        dirs.pop(); // Don't want a dir with the filename.
        var p = '';
        while ( dirs.length ) {
          p = p + dirs.shift() + this.sep;
          if ( ! this.fs.existsSync(p) ) this.fs.mkdirSync(p);
        }

        var s = this.serializer.stringify(x, obj)
        this.fs.writeFileSync(path, s, 'utf8');
        return Promise.resolve(obj);
      }
    },
    {
      name: 'remove_',
      code: function(x, obj) {
        return this.find(obj.id).then(function(o) {
          if ( o == null ) return obj;

        }.bind(this));
      }
    },
    {
      name: 'execute',
      code: function() {
        // TODO remove all this. It's just for testing for now.
        var self = this;
        self.root = '/tmp/foam2out/';
        var classloader = self.__context__.classloader;

        Promise.all([
          classloader.load('foam.build.DirCrawlModelDAO'),
          classloader.load('foam.dao.DAOSink'),
        ]).then(function() {
          var sink = foam.dao.DAOSink.create({ dao: self })
          var dao = foam.build.DirCrawlModelDAO.create()
          return dao.select(sink);
        }).then(function() {
          return self.find('foam.apploader.Classloader')
        }).then(function(o) {
          console.log(o.id);
          /*
        }).then(function() {
          return self.select();
        }).then(function(sink) {
          sink.array.forEach(function(model) {
            console.log(model.id);
          })
          */
        });
      },
    },
  ]
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2020 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
    package: 'foam.nanos.menu',
    name: 'MenuToolBar',
    extends: 'foam.u2.View',

    documentation: 'A toolbar made out of menus according to a given classification (menu id prefix).',

    implements: [
      'foam.mlang.Expressions'
    ],

    imports: [
      'menuDAO',
      'pushMenu'
    ],

    requires: [
      'foam.nanos.menu.Menu'
    ],

    css: `
      ^ {
        width: 504px;
      }
      ^title {
        margin: 24px;
        font-size: 24px;
        font-weight: 900;
      }
      ^options {
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        align-items: center;
        padding: 0 36px 32px 36px;
      }
      ^option {
        display: flex;
        flex-direction: column;
        align-items: center;
        width: 96px;
        height: 118px;
        padding-bottom: 5px;
        padding-top: 15px;
        border-radius: 3px;
        box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.16);
        border: solid 1px #edf0f5;
      }
      ^option:hover {
        border: solid 1px #604aff;
        cursor: pointer;
      }
      ^option-title {
        text-align: center;
        font-size: 12px;
        font-weight: 600;
        line-height: 1.5;
      }
      ^option-icon-container {
        display: flex;
        justify-content: center;
        align-items: center;
        height: 62px;
        width: 62px;
        margin-bottom: 8px;
      }
      ^option-icon {
        width: 50px;
        height: 50px;
      }
    `,

    properties: [
      {
        class: 'String',
        name: 'classification'
      },
      {
        class: 'String',
        name: 'parent'
      },
      {
        class: 'String',
        name: 'title'
      },
      {
        class: 'foam.dao.DAOProperty',
        name: 'dao',
        factory: function() {
          return this.menuDAO
            .where(
              this.AND(
                this.STARTS_WITH(this.Menu.ID, this.classification),
                this.EQ(this.Menu.PARENT, this.parent),
                this.NEQ(this.Menu.ID, `${this.classification}.toolbar`)
              )
            )
            .orderBy(this.Menu.ORDER);
        }
      }
    ],

    methods: [
      function initE() {
        var self = this;
        this
          .addClass(this.myClass())
          .start()
            .addClass(this.myClass('title'))
            .add(this.title)
          .end()
          .start()
            .addClass(this.myClass('options'))
            .select(this.dao$proxy, function(menu) {
              return this.E().start()
                .addClass(self.myClass('option'))
                .on('click', function() {
                  self.pushMenu(menu.id);
                })
                .start()
                  .addClass(self.myClass('option-icon-container'))
                  .start('img')
                    .addClass(self.myClass('option-icon'))
                    .attr('src', menu.icon)
                  .end()
                .end()
                .start()
                  .addClass(self.myClass('option-title'))
                  .add(menu.label)
                .end()
              .end();
            })
          .end();
      }
    ]
  });<commit_msg>fix spacing<commit_after>/**
 * @license
 * Copyright 2020 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.menu',
  name: 'MenuToolBar',
  extends: 'foam.u2.View',

  documentation: 'A toolbar made out of menus according to a given classification (menu id prefix).',

  implements: [
    'foam.mlang.Expressions'
  ],

  imports: [
    'menuDAO',
    'pushMenu'
  ],

  requires: [
    'foam.nanos.menu.Menu'
  ],

  css: `
    ^ {
      width: 504px;
    }
    ^title {
      margin: 24px;
      font-size: 24px;
      font-weight: 900;
    }
    ^options {
      display: flex;
      flex-direction: row;
      justify-content: space-between;
      align-items: center;
      padding: 0 36px 32px 36px;
    }
    ^option {
      display: flex;
      flex-direction: column;
      align-items: center;
      width: 96px;
      height: 118px;
      padding-bottom: 5px;
      padding-top: 15px;
      border-radius: 3px;
      box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.16);
      border: solid 1px #edf0f5;
    }
    ^option:hover {
      border: solid 1px #604aff;
      cursor: pointer;
    }
    ^option-title {
      text-align: center;
      font-size: 12px;
      font-weight: 600;
      line-height: 1.5;
    }
    ^option-icon-container {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 62px;
      width: 62px;
      margin-bottom: 8px;
    }
    ^option-icon {
      width: 50px;
      height: 50px;
    }
  `,

  properties: [
    {
      class: 'String',
      name: 'classification'
    },
    {
      class: 'String',
      name: 'parent'
    },
    {
      class: 'String',
      name: 'title'
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'dao',
      factory: function() {
        return this.menuDAO
          .where(
            this.AND(
              this.STARTS_WITH(this.Menu.ID, this.classification),
              this.EQ(this.Menu.PARENT, this.parent),
              this.NEQ(this.Menu.ID, `${this.classification}.toolbar`)
            )
          )
          .orderBy(this.Menu.ORDER);
      }
    }
  ],

  methods: [
    function initE() {
      var self = this;
      this
        .addClass(this.myClass())
        .start()
          .addClass(this.myClass('title'))
          .add(this.title)
        .end()
        .start()
          .addClass(this.myClass('options'))
          .select(this.dao$proxy, function(menu) {
            return this.E().start()
              .addClass(self.myClass('option'))
              .on('click', function() {
                self.pushMenu(menu.id);
              })
              .start()
                .addClass(self.myClass('option-icon-container'))
                .start('img')
                  .addClass(self.myClass('option-icon'))
                  .attr('src', menu.icon)
                .end()
              .end()
              .start()
                .addClass(self.myClass('option-title'))
                .add(menu.label)
              .end()
            .end();
          })
        .end();
    }
  ]
});<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.menu',
  name: 'SubMenuView',
  extends: 'foam.nanos.menu.PopupMenu',

  requires: [ 'foam.nanos.menu.Menu' ],

  properties: [ 'X', 'menu' ],

  css: `
    ^inner {
      -moz-box-shadow: 0px 0px 67px -15px rgba(0,0,0,0.75);
      -webkit-box-shadow: 0px 0px 67px -15px rgba(0,0,0,0.75);
      border-bottom-left-radius: 5px;
      border-bottom-right-radius: 5px;
      box-shadow: 0px 0px 67px -15px rgba(0,0,0,0.75);
      font-weight: 300;
      position: absolute;
      top: 65px;
      width: 240px;
    }
    ^inner div {
      box-sizing: border-box;
      padding: 8px 24px;
      padding-right: 48px;
      cursor: pointer;
      background: white;
      color: black;
      border-left: solid 1px /*%GREY5%*/ #f5f7fa;
      border-right: solid 1px /*%GREY5%*/ #f5f7fa;
    }
    ^inner div:last-child {
      border-bottom-left-radius: 5px;
      border-bottom-right-radius: 5px;
    }
    ^inner div:hover {
      background: /*%PRIMARY5%*/ #e5f1fc !important;
      border-left: solid 1px /*%PRIMARY5%*/ #e5f1fc;
      border-right: solid 1px /*%PRIMARY5%*/ #e5f1fc;
    }
  `,

  methods: [
    function initE() {
      this.addClass(this.myClass());
      var self = this;
      var menu = this.menu;
      var X    = this.__subContext__;

      menu.children.orderBy(this.Menu.ORDER, this.Menu.LABEL).select({
        put: function(menu) {
          if ( ! menu.handler ) return;
          self.start('div').call(function() {
            var e = this;
            this
              .on('click', function() {
                // TODO: if a submenu, don't close until child closed
                self.close();
                menu.launch_(X, e);
              })
              .add(menu.label);
            })
          .end();
        },
        eof: function() {}
      });
    }
  ]
});
<commit_msg>add translation to sub menu<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.menu',
  name: 'SubMenuView',
  extends: 'foam.nanos.menu.PopupMenu',

  requires: [ 'foam.nanos.menu.Menu' ],

  properties: [ 'X', 'menu' ],

  css: `
    ^inner {
      -moz-box-shadow: 0px 0px 67px -15px rgba(0,0,0,0.75);
      -webkit-box-shadow: 0px 0px 67px -15px rgba(0,0,0,0.75);
      border-bottom-left-radius: 5px;
      border-bottom-right-radius: 5px;
      box-shadow: 0px 0px 67px -15px rgba(0,0,0,0.75);
      font-weight: 300;
      position: absolute;
      top: 65px;
      width: 240px;
    }
    ^inner div {
      box-sizing: border-box;
      padding: 8px 24px;
      padding-right: 48px;
      cursor: pointer;
      background: white;
      color: black;
      border-left: solid 1px /*%GREY5%*/ #f5f7fa;
      border-right: solid 1px /*%GREY5%*/ #f5f7fa;
    }
    ^inner div:last-child {
      border-bottom-left-radius: 5px;
      border-bottom-right-radius: 5px;
    }
    ^inner div:hover {
      background: /*%PRIMARY5%*/ #e5f1fc !important;
      border-left: solid 1px /*%PRIMARY5%*/ #e5f1fc;
      border-right: solid 1px /*%PRIMARY5%*/ #e5f1fc;
    }
  `,

  methods: [
    function initE() {
      this.addClass(this.myClass());
      var self = this;
      var menu = this.menu;
      var X    = this.__subContext__;

      menu.children.orderBy(this.Menu.ORDER, this.Menu.LABEL).select({
        put: function(menu) {
          if ( ! menu.handler ) return;
          self.start('div').call(function() {
            var e = this;
            this
              .on('click', function() {
                // TODO: if a submenu, don't close until child closed
                self.close();
                menu.launch_(X, e);
              })
              .translate(menu.id+'.label',menu.label);
            })
          .end();
        },
        eof: function() {}
      });
    }
  ]
});
<|endoftext|>