<commit_before>/**
 * grunt-properties-to-json
 *
 * @version 0.4.0
 * @copyright 2014-2015 Jacob van Mourik
 * @license MIT
 */

'use strict';

module.exports = function(grunt) {
    var parser = require('properties-parser'),
        _ = require('lodash');

    var splitKeysBy = function(obj, splitBy) {
        var keys, parent, result = {};
        _.forEach(obj, function(val, key) {
            keys = key.split(splitBy);
            parent = result;
            keys.forEach(function(k, i) {
                 if (i === keys.length-1) {
                     parent[k] = val;
                 } else {
                     parent = parent[k] = parent[k] || {};
                 }
            });
        });
        return result;
    };

    var filter = function(obj, regexps, include, deep) {
        return _.transform(obj, function(result, val, key) {
            var hasMatch = hasRegMatch(regexps, key);
            if ((hasMatch && include) || (!hasMatch && !include)) {
                result[key] = deep && _.isPlainObject(val) ? filter(val,regexps,include,deep) : val;
            }
        },{});
    };

    var toRegExps = function(value) {
        value = [].concat(value);
        return value.map(function(val) {
            return _.isRegExp(val) ? val : new RegExp(val);
        });
    };

    var hasRegMatch = function(regexps, value) {
        return regexps.some(function(regexp) {
            return regexp.test(value);
        });
    };

    var writeFile = function(data, dest) {
        grunt.file.write(dest, JSON.stringify(data), { encoding: 'utf8' });
        grunt.log.writeln('File "' + dest + '" created.');
    };

    var propertiesToJSON = function() {
        var dest, data, dataList, options = this.options();

        this.files.forEach(function(f) {
            if (options.merge) {
                if (!f.dest) {
                    return grunt.log.warn('You are trying to merge but no destination file is defined.');
                } else if (grunt.file.isDir(f.dest)) {
                    return grunt.log.warn('Destination "' + f.dest + '" should be a file.');
                }
            } else if (grunt.file.isFile(f.dest)) {
                return grunt.log.warn('Destination "' + f.dest + '" should be a directory.');
            }

            dataList = [];

            f.src.forEach(function(src) {
                if (src.substr(-11) !== '.properties') {
                    return;
                }
                if (!grunt.file.exists(src)) {
                    return grunt.log.warn('Source file "' + src + '" not found.');
                }
                data = parser.read(src);
                if (options.splitKeysBy) {
                    data = splitKeysBy(data, options.splitKeysBy);
                }
                if (options.exclude) {
                    data = filter(data, toRegExps(options.exclude), false, !!options.deepExclude);
                }
                if (options.include) {
                    data = filter(data, toRegExps(options.include), true, !!options.deepInclude);
                }
                if (options.merge) {
                    dataList.push(data);
                } else {
                    dest = f.dest ? f.dest + (_.endsWith(f.dest,'/') ? '' : '/') + src.replace(/.*\//,'') : src;
                    dest = dest.replace('.properties','.json');
                    writeFile(data, dest);
                }
            });

            if (options.merge) {
                writeFile(_.merge.apply(null,dataList), f.dest);
            }
        });
    };

    grunt.registerMultiTask('propertiesToJSON', 'Converts java property files to JSON files.', propertiesToJSON);

    // Deprecate old task name
    grunt.registerMultiTask('properties_to_json', 'Converts java property files to JSON files.', function() {
        grunt.log.warn('The "properties_to_json" task name is deprecated. Please use "propertiesToJSON" instead.');
        propertiesToJSON.apply(this, arguments);
    });
};
<commit_msg>Improved grunt logs<commit_after>/**
 * grunt-properties-to-json
 *
 * @version 0.4.0
 * @copyright 2014-2015 Jacob van Mourik
 * @license MIT
 */

'use strict';

module.exports = function(grunt) {
    var parser = require('properties-parser'),
        _ = require('lodash');

    var splitKeysBy = function(obj, splitBy) {
        var keys, parent, result = {};
        _.forEach(obj, function(val, key) {
            keys = key.split(splitBy);
            parent = result;
            keys.forEach(function(k, i) {
                 if (i === keys.length-1) {
                     parent[k] = val;
                 } else {
                     parent = parent[k] = parent[k] || {};
                 }
            });
        });
        return result;
    };

    var filter = function(obj, regexps, include, deep) {
        return _.transform(obj, function(result, val, key) {
            var hasMatch = hasRegMatch(regexps, key);
            if ((hasMatch && include) || (!hasMatch && !include)) {
                result[key] = deep && _.isPlainObject(val) ? filter(val,regexps,include,deep) : val;
            }
        },{});
    };

    var toRegExps = function(value) {
        value = [].concat(value);
        return value.map(function(val) {
            return _.isRegExp(val) ? val : new RegExp(val);
        });
    };

    var hasRegMatch = function(regexps, value) {
        return regexps.some(function(regexp) {
            return regexp.test(value);
        });
    };

    var writeFile = function(data, dest) {
        grunt.file.write(dest, JSON.stringify(data), { encoding: 'utf8' });
        grunt.log.writeln('File "' + dest + '" created.');
    };

    var propertiesToJSON = function() {
        var dest, data, dataList, options = this.options();

        this.files.forEach(function(f) {
            if (options.merge) {
                if (!f.dest) {
                    return grunt.log.warn('Task skipped, no destination file is defined.');
                } else if (grunt.file.isDir(f.dest)) {
                    return grunt.log.warn('Task skipped, destination "' + f.dest + '" should be a file but is a directory.');
                }
            } else if (grunt.file.isFile(f.dest)) {
                return grunt.log.warn('Task skipped, destination "' + f.dest + '" should be a directory but is a file.');
            }

            dataList = [];

            f.src.forEach(function(src) {
                if (src.substr(-11) !== '.properties') {
                    return;
                }
                if (!grunt.file.exists(src)) {
                    return grunt.log.warn('Source file "' + src + '" not found.');
                }
                data = parser.read(src);
                if (options.splitKeysBy) {
                    data = splitKeysBy(data, options.splitKeysBy);
                }
                if (options.exclude) {
                    data = filter(data, toRegExps(options.exclude), false, !!options.deepExclude);
                }
                if (options.include) {
                    data = filter(data, toRegExps(options.include), true, !!options.deepInclude);
                }
                if (options.merge) {
                    dataList.push(data);
                } else {
                    dest = f.dest ? f.dest + (_.endsWith(f.dest,'/') ? '' : '/') + src.replace(/.*\//,'') : src;
                    dest = dest.replace('.properties','.json');
                    writeFile(data, dest);
                }
            });

            if (options.merge) {
                writeFile(_.merge.apply(null,dataList), f.dest);
            }
        });
    };

    grunt.registerMultiTask('propertiesToJSON', 'Converts java property files to JSON files.', propertiesToJSON);

    // Deprecate old task name
    grunt.registerMultiTask('properties_to_json', 'Converts java property files to JSON files.', function() {
        grunt.log.warn('The "properties_to_json" task name is deprecated. Please use "propertiesToJSON" instead.');
        propertiesToJSON.apply(this, arguments);
    });
};
<|endoftext|>


<commit_before>var compile_templates_js = '.tmp_templates/*.js';

var toArray = function (data) {
    if (typeof data === 'undefined') {
        return [];
    }
    if (data instanceof Array) {
        return data;
    }
    return [data];
};

var runCleanup = function (grunt, wrap, filename, data, len) {
    var files = {};
    files[compile_templates_js] = [];

    for (var i = 0; i < len; i++) {
        var stringReplace = {
            files: {
                '.tmp_templates/': '.tmp_templates/templates_' + i + '.js'
            },
            options: {
                replacements: [
                    {
                        pattern: /angular\.module\(.*?\{/i,
                        replacement: "internal('templates_" + i + "', ['" + (data.name || wrap) + "'], function(app) {"
                    },
                    {
                        pattern: /'use strict';/i,
                        replacement: ''
                    },
                    {
                        pattern: /\$templateCache\.put/gi,
                        replacement: 'app.template'
                    },
                    {
                        pattern: /templates\//gi,
                        replacement: ''
                    },
                    {
                        pattern: /scripts\/directives\//gi,
                        replacement: ''
                    },
                    {
                        pattern: /\.html/gi,
                        replacement: ''
                    },
                    {
                        pattern: /\}\]\);/gim,
                        replacement: '});'
                    }
                ]
            }
        };

        var config = grunt.config.get('string-replace') || {};
        config['hb_tpl_' + i] = stringReplace;
        grunt.config.set('string-replace', config);
        grunt.task.run('string-replace:hb_tpl_' + i);
    }
};

exports.run = function (grunt, wrap, filename, data) {
    if (data.templates && data.templates.length) {

        var templateOpts = {};
        var templates = toArray(data.templates);
        var len = templates.length;
        var i;
        for (i = 0; i < len; i++) {
            //var opts = { cwd: 'src/go', src: '**/**.html' },
            var opts = templates[i];

            templateOpts['template' + i] = {
                src: [opts.src],
                cwd: opts.cwd,
                dest: '.tmp_templates/templates_' + i + '.js',
                options: {
                    module: data.name || wrap,
                    htmlmin: {
                        collapseBooleanAttributes: true,
                        collapseWhitespace: true,
                        removeAttributeQuotes: true,
                        removeComments: true, // Only if you don't use comment directives!
                        removeEmptyAttributes: true,
                        removeRedundantAttributes: true,
                        removeScriptTypeAttributes: true,
                        removeStyleLinkTypeAttributes: true
                    }
                }
            }
        }

        grunt.config.set('ngtemplates', templateOpts);
        grunt.task.run('ngtemplates');

        runCleanup(grunt, wrap, filename, data, len);
    }
};<commit_msg>no longer supporting tplUrl names without extension<commit_after>var compile_templates_js = '.tmp_templates/*.js';

var toArray = function (data) {
    if (typeof data === 'undefined') {
        return [];
    }
    if (data instanceof Array) {
        return data;
    }
    return [data];
};

var runCleanup = function (grunt, wrap, filename, data, len) {
    var files = {};
    files[compile_templates_js] = [];

    for (var i = 0; i < len; i++) {
        var stringReplace = {
            files: {
                '.tmp_templates/': '.tmp_templates/templates_' + i + '.js'
            },
            options: {
                replacements: [
                    {
                        pattern: /angular\.module\(.*?\{/i,
                        replacement: "internal('templates_" + i + "', ['" + (data.name || wrap) + "'], function(app) {"
                    },
                    {
                        pattern: /'use strict';/i,
                        replacement: ''
                    },
                    {
                        pattern: /\$templateCache\.put/gi,
                        replacement: 'app.template'
                    },
                    {
                        pattern: /templates\//gi,
                        replacement: ''
                    },
                    {
                        pattern: /scripts\/directives\//gi,
                        replacement: ''
                    },
                    {
                        pattern: /\}\]\);/gim,
                        replacement: '});'
                    }
                ]
            }
        };

        var config = grunt.config.get('string-replace') || {};
        config['hb_tpl_' + i] = stringReplace;
        grunt.config.set('string-replace', config);
        grunt.task.run('string-replace:hb_tpl_' + i);
    }
};

exports.run = function (grunt, wrap, filename, data) {
    if (data.templates && data.templates.length) {

        var templateOpts = {};
        var templates = toArray(data.templates);
        var len = templates.length;
        var i;
        for (i = 0; i < len; i++) {
            //var opts = { cwd: 'src/go', src: '**/**.html' },
            var opts = templates[i];

            templateOpts['template' + i] = {
                src: [opts.src],
                cwd: opts.cwd,
                dest: '.tmp_templates/templates_' + i + '.js',
                options: {
                    module: data.name || wrap,
                    htmlmin: {
                        collapseBooleanAttributes: true,
                        collapseWhitespace: true,
                        removeAttributeQuotes: true,
                        removeComments: true, // Only if you don't use comment directives!
                        removeEmptyAttributes: true,
                        removeRedundantAttributes: true,
                        removeScriptTypeAttributes: true,
                        removeStyleLinkTypeAttributes: true
                    }
                }
            }
        }

        grunt.config.set('ngtemplates', templateOpts);
        grunt.task.run('ngtemplates');

        runCleanup(grunt, wrap, filename, data, len);
    }
};<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React from 'react'

import StyledPanelBody from './StyledPanelBody'

/**
 * Only <Panel.Body/> is affected by the visibility state.
 */
class PanelBody extends React.PureComponent {
  state = {}

  setHeight = () => {
    this.outerEl.setAttribute('style', 'height: auto; animation: none;')
    this.setState({
      heightIfOpen: `${this.outerEl.offsetHeight}px`
    })
    this.outerEl.removeAttribute('style')
  }

  connectObserver() {
    this.observer = new MutationObserver(() => { this.setHeight() })
    this.observer.observe(this.innerEl, {
      attributes: true,
      childList: true,
      subtree: true,
      characterData: true
    })
  }

  disconnectObserver() {
    if (this.observer) {
      this.observer.disconnect()
    }
  }

  componentDidMount() {
    this.setHeight()
    window.addEventListener('resize', this.setHeight)
    this.connectObserver()
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.setHeight)
    this.disconnectObserver()
  }

  render() {
    const {
      children,
      isFramed,
      isOpen
    } = this.props

    return (

    )
  }
}

PanelBody.propTypes = {
  children: PropTypes.node,
  /**
   * Boolean to control if <Panel.Header/>, <Panel.Body/> and <Panel.Footer/> is initially opened.
   * Value is always overridden by parent element.
   */
  isFramed: PropTypes.bool,
  /**
   * Boolean to control if <Panel.Body/> is initially opened. Value is always overridden by parent element.
   */
  isOpen: PropTypes.bool
}

export default PanelBody
<commit_msg>refactor(tocco-ui): Panel observer only triggers on certain attributes<commit_after>import PropTypes from 'prop-types'
import React from 'react'

import StyledPanelBody from './StyledPanelBody'

/**
 * Only <Panel.Body/> is affected by the visibility state.
 */
class PanelBody extends React.PureComponent {
  state = {}

  setHeight = () => {
    this.outerEl.setAttribute('style', 'height: auto; animation: none;')
    this.setState({
      heightIfOpen: `${this.outerEl.offsetHeight}px`
    })
    this.outerEl.removeAttribute('style')
  }

  relevantAttributes = ['data-image-in-cache']
  observerCallback = mutationsList => {
    const isNotIgnorableAttributeChange = mutation =>
      mutation.type !== 'attributes' || this.relevantAttributes.includes(mutation.attributeName)

    if (mutationsList.some(isNotIgnorableAttributeChange)) {
      this.setHeight()
    }
  }

  connectObserver() {
    this.observer = new MutationObserver(this.observerCallback)
    this.observer.observe(this.innerEl, {
      attributes: true,
      childList: true,
      subtree: true,
      characterData: true
    })
  }

  disconnectObserver() {
    if (this.observer) {
      this.observer.disconnect()
    }
  }

  componentDidMount() {
    this.setHeight()
    window.addEventListener('resize', this.setHeight)
    this.connectObserver()
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.setHeight)
    this.disconnectObserver()
  }

  render() {
    const {
      children,
      isFramed,
      isOpen
    } = this.props

    return (

    )
  }
}

PanelBody.propTypes = {
  children: PropTypes.node,
  /**
   * Boolean to control if <Panel.Header/>, <Panel.Body/> and <Panel.Footer/> is initially opened.
   * Value is always overridden by parent element.
   */
  isFramed: PropTypes.bool,
  /**
   * Boolean to control if <Panel.Body/> is initially opened. Value is always overridden by parent element.
   */
  isOpen: PropTypes.bool
}

export default PanelBody
<|endoftext|>


<commit_before>import { events } from './utils';
import { convert } from 'feathers-errors';

const debug = require('debug')('feathers-socket-commons:client');
const namespacedEmitterMethods = [
  'addListener',
  'emit',
  'listenerCount',
  'listeners',
  'off',
  'on',
  'once',
  'prependListener',
  'prependOnceListener',
  'removeAllListeners',
  'removeListener'
];
const otherEmitterMethods = [
  'eventNames',
  'getMaxListeners',
  'setMaxListeners'
];

const addEmitterMethods = service => {
  otherEmitterMethods.forEach(method => {
    service[method] = function(...args) {
      if(typeof this.connection[method] !== 'function') {
        throw new Error(`Can not call '${method}' on the client service connection.`);
      }

      return this.connection[method](...args);
    };
  });

  namespacedEmitterMethods.forEach(method => {
    // Only set off if it's a method on connection
    if (method === 'off' && typeof service.connection[method] !== 'function') {
      return;
    }

    service[method] = function(name, ...args) {
      if(typeof this.connection[method] !== 'function') {
        throw new Error(`Can not call '${method}' on the client service connection.`);
      }

      const eventName = `${this.path} ${name}`;

      debug(`Calling emitter method ${method} with ` +
        `namespaced event '${eventName}'`);

      const result = this.connection[method](eventName, ...args);

      return result === this.connection ? this : result;
    };
  });
};

export default class Service {
  constructor(options) {
    this.events = events;
    this.path = options.name;
    this.connection = options.connection;
    this.method = options.method;
    this.timeout = options.timeout || 5000;

    addEmitterMethods(this);
  }

  send(method, ...args) {
    let callback = null;
    if (typeof args[args.length - 1] === 'function') {
      callback = args.pop();
    }

    return new Promise((resolve, reject) => {
      const event = `${this.path}::${method}`;
      const timeoutId = setTimeout(() => reject(
        new Error(`Timeout of ${this.timeout}ms exceeded calling ${event}`)
      ), this.timeout);

      args.unshift(event);
      args.push(function(error, data) {
        error = convert(error);
        clearTimeout(timeoutId);

        if (callback) {
          callback(error, data);
        }

        return error ? reject(error) : resolve(data);
      });

      debug(`Sending socket.${this.method}`, args);

      this.connection[this.method](... args);
    });
  }

  find(params = {}) {
    return this.send('find', params.query || {});
  }

  get(id, params = {}) {
    return this.send('get', id, params.query || {});
  }

  create(data, params = {}) {
    return this.send('create', data, params.query || {});
  }

  update(id, data, params = {}) {
    return this.send('update', id, data, params.query || {});
  }

  patch(id, data, params = {}) {
    return this.send('patch', id, data, params.query || {});
  }

  remove(id, params = {}) {
    return this.send('remove', id, params.query || {});
  }

  off(... args) {
    // Note: if .off() is defined on connection, this method is replaced with namespaced method
    if(args.length === 1) {
      return this.removeAllListeners(... args);
    }

    return this.removeListener(... args);
  }
}
<commit_msg>Small refactoring finalizing #26<commit_after>import { events } from './utils';
import { convert } from 'feathers-errors';

const debug = require('debug')('feathers-socket-commons:client');
const namespacedEmitterMethods = [
  'addListener',
  'emit',
  'listenerCount',
  'listeners',
  'on',
  'once',
  'prependListener',
  'prependOnceListener',
  'removeAllListeners',
  'removeListener'
];
const otherEmitterMethods = [
  'eventNames',
  'getMaxListeners',
  'setMaxListeners'
];

const addEmitterMethods = service => {
  otherEmitterMethods.forEach(method => {
    service[method] = function(...args) {
      if(typeof this.connection[method] !== 'function') {
        throw new Error(`Can not call '${method}' on the client service connection.`);
      }

      return this.connection[method](...args);
    };
  });

  namespacedEmitterMethods.forEach(method => {
    service[method] = function(name, ...args) {
      if(typeof this.connection[method] !== 'function') {
        throw new Error(`Can not call '${method}' on the client service connection.`);
      }

      const eventName = `${this.path} ${name}`;

      debug(`Calling emitter method ${method} with ` +
        `namespaced event '${eventName}'`);

      const result = this.connection[method](eventName, ...args);

      return result === this.connection ? this : result;
    };
  });
};

export default class Service {
  constructor(options) {
    this.events = events;
    this.path = options.name;
    this.connection = options.connection;
    this.method = options.method;
    this.timeout = options.timeout || 5000;

    addEmitterMethods(this);
  }

  send(method, ...args) {
    let callback = null;
    if (typeof args[args.length - 1] === 'function') {
      callback = args.pop();
    }

    return new Promise((resolve, reject) => {
      const event = `${this.path}::${method}`;
      const timeoutId = setTimeout(() => reject(
        new Error(`Timeout of ${this.timeout}ms exceeded calling ${event}`)
      ), this.timeout);

      args.unshift(event);
      args.push(function(error, data) {
        error = convert(error);
        clearTimeout(timeoutId);

        if (callback) {
          callback(error, data);
        }

        return error ? reject(error) : resolve(data);
      });

      debug(`Sending socket.${this.method}`, args);

      this.connection[this.method](... args);
    });
  }

  find(params = {}) {
    return this.send('find', params.query || {});
  }

  get(id, params = {}) {
    return this.send('get', id, params.query || {});
  }

  create(data, params = {}) {
    return this.send('create', data, params.query || {});
  }

  update(id, data, params = {}) {
    return this.send('update', id, data, params.query || {});
  }

  patch(id, data, params = {}) {
    return this.send('patch', id, data, params.query || {});
  }

  remove(id, params = {}) {
    return this.send('remove', id, params.query || {});
  }

  off(name, ... args) {
    if(typeof this.connection.off === 'function') {
      return this.connection.off(`${this.path} ${name}`, ... args);
    } else if(args.length === 0) {
      return this.removeAllListeners(name);
    }

    return this.removeListener(name, ... args);
  }
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
/*global console*/
/*eslint no-console:0 max-statements: [1, 30]*/
/*jscs:disable disallowKeywords*/

'use strict';

var util = require('util');
var ansi = require('chalk');
var stream = require('stream');
var hexer = require('hexer');
var sprintf = require('sprintf-js').sprintf;
var thriftDecoder = require('./thrift/simple_decoder');
var v2 = require('tchannel/v2');
var bufrw = require('bufrw');
var ChunkReader = require('bufrw/stream/chunk_reader');

module.exports = TChannelSessionTracker;

function TChannelSessionTracker(opts) {
    var self = this;
    self.packetNumber = 0;
    self.sessionNumber = opts.sessionNumber;
    self.direction = opts.direction;
    self.tcpSession = opts.tcpSession;
    self.alwaysShowFrameDump = opts.alwaysShowFrameDump;
    self.alwaysShowHex = opts.alwaysShowHex;
    self.hexerOptions = opts.hexer || {
        prefix: '  ',
        gutter: 4, // maximum frame length is 64k so FFFF
        renderHuman: renderByte,
        nullHuman: ansi.black(ansi.bold('empty'))
    };
    self.parser = null;
    self.speculative = false;
    if (opts.onTrack) {
        self.startTracking(false);
    }
}

TChannelSessionTracker.prototype.startTracking =
function startTracking(speculative) {
    var self = this;
    self.buffer = new stream.PassThrough();

    self.reader = new ChunkReader(bufrw.UInt16BE, v2.Frame.RW);

    self.buffer.pipe(self.reader);

    self.tracking = true;
    self.speculative = speculative;

    self.reader.on('data', handleFrame);
    function handleFrame(frame) {
        self.handleFrame(frame);
    }

    self.reader.on('error', handleError);
    function handleError(error) {
        self.handleError(error);
    }
};

TChannelSessionTracker.prototype.stopTracking =
function stopTracking() {
    var self = this;
    if (!self.tracking) {
        return;
    }
    self.buffer.end();
    self.tracking = false;
    self.parser = null;
    self.buffer = null;
};

TChannelSessionTracker.prototype.end =
function end() {
    var self = this;
    self.stopTracking();
};

TChannelSessionTracker.prototype.handlePacket =
function handlePacket(packet) {
    var self = this;
    if (self.alwaysShowHex) {
        console.log(ansi.cyan(sprintf(
            'session=%d %s %s %s packet=%s',
            self.sessionNumber,
            self.tcpSession.src,
            (self.direction === 'outgoing' ? '-->' : '<--'),
            self.tcpSession.dst,
            self.packetNumber++
        )));
        console.log(hexer(packet, self.hexerOptions));
        console.log('');
    }
    if (!self.tracking) {
        self.handleUntrackedPacket(packet);
    }
    if (self.buffer) {
        self.buffer.write(packet);
    }
};

TChannelSessionTracker.prototype.handleUntrackedPacket =
function handleUntrackedPacket(packet) {
    var self = this;
    self.startTracking(true);
};

TChannelSessionTracker.prototype.handleFrame =
function handleFrame(frame) {
    var self = this;
    var type =
        frame &&
        frame.body &&
        frame.body.type;
    console.log(ansi.green(sprintf(
        'session=%d %s %s %s frame=%d type=0x%02x%s',
        self.sessionNumber,
        self.tcpSession.src,
        (self.direction === 'outgoing' ? '-->' : '<--'),
        self.tcpSession.dst,
        frame && frame.id,
        type,
        (self.speculative ? ansi.yellow(' ???') : '')
    )));
    var showJson = self.alwaysShowFrameDump;
    self.inspectCommonFrame(frame);
    if (showJson) {
        console.log(ansi.yellow('frame'));
        console.log(util.inspect(frame, {colors: ansi.enabled}));
    }
    console.log('');
};

TChannelSessionTracker.prototype.handleError =
function handleError(error) {
    var self = this;
    console.log(ansi.red(sprintf(
        'session=%d %s %s %s frame parse error',
        self.sessionNumber,
        self.tcpSession.src,
        (self.direction === 'outgoing' ? '-->' : '<--'),
        self.tcpSession.dst
    )));
    console.log(bufrw.formatError(error, {
        color: true,
        markColor: function markColor(str) {
            return ansi.red.bold(str);
        },
        hexerOptions: self.hexerOptions
    }));

    self.stopTracking();
};

TChannelSessionTracker.prototype.nameByType = {
    '01': 'init request',
    '02': 'init response',
    '03': 'call request',
    '04': 'call response',
    '13': 'request continue',
    '14': 'response continue',
    'c0': 'cancel',
    'c1': 'claim',
    'd0': 'ping request',
    'd1': 'ping response',
    'ff': 'error'
};

TChannelSessionTracker.prototype.inspectCommonFrame =
function inspectCommonFrame(frame) {
    var self = this;
    if (!frame.body) {
        console.log(ansi.yellow(sprintf(
            'unexpected shape'
        )));
        // TODO unexpected frame shape
        return;
    }
    self.inspectBanner(frame.body, frame);
    self.inspectHeaders(frame.body.headers);
    self.inspectTracing(frame.body.tracing);
    self.inspectBody(frame.body);
};

TChannelSessionTracker.prototype.inspectTracing =
function inspectTracing(tracing) {
    if (tracing) {
        console.log(
            'tracing: spanid: ' + tracing.spanid.toString('hex') + ' ' +
            'parentid: ' + tracing.parentid.toString('hex') + ' ' +
            'traceid: ' + tracing.traceid.toString('hex') + ' ' +
            'traceflags: ' + tracing.flags.toString(16)
        );
    }
};

TChannelSessionTracker.prototype.inspectBanner =
function inspectBanner(body, frame) {
    var self = this;
    if (!body) {
        // TODO
        return;
    }
    var parts = [];

    // type, e.g., CALL REQUEST
    self.addFrameTypeName(parts, body);

    if (typeof frame.id === 'number') {
        parts.push(sprintf('id=0x%04x (%d)', frame.id, frame.id));
    }

    // e.g., version=2
    if (body.version) {
        parts.push(sprintf('version=%d', body.version));
    }

    self.addServiceName(parts, body);

    // e.g., flags=0x01 continued
    if (body.flags !== undefined) {
        parts.push(sprintf('flags=0x%02x', body.flags));
    }
    if (body.flags) {
        if (body.flags & 0x01) {
            parts.push('continued');
        }
    }

    // e.g., ttl=60
    if (body.ttl) {
        parts.push(sprintf('ttl=0x%04x (%d)', body.ttl, body.ttl));
    }

    if (body.csum) {
        self.addCsum(parts, body.csum);
    }

    console.log(parts.join(' '));
};

TChannelSessionTracker.prototype.addFrameTypeName =
function addFrameTypeName(parts, body) {
    var self = this;
    var type = sprintf('%02x', body.type);
    if (self.nameByType[type]) {
        parts.push(self.nameByType[type].toUpperCase());
    } else {
        parts.push(ansi.red(sprintf(
            'UNRECOGNIZED FRAME TYPE %d',
            body.type
        )));
    }
};

TChannelSessionTracker.prototype.addServiceName =
function addServiceName(parts, body) {
    // e.g., service="teapot"
    if (body.service !== undefined) {
        var service = JSON.stringify(body.service.toString('utf8'));
        if (!body.service.length) {
            service = ansi.red(service);
        }
        parts.push(sprintf('service=%s', service));
    }
};

TChannelSessionTracker.prototype.addCsum =
function addCsum(parts, csum) {
    if (csum.type === 0x00) {
        parts.push(sprintf('csum=none'));
    } else if (csum.type === 0x01) {
        parts.push(sprintf('csum=%04x (crc32 unverified)', csum.val));
    } else if (csum.type === 0x02) {
        parts.push(sprintf(
            'csum=%04x (farmhash Fingerprint32 unverified)',
            csum.val
        ));
    }
};

TChannelSessionTracker.prototype.inspectHeaders =
function inspectHeaders(headers) {
    if (!headers) {
        return;
    }
    var keys = Object.keys(headers);
    if (keys.length) {
        console.log(ansi.yellow('headers'));
        keys.forEach(function eachKey(key) {
            console.log('  %s: %s', ansi.yellow(key), headers[key]);
        });
    }
};

TChannelSessionTracker.prototype.inspectBody =
function inspectBody(body) {
    var self = this;
    if (!body) {
        return;
    }
    if (body.args) {
        for (var i = 0; i < body.args.length; i++) {
            self.inspectArgument('args[' + i + ']', body.args[i]);
        }
    }
    // TODO: bring back/
    // if (!self.inspectThrift(body.arg3)) {
    //     self.inspectJSON(body.arg3);
    // }
    if (body.flags & 0x01) {
        console.log(ansi.yellow('to be continued...'));
    }
};

TChannelSessionTracker.prototype.inspectArgument =
function inspectArgument(name, argument) {
    var self = this;
    if (!argument) {
        return;
    }
    console.log(ansi.yellow(name));
    console.log(hexer(argument, self.hexerOptions));
};

TChannelSessionTracker.prototype.inspectThrift =
function inspectThrift(buf) {
    try {
        var data = thriftDecoder.decode(buf);
        console.log(ansi.yellow('arg3 as thrift'));
        console.log(util.inspect(data, {colors: true, depth: Infinity}));
        return true;
    } catch (e) {
    }
};

TChannelSessionTracker.prototype.inspectJSON =
function inspectJSON(buf) {
    try {
        var data = JSON.parse(buf.toString('utf8'));
        console.log(ansi.yellow('arg3 as json'));
        console.log(util.inspect(data, {colors: true, depth: Infinity}));
        return true;
    } catch (e) {
    }
};

function renderByte(c) {
    if (c > 0x1f && c < 0x7f) {
        return String.fromCharCode(c);
    } else {
        return ansi.bold(ansi.black('.'));
    }
}
<commit_msg>add inspectThrift() back<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
/*global console*/
/*eslint no-console:0 max-statements: [1, 30]*/
/*jscs:disable disallowKeywords*/

'use strict';

var util = require('util');
var ansi = require('chalk');
var stream = require('stream');
var hexer = require('hexer');
var sprintf = require('sprintf-js').sprintf;
var thriftDecoder = require('./thrift/simple_decoder');
var v2 = require('tchannel/v2');
var bufrw = require('bufrw');
var ChunkReader = require('bufrw/stream/chunk_reader');

module.exports = TChannelSessionTracker;

function TChannelSessionTracker(opts) {
    var self = this;
    self.packetNumber = 0;
    self.sessionNumber = opts.sessionNumber;
    self.direction = opts.direction;
    self.tcpSession = opts.tcpSession;
    self.alwaysShowFrameDump = opts.alwaysShowFrameDump;
    self.alwaysShowHex = opts.alwaysShowHex;
    self.hexerOptions = opts.hexer || {
        prefix: '  ',
        gutter: 4, // maximum frame length is 64k so FFFF
        renderHuman: renderByte,
        nullHuman: ansi.black(ansi.bold('empty'))
    };
    self.parser = null;
    self.speculative = false;
    if (opts.onTrack) {
        self.startTracking(false);
    }
}

TChannelSessionTracker.prototype.startTracking =
function startTracking(speculative) {
    var self = this;
    self.buffer = new stream.PassThrough();

    self.reader = new ChunkReader(bufrw.UInt16BE, v2.Frame.RW);

    self.buffer.pipe(self.reader);

    self.tracking = true;
    self.speculative = speculative;

    self.reader.on('data', handleFrame);
    function handleFrame(frame) {
        self.handleFrame(frame);
    }

    self.reader.on('error', handleError);
    function handleError(error) {
        self.handleError(error);
    }
};

TChannelSessionTracker.prototype.stopTracking =
function stopTracking() {
    var self = this;
    if (!self.tracking) {
        return;
    }
    self.buffer.end();
    self.tracking = false;
    self.parser = null;
    self.buffer = null;
};

TChannelSessionTracker.prototype.end =
function end() {
    var self = this;
    self.stopTracking();
};

TChannelSessionTracker.prototype.handlePacket =
function handlePacket(packet) {
    var self = this;
    if (self.alwaysShowHex) {
        console.log(ansi.cyan(sprintf(
            'session=%d %s %s %s packet=%s',
            self.sessionNumber,
            self.tcpSession.src,
            (self.direction === 'outgoing' ? '-->' : '<--'),
            self.tcpSession.dst,
            self.packetNumber++
        )));
        console.log(hexer(packet, self.hexerOptions));
        console.log('');
    }
    if (!self.tracking) {
        self.handleUntrackedPacket(packet);
    }
    if (self.buffer) {
        self.buffer.write(packet);
    }
};

TChannelSessionTracker.prototype.handleUntrackedPacket =
function handleUntrackedPacket(packet) {
    var self = this;
    self.startTracking(true);
};

TChannelSessionTracker.prototype.handleFrame =
function handleFrame(frame) {
    var self = this;
    var type =
        frame &&
        frame.body &&
        frame.body.type;
    console.log(ansi.green(sprintf(
        'session=%d %s %s %s frame=%d type=0x%02x%s',
        self.sessionNumber,
        self.tcpSession.src,
        (self.direction === 'outgoing' ? '-->' : '<--'),
        self.tcpSession.dst,
        frame && frame.id,
        type,
        (self.speculative ? ansi.yellow(' ???') : '')
    )));
    var showJson = self.alwaysShowFrameDump;
    self.inspectCommonFrame(frame);
    if (showJson) {
        console.log(ansi.yellow('frame'));
        console.log(util.inspect(frame, {colors: ansi.enabled}));
    }
    console.log('');
};

TChannelSessionTracker.prototype.handleError =
function handleError(error) {
    var self = this;
    console.log(ansi.red(sprintf(
        'session=%d %s %s %s frame parse error',
        self.sessionNumber,
        self.tcpSession.src,
        (self.direction === 'outgoing' ? '-->' : '<--'),
        self.tcpSession.dst
    )));
    console.log(bufrw.formatError(error, {
        color: true,
        markColor: function markColor(str) {
            return ansi.red.bold(str);
        },
        hexerOptions: self.hexerOptions
    }));

    self.stopTracking();
};

TChannelSessionTracker.prototype.nameByType = {
    '01': 'init request',
    '02': 'init response',
    '03': 'call request',
    '04': 'call response',
    '13': 'request continue',
    '14': 'response continue',
    'c0': 'cancel',
    'c1': 'claim',
    'd0': 'ping request',
    'd1': 'ping response',
    'ff': 'error'
};

TChannelSessionTracker.prototype.inspectCommonFrame =
function inspectCommonFrame(frame) {
    var self = this;
    if (!frame.body) {
        console.log(ansi.yellow(sprintf(
            'unexpected shape'
        )));
        // TODO unexpected frame shape
        return;
    }
    self.inspectBanner(frame.body, frame);
    self.inspectHeaders(frame.body.headers);
    self.inspectTracing(frame.body.tracing);
    self.inspectBody(frame.body);
};

TChannelSessionTracker.prototype.inspectTracing =
function inspectTracing(tracing) {
    if (tracing) {
        console.log(
            'tracing: spanid: ' + tracing.spanid.toString('hex') + ' ' +
            'parentid: ' + tracing.parentid.toString('hex') + ' ' +
            'traceid: ' + tracing.traceid.toString('hex') + ' ' +
            'traceflags: ' + tracing.flags.toString(16)
        );
    }
};

TChannelSessionTracker.prototype.inspectBanner =
function inspectBanner(body, frame) {
    var self = this;
    if (!body) {
        // TODO
        return;
    }
    var parts = [];

    // type, e.g., CALL REQUEST
    self.addFrameTypeName(parts, body);

    if (typeof frame.id === 'number') {
        parts.push(sprintf('id=0x%04x (%d)', frame.id, frame.id));
    }

    // e.g., version=2
    if (body.version) {
        parts.push(sprintf('version=%d', body.version));
    }

    self.addServiceName(parts, body);

    // e.g., flags=0x01 continued
    if (body.flags !== undefined) {
        parts.push(sprintf('flags=0x%02x', body.flags));
    }
    if (body.flags) {
        if (body.flags & 0x01) {
            parts.push('continued');
        }
    }

    // e.g., ttl=60
    if (body.ttl) {
        parts.push(sprintf('ttl=0x%04x (%d)', body.ttl, body.ttl));
    }

    if (body.csum) {
        self.addCsum(parts, body.csum);
    }

    console.log(parts.join(' '));
};

TChannelSessionTracker.prototype.addFrameTypeName =
function addFrameTypeName(parts, body) {
    var self = this;
    var type = sprintf('%02x', body.type);
    if (self.nameByType[type]) {
        parts.push(self.nameByType[type].toUpperCase());
    } else {
        parts.push(ansi.red(sprintf(
            'UNRECOGNIZED FRAME TYPE %d',
            body.type
        )));
    }
};

TChannelSessionTracker.prototype.addServiceName =
function addServiceName(parts, body) {
    // e.g., service="teapot"
    if (body.service !== undefined) {
        var service = JSON.stringify(body.service.toString('utf8'));
        if (!body.service.length) {
            service = ansi.red(service);
        }
        parts.push(sprintf('service=%s', service));
    }
};

TChannelSessionTracker.prototype.addCsum =
function addCsum(parts, csum) {
    if (csum.type === 0x00) {
        parts.push(sprintf('csum=none'));
    } else if (csum.type === 0x01) {
        parts.push(sprintf('csum=%04x (crc32 unverified)', csum.val));
    } else if (csum.type === 0x02) {
        parts.push(sprintf(
            'csum=%04x (farmhash Fingerprint32 unverified)',
            csum.val
        ));
    }
};

TChannelSessionTracker.prototype.inspectHeaders =
function inspectHeaders(headers) {
    if (!headers) {
        return;
    }
    var keys = Object.keys(headers);
    if (keys.length) {
        console.log(ansi.yellow('headers'));
        keys.forEach(function eachKey(key) {
            console.log('  %s: %s', ansi.yellow(key), headers[key]);
        });
    }
};

TChannelSessionTracker.prototype.inspectBody =
function inspectBody(body) {
    var self = this;
    if (!body) {
        return;
    }
    if (body.args) {
        for (var i = 0; i < body.args.length; i++) {
            self.inspectArgument('args[' + i + ']', body.args[i]);
        }
    }
    // TODO: bring back/
    if (body.args && body.args[2] && !self.inspectThrift(body.args[2])) {
        self.inspectJSON(body.args[2]);
    }
    if (body.flags & 0x01) {
        console.log(ansi.yellow('to be continued...'));
    }
};

TChannelSessionTracker.prototype.inspectArgument =
function inspectArgument(name, argument) {
    var self = this;
    if (!argument) {
        return;
    }
    console.log(ansi.yellow(name));
    console.log(hexer(argument, self.hexerOptions));
};

TChannelSessionTracker.prototype.inspectThrift =
function inspectThrift(buf) {
    try {
        var data = thriftDecoder.decode(buf);
        console.log(ansi.yellow('arg3 as thrift'));
        console.log(util.inspect(data, {colors: true, depth: Infinity}));
        return true;
    } catch (e) {
    }
};

TChannelSessionTracker.prototype.inspectJSON =
function inspectJSON(buf) {
    try {
        var data = JSON.parse(buf.toString('utf8'));
        console.log(ansi.yellow('arg3 as json'));
        console.log(util.inspect(data, {colors: true, depth: Infinity}));
        return true;
    } catch (e) {
    }
};

function renderByte(c) {
    if (c > 0x1f && c < 0x7f) {
        return String.fromCharCode(c);
    } else {
        return ansi.bold(ansi.black('.'));
    }
}
<|endoftext|>


<commit_before>ASTool.DocumentView = Em.Object.extend({

	dataSource: null,

	listener: null,

	restrictToDescendants: false,

	_spritesBinding: 'dataSource.sprites',

	canvas: null,

	ignoredElementTags: ['html', 'body'],

	mouseDown: 0,

	loader: null,

	sprites: function() {
		if (!this.get('dataSource')) {
			return [];
		} else {
			return this.get('_sprites') || [];
		}
	}.property('_sprites.@each'),

	_elementSelectionEnabled: null,

	getIframe: function() {
		return $('#scraped-doc-iframe').contents();
	},

	elementSelectionEnabled: function(key, selectionEnabled) {
		if (arguments.length > 1) {
			if (selectionEnabled) {
			    if (!this.get('_elementSelectionEnabled')) {
					this.set('_elementSelectionEnabled', true);
					this.showHoveredInfo();
					this.installEventHandlersForSelecting();
				}
			} else {
				this.set('_elementSelectionEnabled', false);
				this.uninstallEventHandlers();
				this.hideHoveredInfo();
			}
		} else {
			return this.get('_elementSelectionEnabled');
		}
	}.property('_elementSelectionEnabled'),

	partialSelectionEnabled: false,

	reset: function() {
		this.uninstallEventHandlers();
		this.set('elementSelectionEnabled', false);
		this.set('partialSelectionEnabled', false);
		this.set('dataSource', null);
		this.set('listener', null);
	},

	config: function(options) {
		this.set('dataSource', options.dataSource);
		this.set('listener', options.listener);
		if (options.mode == 'select') {
			this.set('elementSelectionEnabled', true);
			this.set('partialSelectionEnabled', options.partialSelects);
		} else if (options.mode == 'browse') {
			this.installEventHandlersForBrowsing();
		}
	},

	redrawNow: function() {
		var canvas = this.get('canvas');
		if (this.get('dataSource')) {
			canvas.draw();
		} else {
			canvas.clear();
		}
	}.observes('sprites', 'dataSource'),

	installEventHandlersForBrowsing: function() {
		this.uninstallEventHandlers();
		this.getIframe().bind('click', null, this.clickHandlerBrowse.bind(this));
	},

	installEventHandlersForSelecting: function() {
		this.uninstallEventHandlers();
		this.getIframe().bind('click', null, this.clickHandler.bind(this));
		this.getIframe().bind('mouseover', null, this.mouseOverHandler.bind(this));
		this.getIframe().bind('mouseout', null, this.mouseOutHandler.bind(this));
		this.getIframe().bind('mousedown', null, this.mouseDownHandler.bind(this));
		this.getIframe().bind('mouseup', null, this.mouseUpHandler.bind(this));
		this.getIframe().bind('hover', null, function(event) {event.preventDefault()});
		this.redrawNow();
	},

	uninstallEventHandlers: function() {
		this.getIframe().unbind('click');
		this.getIframe().unbind('mouseover');
		this.getIframe().unbind('mouseout');
		this.getIframe().unbind('mousedown');
		this.getIframe().unbind('mouseup');
		this.getIframe().unbind('hover');
		this.set('hoveredSprite', null);
	},

	showHoveredInfo: function() {
		$("#hovered-element-info").css('display', 'inline');
	},

	hideHoveredInfo: function() {
		$("#hovered-element-info").css('display', 'none');
	},

	updateHoveredInfo: function(element) {
		var path = $(element).getPath();
		var attributes = $(element).getAttributeList();
		var contents = '
' + path + '

';
		$(attributes).each(function(i, attribute) {
			var value = trim(attribute.get('value'), 60);
			contents += '
' + attribute.get('name') + ": " + value + '
';
		});
		$("#hovered-element-info").html(contents);
	},

	mouseOverHandler:  function(event) {
		event.preventDefault();
		var target = event.target;
		var tagName = $(target).prop("tagName").toLowerCase();
		if ($.inArray(tagName, this.get('ignoredElementTags')) == -1 &&
			this.mouseDown == 0) {
			if (!this.get('restrictToDescendants') ||
				$(target).isDescendant(this.get('restrictToDescendants'))) {
				if (!this.get('hoveredSprite')) {
					this.updateHoveredInfo(target);
					this.set('hoveredSprite',
							 ASTool.ElementSprite.create({'element': target}));
					this.get('canvas').draw();
				}
			}
		}
	},

	mouseOutHandler: function(event) {
		this.set('hoveredSprite', null);
		this.get('canvas').draw();
	},

	clickHandler: function(event) {
		event.preventDefault();
	},

	clickHandlerBrowse: function(event) {
		event.preventDefault();
		var linkingElement = $(event.target).closest('[href]');
		if (linkingElement.length) {
			var href = $(linkingElement).attr('href');
        	this.sendEvent('linkClicked', href);
		}
	},

	mouseDownHandler: function(event) {
		this.set('hoveredSprite', null);
		++this.mouseDown;
		this.get('canvas').draw();
	},

	mouseUpHandler: function(event) {
		--this.mouseDown;
		var selectedText = this.getIframeSelectedText();
		if (selectedText) {
			if (this.get('partialSelectionEnabled')) {
				if (selectedText.anchorNode == selectedText.focusNode) {
					this.sendEvent('partialSelection', selectedText);
				} else {
					alert('The selected text must belong to a single HTML element');
					selectedText.collapse();
				}
			} else {
				selectedText.collapse();
			}
		} else {
			var target = event.target;
			var tagName = $(target).prop("tagName").toLowerCase();
			if ($.inArray(tagName, this.get('ignoredElementTags')) == -1) {
				if (!this.get('restrictToDescendants') ||
					$(target).isDescendant(this.get('restrictToDescendants'))) {
					this.sendEvent('elementSelected', target);
				} else {
					this.sendEvent('elementSelected', null);
				}
			}
		}
	},

	sendEvent: function(name, target) {
		var actions = this.get('listener.documentActions');
		if (actions && actions[name]) {
			Em.run(function(){
				actions[name].call(this.get('listener'), target);
			}.bind(this));
		}
	},

	getIframeSelectedText: function() {
		var range = this.getIframe().get(0).getSelection();
		if (range && !range.isCollapsed) {
			return range;
		} else {
			return null;
		}
	},

	initCanvas: function() {
		var canvas = ASTool.Canvas.create({canvasId: 'infocanvas',
										   datasource: this})
		this.set('canvas', canvas);
		if (!Ember.testing){
			// Disable automatic redrawing during tests.
			var self = this;
			this.set('autoRedrawId', setInterval(function() {
				Ember.run(function(){
					self.redrawNow();
				});
			}, 1000));
			window.onresize = function() {
				$('#scraped-doc-iframe').height(window.innerHeight);
				$('#toolbar').height(window.innerHeight);
				this.get('canvas').draw();
				if (ASTool.graph) {
					// FIXME: move this to a good place.
					ASTool.graph.resize();
				}
			}.bind(this);
		}
	},

	displayAnnotatedDocument: function(annotatedDocument, readyCallback) {
		if (this.get('autoRedrawId')) {
			clearInterval(this.get('autoRedrawId'));
		}

		// FIXME!!
		if (!Ember.testing){
			document.getElementById('scraped-doc-iframe').srcdoc = annotatedDocument;
		} else {
			this.getIframe().find('html').html(annotatedDocument);
		}

		if (!this.getCanvas) {
			this.initCanvas();
		}

		// We need to disable all interactions with the document we are loading
		// until we trigger the callback.
		this.set('canvas.interactionsBlocked', true);
		Em.run.later(this, function() {
			var doc = document.getElementById('scraped-doc-iframe').contentWindow.document;
			doc.onscroll = this.get('canvas').draw.bind(this.get('canvas'));
			this.set('canvas.interactionsBlocked', false);
			if (readyCallback) {
				readyCallback(this.getIframe());
			};
		}, 1000);
	},

	showLoading: function() {
		this.set('canvas.interactionsBlocked', true);
		var loader = this.get('loader');
		if (!loader) {
			loader = new CanvasLoader('loader-container');
			loader.setColor('#2398c2');
			loader.setShape('spiral');
			loader.setDiameter(133);
			loader.setRange(0.9);
			loader.setSpeed(1);
			loader.setFPS(60);
			var loaderObj = document.getElementById("canvasLoader");
		  	loaderObj.style.position = "absolute";
		  	loaderObj.style["top"] = loader.getDiameter() * -0.5 + "px";
		  	loaderObj.style["left"] = loader.getDiameter() * -0.5 + "px";
		  	this.set('loader', loader);
		}
		loader.show();
	},

	hideLoading: function() {
		if (this.get('loader')) {
			this.get('loader').hide();
		}
	},

	showError: function(error) {
		this.getIframe().find('html').html(error);
	},

	showSpider: function() {
		if (!Ember.testing){
			ic.ajax('start.html').then(function(page) {
				this.getIframe().find('html').html(page);
			}.bind(this));
		}
	},

	getAnnotatedDocument: function() {
		return this.getIframe().find('html').get(0).outerHTML;
	},

	scrollToElement: function(element) {
		var rect = $(element).boundingBox();
		this.getIframe().scrollTop(rect.top - 100);
		this.getIframe().scrollLeft(rect.left - 100);
	},
});
<commit_msg>Removed unused parameter.<commit_after>ASTool.DocumentView = Em.Object.extend({

	dataSource: null,

	listener: null,

	restrictToDescendants: false,

	_spritesBinding: 'dataSource.sprites',

	canvas: null,

	ignoredElementTags: ['html', 'body'],

	mouseDown: 0,

	loader: null,

	sprites: function() {
		if (!this.get('dataSource')) {
			return [];
		} else {
			return this.get('_sprites') || [];
		}
	}.property('_sprites.@each'),

	_elementSelectionEnabled: null,

	getIframe: function() {
		return $('#scraped-doc-iframe').contents();
	},

	elementSelectionEnabled: function(key, selectionEnabled) {
		if (arguments.length > 1) {
			if (selectionEnabled) {
			    if (!this.get('_elementSelectionEnabled')) {
					this.set('_elementSelectionEnabled', true);
					this.showHoveredInfo();
					this.installEventHandlersForSelecting();
				}
			} else {
				this.set('_elementSelectionEnabled', false);
				this.uninstallEventHandlers();
				this.hideHoveredInfo();
			}
		} else {
			return this.get('_elementSelectionEnabled');
		}
	}.property('_elementSelectionEnabled'),

	partialSelectionEnabled: false,

	reset: function() {
		this.uninstallEventHandlers();
		this.set('elementSelectionEnabled', false);
		this.set('partialSelectionEnabled', false);
		this.set('dataSource', null);
		this.set('listener', null);
	},

	config: function(options) {
		this.set('dataSource', options.dataSource);
		this.set('listener', options.listener);
		if (options.mode == 'select') {
			this.set('elementSelectionEnabled', true);
			this.set('partialSelectionEnabled', options.partialSelects);
		} else if (options.mode == 'browse') {
			this.installEventHandlersForBrowsing();
		}
	},

	redrawNow: function() {
		var canvas = this.get('canvas');
		if (this.get('dataSource')) {
			canvas.draw();
		} else {
			canvas.clear();
		}
	}.observes('sprites', 'dataSource'),

	installEventHandlersForBrowsing: function() {
		this.uninstallEventHandlers();
		this.getIframe().bind('click', this.clickHandlerBrowse.bind(this));
	},

	installEventHandlersForSelecting: function() {
		this.uninstallEventHandlers();
		this.getIframe().bind('click', this.clickHandler.bind(this));
		this.getIframe().bind('mouseover', this.mouseOverHandler.bind(this));
		this.getIframe().bind('mouseout', this.mouseOutHandler.bind(this));
		this.getIframe().bind('mousedown', this.mouseDownHandler.bind(this));
		this.getIframe().bind('mouseup', this.mouseUpHandler.bind(this));
		this.getIframe().bind('hover', function(event) {event.preventDefault()});
		this.redrawNow();
	},

	uninstallEventHandlers: function() {
		this.getIframe().unbind('click');
		this.getIframe().unbind('mouseover');
		this.getIframe().unbind('mouseout');
		this.getIframe().unbind('mousedown');
		this.getIframe().unbind('mouseup');
		this.getIframe().unbind('hover');
		this.set('hoveredSprite', null);
	},

	showHoveredInfo: function() {
		$("#hovered-element-info").css('display', 'inline');
	},

	hideHoveredInfo: function() {
		$("#hovered-element-info").css('display', 'none');
	},

	updateHoveredInfo: function(element) {
		var path = $(element).getPath();
		var attributes = $(element).getAttributeList();
		var contents = '
' + path + '

';
		$(attributes).each(function(i, attribute) {
			var value = trim(attribute.get('value'), 60);
			contents += '
' + attribute.get('name') + ": " + value + '
';
		});
		$("#hovered-element-info").html(contents);
	},

	mouseOverHandler:  function(event) {
		event.preventDefault();
		var target = event.target;
		var tagName = $(target).prop("tagName").toLowerCase();
		if ($.inArray(tagName, this.get('ignoredElementTags')) == -1 &&
			this.mouseDown == 0) {
			if (!this.get('restrictToDescendants') ||
				$(target).isDescendant(this.get('restrictToDescendants'))) {
				if (!this.get('hoveredSprite')) {
					this.updateHoveredInfo(target);
					this.set('hoveredSprite',
							 ASTool.ElementSprite.create({'element': target}));
					this.get('canvas').draw();
				}
			}
		}
	},

	mouseOutHandler: function(event) {
		this.set('hoveredSprite', null);
		this.get('canvas').draw();
	},

	clickHandler: function(event) {
		event.preventDefault();
	},

	clickHandlerBrowse: function(event) {
		event.preventDefault();
		var linkingElement = $(event.target).closest('[href]');
		if (linkingElement.length) {
			var href = $(linkingElement).attr('href');
        	this.sendEvent('linkClicked', href);
		}
	},

	mouseDownHandler: function(event) {
		this.set('hoveredSprite', null);
		++this.mouseDown;
		this.get('canvas').draw();
	},

	mouseUpHandler: function(event) {
		--this.mouseDown;
		var selectedText = this.getIframeSelectedText();
		if (selectedText) {
			if (this.get('partialSelectionEnabled')) {
				if (selectedText.anchorNode == selectedText.focusNode) {
					this.sendEvent('partialSelection', selectedText);
				} else {
					alert('The selected text must belong to a single HTML element');
					selectedText.collapse();
				}
			} else {
				selectedText.collapse();
			}
		} else {
			var target = event.target;
			var tagName = $(target).prop("tagName").toLowerCase();
			if ($.inArray(tagName, this.get('ignoredElementTags')) == -1) {
				if (!this.get('restrictToDescendants') ||
					$(target).isDescendant(this.get('restrictToDescendants'))) {
					this.sendEvent('elementSelected', target);
				} else {
					this.sendEvent('elementSelected', null);
				}
			}
		}
	},

	sendEvent: function(name, target) {
		var actions = this.get('listener.documentActions');
		if (actions && actions[name]) {
			Em.run(function(){
				actions[name].call(this.get('listener'), target);
			}.bind(this));
		}
	},

	getIframeSelectedText: function() {
		var range = this.getIframe().get(0).getSelection();
		if (range && !range.isCollapsed) {
			return range;
		} else {
			return null;
		}
	},

	initCanvas: function() {
		var canvas = ASTool.Canvas.create({canvasId: 'infocanvas',
										   datasource: this})
		this.set('canvas', canvas);
		if (!Ember.testing){
			// Disable automatic redrawing during tests.
			var self = this;
			this.set('autoRedrawId', setInterval(function() {
				Ember.run(function(){
					self.redrawNow();
				});
			}, 1000));
			window.onresize = function() {
				$('#scraped-doc-iframe').height(window.innerHeight);
				$('#toolbar').height(window.innerHeight);
				this.get('canvas').draw();
				if (ASTool.graph) {
					// FIXME: move this to a good place.
					ASTool.graph.resize();
				}
			}.bind(this);
		}
	},

	displayAnnotatedDocument: function(annotatedDocument, readyCallback) {
		if (this.get('autoRedrawId')) {
			clearInterval(this.get('autoRedrawId'));
		}

		// FIXME!!
		if (!Ember.testing){
			document.getElementById('scraped-doc-iframe').srcdoc = annotatedDocument;
		} else {
			this.getIframe().find('html').html(annotatedDocument);
		}

		if (!this.getCanvas) {
			this.initCanvas();
		}

		// We need to disable all interactions with the document we are loading
		// until we trigger the callback.
		this.set('canvas.interactionsBlocked', true);
		Em.run.later(this, function() {
			var doc = document.getElementById('scraped-doc-iframe').contentWindow.document;
			doc.onscroll = this.get('canvas').draw.bind(this.get('canvas'));
			this.set('canvas.interactionsBlocked', false);
			if (readyCallback) {
				readyCallback(this.getIframe());
			};
		}, 1000);
	},

	showLoading: function() {
		this.set('canvas.interactionsBlocked', true);
		var loader = this.get('loader');
		if (!loader) {
			loader = new CanvasLoader('loader-container');
			loader.setColor('#2398c2');
			loader.setShape('spiral');
			loader.setDiameter(133);
			loader.setRange(0.9);
			loader.setSpeed(1);
			loader.setFPS(60);
			var loaderObj = document.getElementById("canvasLoader");
		  	loaderObj.style.position = "absolute";
		  	loaderObj.style["top"] = loader.getDiameter() * -0.5 + "px";
		  	loaderObj.style["left"] = loader.getDiameter() * -0.5 + "px";
		  	this.set('loader', loader);
		}
		loader.show();
	},

	hideLoading: function() {
		if (this.get('loader')) {
			this.get('loader').hide();
		}
	},

	showError: function(error) {
		this.getIframe().find('html').html(error);
	},

	showSpider: function() {
		if (!Ember.testing){
			ic.ajax('start.html').then(function(page) {
				this.getIframe().find('html').html(page);
			}.bind(this));
		}
	},

	getAnnotatedDocument: function() {
		return this.getIframe().find('html').get(0).outerHTML;
	},

	scrollToElement: function(element) {
		var rect = $(element).boundingBox();
		this.getIframe().scrollTop(rect.top - 100);
		this.getIframe().scrollLeft(rect.left - 100);
	},
});
<|endoftext|>


<commit_before>import forEach from 'lodash.foreach'
import {BaseError} from 'make-error'
import {EventEmitter} from 'events'

import isEmpty from './is-empty'

// ===================================================================

const {hasOwnProperty} = Object

// ===================================================================

export class BufferAlreadyFlushed extends BaseError {
  constructor () {
    super('buffer flush already requested')
  }
}

export class DuplicateIndex extends BaseError {
  constructor (name) {
    super('there is already an index with the name ' + name)
  }
}

export class DuplicateItem extends BaseError {
  constructor (key) {
    super('there is already a item with the key ' + key)
  }
}

export class IllegalTouch extends BaseError {
  constructor (value) {
    super('only an object value can be touched (found a ' + typeof value + ')')
  }
}

export class InvalidKey extends BaseError {
  constructor (key) {
    super('invalid key of type ' + typeof key)
  }
}

export class NoSuchIndex extends BaseError {
  constructor (name) {
    super('there is no index with the name ' + name)
  }
}

export class NoSuchItem extends BaseError {
  constructor (key) {
    super('there is no item with the key ' + key)
  }
}

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

export default class Collection extends EventEmitter {
  constructor () {
    super()

    this._buffer = Object.create(null)
    this._buffering = 0
    this._indexes = Object.create(null)
    this._indexedItems = Object.create(null)
    this._items = Object.create(null)
    this._size = 0
  }

  // Overridable method used to compute the key of an item when
  // unspecified.
  //
  // Default implementation returns the `id` property.
  getKey (value) {
    return value && value.id
  }

  // -----------------------------------------------------------------
  // Properties
  // -----------------------------------------------------------------

  get all () {
    return this._items
  }

  get indexes () {
    return this._indexedItems
  }

  get size () {
    return this._size
  }

  // -----------------------------------------------------------------
  // Manipulation
  // -----------------------------------------------------------------

  add (keyOrObjectWithId, valueIfKey = undefined) {
    const [key, value] = this._resolveItem(keyOrObjectWithId, valueIfKey)
    this._assertHasNot(key)

    this._items[key] = value
    this._size++
    this._touch('add', key)
  }

  clear () {
    forEach(this._items, (_, key) => {
      delete this._items[key]
      this._size--
      this._touch('remove', key)
    })
  }

  remove (keyOrObjectWithId) {
    const [key] = this._resolveItem(keyOrObjectWithId)
    this._assertHas(key)

    delete this._items[key]
    this._size--
    this._touch('remove', key)
  }

  set (keyOrObjectWithId, valueIfKey = undefined) {
    const [key, value] = this._resolveItem(keyOrObjectWithId, valueIfKey)

    const action = this.has(key) ? 'update' : 'add'
    this._items[key] = value
    if (action === 'add') {
      this._size++
    }
    this._touch(action, key)
  }

  touch (keyOrObjectWithId) {
    const [key] = this._resolveItem(keyOrObjectWithId)
    this._assertHas(key)
    const value = this.get(key)
    if (typeof value !== 'object' || value === null) {
      throw new IllegalTouch(value)
    }

    this._touch('update', key)

    return this.get(key)
  }

  update (keyOrObjectWithId, valueIfKey = undefined) {
    const [key, value] = this._resolveItem(keyOrObjectWithId, valueIfKey)
    this._assertHas(key)

    this._items[key] = value
    this._touch('update', key)
  }

  // -----------------------------------------------------------------
  // Query
  // -----------------------------------------------------------------

  get (key, defaultValue) {
    if (this.has(key)) {
      return this._items[key]
    }

    if (arguments.length > 1) {
      return defaultValue
    }

    // Throws a NoSuchItem.
    this._assertHas(key)
  }

  has (key) {
    return hasOwnProperty.call(this._items, key)
  }

  // -----------------------------------------------------------------
  // Indexes
  // -----------------------------------------------------------------

  createIndex (name, index) {
    const {_indexes: indexes} = this
    if (hasOwnProperty.call(indexes, name)) {
      throw new DuplicateIndex(name)
    }

    indexes[name] = index
    this._indexedItems[name] = index.items

    index._attachCollection(this)
  }

  deleteIndex (name) {
    const {_indexes: indexes} = this
    if (!hasOwnProperty.call(indexes, name)) {
      throw new NoSuchIndex(name)
    }

    const index = indexes[name]
    delete indexes[name]
    delete this._indexedItems[name]

    index._detachCollection(this)
  }

  // -----------------------------------------------------------------
  // Iteration
  // -----------------------------------------------------------------

  * [Symbol.iterator] () {
    const {_items: items} = this

    for (let key in items) {
      yield [key, items[key]]
    }
  }

  * keys () {
    const {_items: items} = this

    for (let key in items) {
      yield key
    }
  }

  * values () {
    const {_items: items} = this

    for (let key in items) {
      yield items[key]
    }
  }

  // -----------------------------------------------------------------
  // Events buffering
  // -----------------------------------------------------------------

  bufferEvents () {
    ++this._buffering

    let called = false
    return () => {
      if (called) {
        throw new BufferAlreadyFlushed()
      }
      called = true

      if (--this._buffering) {
        return
      }

      const data = {
        add: Object.create(null),
        remove: Object.create(null),
        update: Object.create(null)
      }

      for (let key in this._buffer) {
        data[this._buffer[key]][key] = this._items[key]
      }

      forEach(['add', 'update', 'remove'], action => {
        const items = data[action]

        if (!isEmpty(items)) {
          this.emit(action, items)
        }
      })

      this._buffer = Object.create(null)
    }
  }

  // =================================================================

  _assertHas (key) {
    if (!this.has(key)) {
      throw new NoSuchItem(key)
    }
  }

  _assertHasNot (key) {
    if (this.has(key)) {
      throw new DuplicateItem(key)
    }
  }

  _assertValidKey (key) {
    if (!this._isValidKey(key)) {
      throw new InvalidKey(key)
    }
  }

  _isValidKey (key) {
    return typeof key === 'number' || typeof key === 'string'
  }

  _resolveItem (keyOrObjectWithId, valueIfKey = undefined) {
    if (valueIfKey !== undefined) {
      this._assertValidKey(keyOrObjectWithId)

      return [keyOrObjectWithId, valueIfKey]
    }

    if (this._isValidKey(keyOrObjectWithId)) {
      return [keyOrObjectWithId]
    }

    const key = this.getKey(keyOrObjectWithId)
    this._assertValidKey(key)

    return [key, keyOrObjectWithId]
  }

  _touch (action, key) {
    if (this._buffering === 0) {
      const flush = this.bufferEvents()

      process.nextTick(flush)
    }

    if (action === 'add') {
      this._buffer[key] = this._buffer[key] ? 'update' : 'add'
    } else if (action === 'remove') {
      if (this._buffer[key] === 'add') {
        delete this._buffer[key]
      } else {
        this._buffer[key] = 'remove'
      }
    } else { // update
      if (!this._buffer[key]) {
        this._buffer[key] = 'update'
      }
    }
  }
}
<commit_msg>Remove unnecessary array.<commit_after>import forEach from 'lodash.foreach'
import {BaseError} from 'make-error'
import {EventEmitter} from 'events'

import isEmpty from './is-empty'

// ===================================================================

const {hasOwnProperty} = Object

// ===================================================================

export class BufferAlreadyFlushed extends BaseError {
  constructor () {
    super('buffer flush already requested')
  }
}

export class DuplicateIndex extends BaseError {
  constructor (name) {
    super('there is already an index with the name ' + name)
  }
}

export class DuplicateItem extends BaseError {
  constructor (key) {
    super('there is already a item with the key ' + key)
  }
}

export class IllegalTouch extends BaseError {
  constructor (value) {
    super('only an object value can be touched (found a ' + typeof value + ')')
  }
}

export class InvalidKey extends BaseError {
  constructor (key) {
    super('invalid key of type ' + typeof key)
  }
}

export class NoSuchIndex extends BaseError {
  constructor (name) {
    super('there is no index with the name ' + name)
  }
}

export class NoSuchItem extends BaseError {
  constructor (key) {
    super('there is no item with the key ' + key)
  }
}

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

export default class Collection extends EventEmitter {
  constructor () {
    super()

    this._buffer = Object.create(null)
    this._buffering = 0
    this._indexes = Object.create(null)
    this._indexedItems = Object.create(null)
    this._items = Object.create(null)
    this._size = 0
  }

  // Overridable method used to compute the key of an item when
  // unspecified.
  //
  // Default implementation returns the `id` property.
  getKey (value) {
    return value && value.id
  }

  // -----------------------------------------------------------------
  // Properties
  // -----------------------------------------------------------------

  get all () {
    return this._items
  }

  get indexes () {
    return this._indexedItems
  }

  get size () {
    return this._size
  }

  // -----------------------------------------------------------------
  // Manipulation
  // -----------------------------------------------------------------

  add (keyOrObjectWithId, valueIfKey = undefined) {
    const [key, value] = this._resolveItem(keyOrObjectWithId, valueIfKey)
    this._assertHasNot(key)

    this._items[key] = value
    this._size++
    this._touch('add', key)
  }

  clear () {
    forEach(this._items, (_, key) => {
      delete this._items[key]
      this._size--
      this._touch('remove', key)
    })
  }

  remove (keyOrObjectWithId) {
    const [key] = this._resolveItem(keyOrObjectWithId)
    this._assertHas(key)

    delete this._items[key]
    this._size--
    this._touch('remove', key)
  }

  set (keyOrObjectWithId, valueIfKey = undefined) {
    const [key, value] = this._resolveItem(keyOrObjectWithId, valueIfKey)

    const action = this.has(key) ? 'update' : 'add'
    this._items[key] = value
    if (action === 'add') {
      this._size++
    }
    this._touch(action, key)
  }

  touch (keyOrObjectWithId) {
    const [key] = this._resolveItem(keyOrObjectWithId)
    this._assertHas(key)
    const value = this.get(key)
    if (typeof value !== 'object' || value === null) {
      throw new IllegalTouch(value)
    }

    this._touch('update', key)

    return this.get(key)
  }

  update (keyOrObjectWithId, valueIfKey = undefined) {
    const [key, value] = this._resolveItem(keyOrObjectWithId, valueIfKey)
    this._assertHas(key)

    this._items[key] = value
    this._touch('update', key)
  }

  // -----------------------------------------------------------------
  // Query
  // -----------------------------------------------------------------

  get (key, defaultValue) {
    if (this.has(key)) {
      return this._items[key]
    }

    if (arguments.length > 1) {
      return defaultValue
    }

    // Throws a NoSuchItem.
    this._assertHas(key)
  }

  has (key) {
    return hasOwnProperty.call(this._items, key)
  }

  // -----------------------------------------------------------------
  // Indexes
  // -----------------------------------------------------------------

  createIndex (name, index) {
    const {_indexes: indexes} = this
    if (hasOwnProperty.call(indexes, name)) {
      throw new DuplicateIndex(name)
    }

    indexes[name] = index
    this._indexedItems[name] = index.items

    index._attachCollection(this)
  }

  deleteIndex (name) {
    const {_indexes: indexes} = this
    if (!hasOwnProperty.call(indexes, name)) {
      throw new NoSuchIndex(name)
    }

    const index = indexes[name]
    delete indexes[name]
    delete this._indexedItems[name]

    index._detachCollection(this)
  }

  // -----------------------------------------------------------------
  // Iteration
  // -----------------------------------------------------------------

  * [Symbol.iterator] () {
    const {_items: items} = this

    for (let key in items) {
      yield [key, items[key]]
    }
  }

  * keys () {
    const {_items: items} = this

    for (let key in items) {
      yield key
    }
  }

  * values () {
    const {_items: items} = this

    for (let key in items) {
      yield items[key]
    }
  }

  // -----------------------------------------------------------------
  // Events buffering
  // -----------------------------------------------------------------

  bufferEvents () {
    ++this._buffering

    let called = false
    return () => {
      if (called) {
        throw new BufferAlreadyFlushed()
      }
      called = true

      if (--this._buffering) {
        return
      }

      const data = {
        add: Object.create(null),
        remove: Object.create(null),
        update: Object.create(null)
      }

      for (let key in this._buffer) {
        data[this._buffer[key]][key] = this._items[key]
      }

      forEach(data, (items, action) => {
        if (!isEmpty(items)) {
          this.emit(action, items)
        }
      })

      this._buffer = Object.create(null)
    }
  }

  // =================================================================

  _assertHas (key) {
    if (!this.has(key)) {
      throw new NoSuchItem(key)
    }
  }

  _assertHasNot (key) {
    if (this.has(key)) {
      throw new DuplicateItem(key)
    }
  }

  _assertValidKey (key) {
    if (!this._isValidKey(key)) {
      throw new InvalidKey(key)
    }
  }

  _isValidKey (key) {
    return typeof key === 'number' || typeof key === 'string'
  }

  _resolveItem (keyOrObjectWithId, valueIfKey = undefined) {
    if (valueIfKey !== undefined) {
      this._assertValidKey(keyOrObjectWithId)

      return [keyOrObjectWithId, valueIfKey]
    }

    if (this._isValidKey(keyOrObjectWithId)) {
      return [keyOrObjectWithId]
    }

    const key = this.getKey(keyOrObjectWithId)
    this._assertValidKey(key)

    return [key, keyOrObjectWithId]
  }

  _touch (action, key) {
    if (this._buffering === 0) {
      const flush = this.bufferEvents()

      process.nextTick(flush)
    }

    if (action === 'add') {
      this._buffer[key] = this._buffer[key] ? 'update' : 'add'
    } else if (action === 'remove') {
      if (this._buffer[key] === 'add') {
        delete this._buffer[key]
      } else {
        this._buffer[key] = 'remove'
      }
    } else { // update
      if (!this._buffer[key]) {
        this._buffer[key] = 'update'
      }
    }
  }
}
<|endoftext|>


<commit_before>/*
 *  Copyright 2011 Twitter, Inc.
 *  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.
 */

var Hogan = require(__dirname + '/../lib/hogan.js');
var fs = require('fs');
var path = require('path');

// Substitute variables in the homepage with values from package.json
var homeTemplatePath = __dirname + '/../build/gh-pages/index.html.mustache';
var contextPath = __dirname + '/../build/gh-pages/package.json';

var homepage = fs.readFileSync(homeTemplatePath).toString();
var context = JSON.parse(fs.readFileSync(contextPath).toString());

var template = Hogan.compile(homepage);

fs.writeFileSync(path.dirname(homeTemplatePath) + '/index.html',
                 template.render(context));

fs.unlinkSync(homepateTemplatePath);<commit_msg>Fix typo in website script.<commit_after>/*
 *  Copyright 2011 Twitter, Inc.
 *  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.
 */

var Hogan = require(__dirname + '/../lib/hogan.js');
var fs = require('fs');
var path = require('path');

// Substitute variables in the homepage with values from package.json
var homeTemplatePath = __dirname + '/../build/gh-pages/index.html.mustache';
var contextPath = __dirname + '/../build/gh-pages/package.json';

var homepage = fs.readFileSync(homeTemplatePath).toString();
var context = JSON.parse(fs.readFileSync(contextPath).toString());

var template = Hogan.compile(homepage);

fs.writeFileSync(path.dirname(homeTemplatePath) + '/index.html',
                 template.render(context));

fs.unlinkSync(homeTemplatePath);<|endoftext|>