<commit_before>
var inherits = require('util').inherits,
    inspect  = require('util').inspect,
    repeat   = require('./util').repeat,
    out      = process.stdout

var types = require('./types')

// http://stackoverflow.com/a/5450113
var repeat = function (pattern, count) {
  if (count < 1) { return '' }
  var result = ''
  while (count > 1) {
    if (count & 1) result += pattern;
    count >>= 1, pattern += pattern
  }
  return result + pattern
}

// TODO: Refactor all this crazy indentation stuff!
var INDENT = 2

var _ind = 0,
    _i   = function () { return repeat(' ', _ind) },
    _w   = function (s) { out.write(_i() + s) },
    _win = function (s) {
      // Indent and write
      _w(s); _ind += INDENT
    },
    _wout = function (s) { _ind -= INDENT; _w(s) },
    _include_types = true

// Nodes ----------------------------------------------------------------------

var Node = function () {}
Node.prototype.print = function () { out.write(inspect(this)) }
Node.prototype.compile = function (context) {
  throw new Error('Compilation not yet implemented for node type: '+this.constructor.name)
}
Node.prototype.setParsePosition = function (parser) {
  this._file   = parser.file
  this._line   = parser.line
  this._column = parser.column
}

function NameType (name) {
  this.name = name.trim()
}
inherits(NameType, Node)
NameType.prototype.toString = function () { return this.name }

function FunctionType (args, ret) {
  this.args = args
  this.ret  = ret
}
inherits(FunctionType, Node)
FunctionType.prototype.toString = function () {
  var args = this.args.map(function (arg) { return arg.toString() }).join(', '),
      ret  = (this.ret ? this.ret.toString() : 'Void')
  return '('+args+') -> '+ret
}

function Let (name, immediateType) {
  this.name          = name.trim()
  this.immediateType = immediateType
}
inherits(Let, Node)
Let.prototype.print    = function () { _w(this.toString()+"\n") }
Let.prototype.toString = function () {
  var ret = this.name
  if (_include_types && this.type) {
    ret += ': '+this.type
  }
  return ret
}

// Quick and dirty clone of Let
function Var (name, immediateType) {
  this.name          = name.trim()
  this.immediateType = immediateType
}
inherits(Var, Node)
Var.prototype.print    = Let.prototype.print
Var.prototype.toString = Let.prototype.toString

var Expression = function () {}
inherits(Expression, Node)

function Group (expr) {
  this.expr = expr
}
inherits(Group, Expression)
Group.prototype.toString = function () { return '('+this.expr.toString()+')' }

function Binary (lexpr, op, rexpr) {
  this.lexpr = lexpr
  this.op    = op
  this.rexpr = rexpr
}
inherits(Binary, Expression)
Binary.prototype.isBinaryStatement = function () {
  return (this.op === '+=')
}
Binary.prototype.print = function () { out.write(this.toString()) }
Binary.prototype.toString = function () {
  return this.lexpr.toString()+' '+this.op+' '+this.rexpr.toString()
}

var Literal = function Literal (value, type) {
  this.value = value
  this.type  = (type !== undefined) ? type : null
}
inherits(Literal, Node)
Literal.prototype.print    = function () { out.write(this.toString()) }
Literal.prototype.toString = function () { return this.value.toString() }

function Assignment (type, lvalue, op, rvalue) {
  this.type   = type
  this.lvalue = lvalue
  this.rvalue = rvalue
  // Possible values: '=', '+=', or null
  this.op     = op
  // Only allowed .op for lets/vars is a '='
  if ((this.type === 'let' || this.type === 'var') && this.op !== '=') {
    throw new Error('Invalid operator on '+this.type+" statement: '"+this.op+"'")
  }
}
inherits(Assignment, Node)
Assignment.prototype.print = function () {
  var type = (this.type != 'path') ? (this.type+' ') : ''
  out.write(type + this.lvalue.toString())
  if (this.rvalue) {
    var op = (this.op === null) ? '?' : this.op.toString()
    out.write(' '+op+' ')
    // _ind += INDENT
    this.rvalue.print()
    // _ind -= INDENT
  }
}

function Path (name) { this.name = name }
inherits(Path, Node)
Path.prototype.toString = function () { return this.name }

function assertHasProperty (obj, prop) {
  var val = obj[prop]
  if (val) { return }
  throw new Error("Object missing property '"+prop+"'")
}

// Compiler sanity check to make sure all the args have the correct properties
function assertSaneArgs (args) {
  for (var i = args.length - 1; i >= 0; i--) {
    var arg = args[i]
    assertHasProperty(arg, 'name')
    assertHasProperty(arg, 'type')
    assertHasProperty(arg, 'def')
    var def = arg.def
    if (def && !(def instanceof Literal)) {
      throw new Error('Expected default to be an AST.Literal')
    }
  }// for
}// assertSaneArgs

function Function (args, ret, block) {
  this.args  = args
  this.ret   = ret
  this.block = block
  // Run some compiler checks
  assertSaneArgs(args)
}
inherits(Function, Node)
Function.prototype.print = function () {
  var args = this.args.map(function (arg) {
    var ret = arg.name
    if (arg.type) {
      ret += ': '+arg.type
    }
    return ret
  }).join(', ')
  out.write('func ('+args+') ')
  if (this.ret) {
    out.write('-> '+this.ret+' ')
  }
  this.block.print()
}

function Call(args) {
  this.args = args
}
inherits(Call, Node)
Call.prototype.toString = function () {
  return '('+this.args.map(function (arg) { return arg.toString() }).join(', ')+')'
}

function If (cond, block) {
  this.cond  = cond
  this.block = block
}
inherits(If, Node)
If.prototype.print = function () {
  var cond = this.cond.toString()
  out.write("if "+cond+" ")
  this.block.print()
}

function While (expr, block) {
  this.expr  = expr // Loop expression
  this.block = block
}
inherits(While, Node)
While.prototype.print = function () {
  out.write("while "+this.expr.toString()+" ")
  this.block.print()
}

function For (init, cond, after, block) {
  this.init  = init // Initialization
  this.cond  = cond // Condition
  this.after = after // Afterthought
  this.block = block
}
inherits(For, Node)
For.prototype.print = function () {
  out.write("for ")
  // Don't indent while we're writing out these statements
  var i = _ind
  _ind = 0;
  this.init.print();  out.write('; ')
  this.cond.print();  out.write('; ')
  this.after.print(); out.write(' ')
  // Restore indent and print the block
  _ind = i;
  this.block.print()
}

function Chain (name, tail) {
  this.name = name
  this.tail = tail
}
inherits(Chain, Node)
Chain.prototype.toString = function () {
  var base = this.name
  this.tail.forEach(function (expr) {
    base += expr.toString()
  })
  return base
}
Chain.prototype.print = function () { out.write(this.toString()) }

function Return (expr) {
  this.expr = expr
}
inherits(Return, Node)
Return.prototype.print    = function () { out.write(this.toString()) }
Return.prototype.toString = function () { return 'return '+this.expr.toString() }

function Root (statements) {
  this.statements = statements
}
inherits(Root, Node)
Root.prototype.print = function (includeTypes) {
  if (includeTypes !== undefined) {
    _include_types = includeTypes
  }
  _win("root {\n")
  this.statements.forEach(function (stmt) {
    _w('')
    stmt.print()
    out.write("\n")
  })
  _wout("}\n");
}

function Block (statements) {
  this.statements = statements
}
inherits(Block, Node)
Block.prototype.print = function () {
  out.write("{\n")
  _ind += INDENT
  this.statements.forEach(function (stmt) {
    _w('')
    stmt.print()
    out.write("\n")
  })
  _ind -= INDENT
  _w('}')
  // out.write(repeat(' ', _ind - INDENT) + '}')
}

module.exports = {
  Node: Node,
  NameType: NameType,
  FunctionType: FunctionType,
  Let: Let,
  Var: Var,
  Path: Path,
  Root: Root,
  Assignment: Assignment,
  Expression: Expression,
  Binary: Binary,
  Literal: Literal,
  Group: Group,
  Function: Function,
  Block: Block,
  If: If,
  While: While,
  For: For,
  Chain: Chain,
  Return: Return,
  Call: Call
}
<commit_msg>Add AST sanity check for function arguments<commit_after>
var inherits = require('util').inherits,
    inspect  = require('util').inspect,
    repeat   = require('./util').repeat,
    out      = process.stdout

var types = require('./types')

// http://stackoverflow.com/a/5450113
var repeat = function (pattern, count) {
  if (count < 1) { return '' }
  var result = ''
  while (count > 1) {
    if (count & 1) result += pattern;
    count >>= 1, pattern += pattern
  }
  return result + pattern
}

// TODO: Refactor all this crazy indentation stuff!
var INDENT = 2

var _ind = 0,
    _i   = function () { return repeat(' ', _ind) },
    _w   = function (s) { out.write(_i() + s) },
    _win = function (s) {
      // Indent and write
      _w(s); _ind += INDENT
    },
    _wout = function (s) { _ind -= INDENT; _w(s) },
    _include_types = true

// Nodes ----------------------------------------------------------------------

var Node = function () {}
Node.prototype.print = function () { out.write(inspect(this)) }
Node.prototype.compile = function (context) {
  throw new Error('Compilation not yet implemented for node type: '+this.constructor.name)
}
Node.prototype.setParsePosition = function (parser) {
  this._file   = parser.file
  this._line   = parser.line
  this._column = parser.column
}

function NameType (name) {
  this.name = name.trim()
}
inherits(NameType, Node)
NameType.prototype.toString = function () { return this.name }

function FunctionType (args, ret) {
  this.args = args
  this.ret  = ret
}
inherits(FunctionType, Node)
FunctionType.prototype.toString = function () {
  var args = this.args.map(function (arg) { return arg.toString() }).join(', '),
      ret  = (this.ret ? this.ret.toString() : 'Void')
  return '('+args+') -> '+ret
}

function Let (name, immediateType) {
  this.name          = name.trim()
  this.immediateType = immediateType
}
inherits(Let, Node)
Let.prototype.print    = function () { _w(this.toString()+"\n") }
Let.prototype.toString = function () {
  var ret = this.name
  if (_include_types && this.type) {
    ret += ': '+this.type
  }
  return ret
}

// Quick and dirty clone of Let
function Var (name, immediateType) {
  this.name          = name.trim()
  this.immediateType = immediateType
}
inherits(Var, Node)
Var.prototype.print    = Let.prototype.print
Var.prototype.toString = Let.prototype.toString

var Expression = function () {}
inherits(Expression, Node)

function Group (expr) {
  this.expr = expr
}
inherits(Group, Expression)
Group.prototype.toString = function () { return '('+this.expr.toString()+')' }

function Binary (lexpr, op, rexpr) {
  this.lexpr = lexpr
  this.op    = op
  this.rexpr = rexpr
}
inherits(Binary, Expression)
Binary.prototype.isBinaryStatement = function () {
  return (this.op === '+=')
}
Binary.prototype.print = function () { out.write(this.toString()) }
Binary.prototype.toString = function () {
  return this.lexpr.toString()+' '+this.op+' '+this.rexpr.toString()
}

var Literal = function Literal (value, type) {
  this.value = value
  this.type  = (type !== undefined) ? type : null
}
inherits(Literal, Node)
Literal.prototype.print    = function () { out.write(this.toString()) }
Literal.prototype.toString = function () { return this.value.toString() }

function Assignment (type, lvalue, op, rvalue) {
  this.type   = type
  this.lvalue = lvalue
  this.rvalue = rvalue
  // Possible values: '=', '+=', or null
  this.op     = op
  // Only allowed .op for lets/vars is a '='
  if ((this.type === 'let' || this.type === 'var') && this.op !== '=') {
    throw new Error('Invalid operator on '+this.type+" statement: '"+this.op+"'")
  }
}
inherits(Assignment, Node)
Assignment.prototype.print = function () {
  var type = (this.type != 'path') ? (this.type+' ') : ''
  out.write(type + this.lvalue.toString())
  if (this.rvalue) {
    var op = (this.op === null) ? '?' : this.op.toString()
    out.write(' '+op+' ')
    // _ind += INDENT
    this.rvalue.print()
    // _ind -= INDENT
  }
}

function Path (name) { this.name = name }
inherits(Path, Node)
Path.prototype.toString = function () { return this.name }

function assertHasProperty (obj, prop) {
  var val = obj[prop]
  if (val) { return }
  throw new Error("Object missing property '"+prop+"'")
}

function assertPropertyIsInstanceOf (recv, prop, type) {
  if (recv[prop] instanceof type) { return }
  throw new Error('Expected '+prop+' to be an instance of '+type.name)
}

// Compiler sanity check to make sure all the args have the correct properties
function assertSaneArgs (args) {
  for (var i = args.length - 1; i >= 0; i--) {
    var arg = args[i]
    assertHasProperty(arg, 'name')
    assertHasProperty(arg, 'type')
    var def = arg.def
    if (def && !(def instanceof Literal)) {
      throw new Error('Expected default to be an AST.Literal')
    }
  }// for
}// assertSaneArgs

function Function (args, ret, block) {
  this.args  = args
  this.ret   = ret
  this.block = block
  // Run some compiler checks
  assertPropertyIsInstanceOf(this, 'args', Array)
  assertSaneArgs(this.args)
}
inherits(Function, Node)
Function.prototype.print = function () {
  var args = this.args.map(function (arg) {
    var ret = arg.name
    if (arg.type) {
      ret += ': '+arg.type
    }
    return ret
  }).join(', ')
  out.write('func ('+args+') ')
  if (this.ret) {
    out.write('-> '+this.ret+' ')
  }
  this.block.print()
}

function Call(args) {
  this.args = args
}
inherits(Call, Node)
Call.prototype.toString = function () {
  return '('+this.args.map(function (arg) { return arg.toString() }).join(', ')+')'
}

function If (cond, block) {
  this.cond  = cond
  this.block = block
}
inherits(If, Node)
If.prototype.print = function () {
  var cond = this.cond.toString()
  out.write("if "+cond+" ")
  this.block.print()
}

function While (expr, block) {
  this.expr  = expr // Loop expression
  this.block = block
}
inherits(While, Node)
While.prototype.print = function () {
  out.write("while "+this.expr.toString()+" ")
  this.block.print()
}

function For (init, cond, after, block) {
  this.init  = init // Initialization
  this.cond  = cond // Condition
  this.after = after // Afterthought
  this.block = block
}
inherits(For, Node)
For.prototype.print = function () {
  out.write("for ")
  // Don't indent while we're writing out these statements
  var i = _ind
  _ind = 0;
  this.init.print();  out.write('; ')
  this.cond.print();  out.write('; ')
  this.after.print(); out.write(' ')
  // Restore indent and print the block
  _ind = i;
  this.block.print()
}

function Chain (name, tail) {
  this.name = name
  this.tail = tail
}
inherits(Chain, Node)
Chain.prototype.toString = function () {
  var base = this.name
  this.tail.forEach(function (expr) {
    base += expr.toString()
  })
  return base
}
Chain.prototype.print = function () { out.write(this.toString()) }

function Return (expr) {
  this.expr = expr
}
inherits(Return, Node)
Return.prototype.print    = function () { out.write(this.toString()) }
Return.prototype.toString = function () { return 'return '+this.expr.toString() }

function Root (statements) {
  this.statements = statements
}
inherits(Root, Node)
Root.prototype.print = function (includeTypes) {
  if (includeTypes !== undefined) {
    _include_types = includeTypes
  }
  _win("root {\n")
  this.statements.forEach(function (stmt) {
    _w('')
    stmt.print()
    out.write("\n")
  })
  _wout("}\n");
}

function Block (statements) {
  this.statements = statements
}
inherits(Block, Node)
Block.prototype.print = function () {
  out.write("{\n")
  _ind += INDENT
  this.statements.forEach(function (stmt) {
    _w('')
    stmt.print()
    out.write("\n")
  })
  _ind -= INDENT
  _w('}')
  // out.write(repeat(' ', _ind - INDENT) + '}')
}

module.exports = {
  Node: Node,
  NameType: NameType,
  FunctionType: FunctionType,
  Let: Let,
  Var: Var,
  Path: Path,
  Root: Root,
  Assignment: Assignment,
  Expression: Expression,
  Binary: Binary,
  Literal: Literal,
  Group: Group,
  Function: Function,
  Block: Block,
  If: If,
  While: While,
  For: For,
  Chain: Chain,
  Return: Return,
  Call: Call
}
<|endoftext|>


<commit_before>define([
  'dojo/dom-construct',
  'esri/map',
  'esri-utils/imageServiceUtils'
], function(
  domConstruct,
  Map,
  imageServiceUtils
) {

  describe('when setting layer url', function() {
    var map;
    var url;

    // runs before each spec
    beforeEach(function() {
      // test service
      url = "/arcgis/rest/services/LandsatGLS/FalseColor/ImageServer";
      // init map
      var div = domConstruct.create('div', {style: 'width:300px;height:200px'});
      map = new Map(div, {
          basemap: "topo",
          center: [-122.45, 37.75],
          zoom: 13,
          sliderStyle: "small"
      });
    });

    // runs after each spec
    afterEach(function() {
      // clear map
      map.destroy();
      map = null;
    });

    // specs
    it("should add the layer if not already added", function(done) {
      var options = {id: 'myLayerId'},
        myLayer;
      map.on('layer-add-result', function(e) {
        console.log(e.layer.id, e.layer.url);
        if (e.layer.url === url) {
          myLayer = e.layer;
          expect(myLayer).toBeDefined();
          expect(myLayer.id).toEqual(options.id);
          expect(myLayer.url).toEqual(url);
          done();
        }
      });
      imageServiceUtils.setUrl(map, url, options);
    });

    it("should replace the layer if already added", function(done) {
      var options = {id: 'myLayerId'},
        replaceUrl = '/arcgis/rest/services/LandsatGLS/GLS2010_Enhanced/ImageServer',
        myLayer;
      // after each layer has been added
      map.on('layer-add-result', function(e) {
        console.log(e.layer.id, e.layer.url);
        if (e.layer.url === url) {
          // layer added for the first time
          myLayer = e.layer;
          expect(myLayer).toBeDefined();
          expect(myLayer.id).toEqual(options.id);
          expect(myLayer.url).toEqual(url);

          // now let's update the layer and re test
          imageServiceUtils.setUrl(map, replaceUrl, options);
        } else if (e.layer.url === replaceUrl) {
          // second time
          // - id should be same, but url updated
          myLayer = e.layer;
          expect(myLayer).toBeDefined();
          expect(myLayer.id).toEqual(options.id);
          expect(myLayer.url).toEqual(replaceUrl);
          done();
        }
      });
      imageServiceUtils.setUrl(map, url, options);
    });
  });
});
<commit_msg>map tests running in own suite<commit_after>define([
  'dojo/dom-construct',
  'esri/map',
  'esri-utils/imageServiceUtils'
], function(
  domConstruct,
  Map,
  imageServiceUtils
) {

  describe('map tests', function() {
    var map;

    // create the map
    beforeEach(function() {
      var div = domConstruct.create('div', {style: 'width:300px;height:200px'});
      map = new Map(div, {
          basemap: "topo",
          center: [-122.45, 37.75],
          zoom: 13,
          sliderStyle: "small"
      });
      console.log('created map');
    });

    // destroy the map
    afterEach(function() {
      map.destroy();
      map = null;
    });

    describe('when setting layer url', function() {
      var url;

      // default image service
      beforeEach(function() {
        url = "/arcgis/rest/services/LandsatGLS/FalseColor/ImageServer";
      });

      // specs
      it("should add the layer if not already added", function(done) {
        var options = {id: 'myLayerId'},
          myLayer;
        map.on('layer-add-result', function(e) {
          console.log(e.layer.id, e.layer.url);
          if (e.layer.url === url) {
            myLayer = e.layer;
            expect(myLayer).toBeDefined();
            expect(myLayer.id).toEqual(options.id);
            expect(myLayer.url).toEqual(url);
            done();
          }
        });
        imageServiceUtils.setUrl(map, url, options);
      });

      it("should replace the layer if already added", function(done) {
        var options = {id: 'myLayerId'},
          replaceUrl = '/arcgis/rest/services/LandsatGLS/GLS2010_Enhanced/ImageServer',
          myLayer;
        // after each layer has been added
        map.on('layer-add-result', function(e) {
          console.log(e.layer.id, e.layer.url);
          if (e.layer.url === url) {
            // layer added for the first time
            myLayer = e.layer;
            expect(myLayer).toBeDefined();
            expect(myLayer.id).toEqual(options.id);
            expect(myLayer.url).toEqual(url);

            // now let's update the layer and re test
            imageServiceUtils.setUrl(map, replaceUrl, options);
          } else if (e.layer.url === replaceUrl) {
            // second time
            // - id should be same, but url updated
            myLayer = e.layer;
            expect(myLayer).toBeDefined();
            expect(myLayer.id).toEqual(options.id);
            expect(myLayer.url).toEqual(replaceUrl);
            done();
          }
        });
        imageServiceUtils.setUrl(map, url, options);
      });
    });
  });
});
<|endoftext|>


<commit_before>/**
 * Copyright 2015, Yahoo! Inc.
 * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */

import Ember from 'ember';

const { get, set, deprecate } = Ember;

const TranslationModel = Ember.Object.extend({
  init() {
    this._super(...arguments);

    if (!this.translations) {
      this.translations = Object.create(null);
    }
  },

  /**
   * Add a single translation
   */
  addTranslation(key, value) {
    set(this.translations, key, value);
  },

  /**
   * Adds a translation hash
   */
  addTranslations(translationsObject) {
    for (let key in translationsObject) {
      if (typeof translationsObject[key] === 'string') {
        this.addTranslation(key, translationsObject[key]);
      }
    }
  },

  /**
   * Custom accessor hook that can be overridden.
   * This would enable consumers that have dot notated strings
   * to implement this function as `return this[key];`
   */
  getValue(key) {
    let translation = get(this.translations, key);

    if (typeof translation === 'string') {
      return translation;
    }

    translation = get(this, key);

    if (typeof translation === 'string') {
      deprecate('[ember-intl] translations should be added via the `addTranslations`/`addTranslation` API.', false, {
        id: 'ember-intl-add-translation'
      });

      return translation;
    }
  },

  /**
   * Determines if the translation model contains a key
   */
  has(key) {
    return typeof this.getValue(key) === 'string';
  }
});

export default TranslationModel;
<commit_msg>Translation model no longer asserts string<commit_after>/**
 * Copyright 2015, Yahoo! Inc.
 * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
 */

import Ember from 'ember';

const { get, set, deprecate } = Ember;

const TranslationModel = Ember.Object.extend({
  init() {
    this._super(...arguments);

    if (!this.translations) {
      this.translations = Object.create(null);
    }
  },

  /**
   * Add a single translation
   */
  addTranslation(key, value) {
    set(this.translations, key, value);
  },

  /**
   * Adds a translation hash
   */
  addTranslations(translationsObject) {
    for (let key in translationsObject) {
      this.addTranslation(key, translationsObject[key]);
    }
  },

  /**
   * Custom accessor hook that can be overridden.
   * This would enable consumers that have dot notated strings
   * to implement this function as `return this[key];`
   */
  getValue(key) {
    let translation = get(this.translations, key);

    if (typeof translation === 'string') {
      return translation;
    }

    translation = get(this, key);

    if (typeof translation === 'string') {
      deprecate('[ember-intl] translations should be added via the `addTranslations`/`addTranslation` API.', false, {
        id: 'ember-intl-add-translation'
      });

      return translation;
    }
  },

  /**
   * Determines if the translation model contains a key
   */
  has(key) {
    return typeof this.getValue(key) === 'string';
  }
});

export default TranslationModel;
<|endoftext|>


<commit_before>'use babel'

import MessageRegistry from '../lib/message-registry'

describe('Message Registry', function() {
  function getMessage() {
    return { type: 'Error', filePath: '/tmp/passwd', range: [[0, 1], [1, 0]], text: String(Math.random()) }
  }
  function getLinter(name) {
    return { name }
  }

  let messageRegistry
  beforeEach(function() {
    messageRegistry = new MessageRegistry()
    messageRegistry.debouncedUpdate = jasmine.createSpy('debouncedUpdate')
  })
  afterEach(function() {
    messageRegistry.dispose()
  })

  describe('::set', function() {
    it('stores results using both buffer and linter', function() {
      const messageFirst = getMessage()
      const messageSecond = getMessage()
      const messageThird = getMessage()
      const linter = getLinter('any')
      const buffer = {}
      let info

      messageRegistry.set({ linter, buffer: null, messages: [messageFirst] })
      expect(messageRegistry.debouncedUpdate.calls.length).toBe(1)
      expect(messageRegistry.messagesMap.size).toBe(1)
      info = [...messageRegistry.messagesMap][0]

      expect(info.changed).toBe(true)
      expect(info.linter).toBe(linter)
      expect(info.buffer).toBe(null)
      expect(info.oldMessages.length).toBe(0)
      expect(info.messages.length).toBe(1)
      expect(info.messages[0]).toBe(messageFirst)

      messageRegistry.set({ linter, buffer: null, messages: [messageFirst] })
      expect(messageRegistry.debouncedUpdate.calls.length).toBe(2)
      expect(messageRegistry.messagesMap.size).toBe(1)
      info = [...messageRegistry.messagesMap][0]

      expect(info.changed).toBe(true)
      expect(info.linter).toBe(linter)
      expect(info.buffer).toBe(null)
      expect(info.messages.length).toBe(1)
      expect(info.messages[0]).toBe(messageFirst)

      messageRegistry.set({ linter, buffer, messages: [messageThird] })
      expect(messageRegistry.debouncedUpdate.calls.length).toBe(3)
      expect(messageRegistry.messagesMap.size).toBe(2)
      info = [...messageRegistry.messagesMap][0]

      expect(info.changed).toBe(true)
      expect(info.linter).toBe(linter)
      expect(info.buffer).toBe(null)
      expect(info.messages.length).toBe(1)
      expect(info.messages[0]).toBe(messageFirst)

      info = [...messageRegistry.messagesMap][1]

      expect(info.changed).toBe(true)
      expect(info.linter).toBe(linter)
      expect(info.buffer).toBe(buffer)
      expect(info.messages.length).toBe(1)
      expect(info.messages[0]).toBe(messageThird)

      messageRegistry.set({ linter, buffer: null, messages: [messageFirst, messageSecond] })
      expect(messageRegistry.debouncedUpdate.calls.length).toBe(4)
      expect(messageRegistry.messagesMap.size).toBe(2)
      info = [...messageRegistry.messagesMap][0]

      expect(info.changed).toBe(true)
      expect(info.linter).toBe(linter)
      expect(info.buffer).toBe(null)
      expect(info.messages.length).toBe(2)
      expect(info.messages[0]).toBe(messageFirst)
      expect(info.messages[1]).toBe(messageSecond)

      info = [...messageRegistry.messagesMap][1]

      expect(info.changed).toBe(true)
      expect(info.linter).toBe(linter)
      expect(info.buffer).toBe(buffer)
      expect(info.messages.length).toBe(1)
      expect(info.messages[0]).toBe(messageThird)
    })
  })

  describe('updates (::update & ::onDidUpdateMessages)', function() {
    it('notifies on changes', function() {
      let called = 0
      const message = getMessage()
      messageRegistry.onDidUpdateMessages(function({ added, removed, messages }) {
        called++
        expect(added.length).toBe(1)
        expect(removed.length).toBe(0)
        expect(messages.length).toBe(1)
        expect(added).toEqual(messages)
        expect(added[0]).toBe(message)
      })
      messageRegistry.set({ linter: {}, buffer: null, messages: [message] })
      messageRegistry.update()
      expect(called).toBe(1)
    })
    it('notifies properly for as many linters as you want', function() {
      const buffer = {}
      const linterFirst = getLinter('named')
      const linterSecond = {}
      const messageFirst = getMessage()
      const messageSecond = getMessage()
      const messageThird = getMessage()
      let called = 0

      messageRegistry.onDidUpdateMessages(function({ added, removed, messages }) {
        called++

        if (called === 1) {
          expect(added.length).toBe(1)
          expect(removed.length).toBe(0)
          expect(added).toEqual(messages)
          expect(added[0]).toEqual(messageFirst)
          expect(added[0].name).toEqual('named')
        } else if (called === 2) {
          expect(added.length).toBe(2)
          expect(removed.length).toBe(0)
          expect(messages.length).toBe(3)
          expect(messages[0]).toBe(messageFirst)
          expect(messages[1]).toBe(messageSecond)
          expect(messages[2]).toBe(messageThird)
        } else if (called === 3) {
          expect(added.length).toBe(0)
          expect(removed.length).toBe(1)
          expect(removed[0]).toBe(messageFirst)
          expect(messages.length).toBe(2)
          expect(messages[0]).toBe(messageSecond)
          expect(messages[1]).toBe(messageThird)
        } else if (called === 4) {
          expect(added.length).toBe(0)
          expect(removed.length).toBe(2)
          expect(messages.length).toBe(0)
          expect(removed[0]).toBe(messageSecond)
          expect(removed[1]).toBe(messageThird)
        } else {
          throw new Error('Unnecessary update call')
        }
      })

      messageRegistry.set({ buffer, linter: linterFirst, messages: [messageFirst] })
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(1)
      messageRegistry.set({ buffer, linter: linterSecond, messages: [messageSecond, messageThird] })
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(2)
      messageRegistry.set({ buffer, linter: linterFirst, messages: [] })
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(3)
      messageRegistry.set({ buffer, linter: linterSecond, messages: [] })
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(4)
    })

    it('sets key, severity and name on messages', function() {
      const linter = {}
      const buffer = {}
      const messageFirst = getMessage()
      const messageSecond = getMessage()
      const messageThird = getMessage()

      let called = 0

      messageRegistry.onDidUpdateMessages(function({ added, removed, messages }) {
        called++
        if (called === 1) {
          // All messages are new
          expect(added.length).toBe(2)
          expect(removed.length).toBe(0)
          expect(messages.length).toBe(2)
          expect(added).toEqual(messages)
          expect(typeof messages[0].key).toBe('string')
          expect(typeof messages[1].key).toBe('string')
          expect(typeof messages[0].severity).toBe('string')
          expect(typeof messages[1].severity).toBe('string')
          expect(messages[0].name).toBe(null)
          expect(messages[1].name).toBe(null)
        } else {
          // One removed, one added
          expect(added.length).toBe(1)
          expect(removed.length).toBe(1)
          expect(messages.length).toBe(2)
          expect(messages.indexOf(added[0])).not.toBe(-1)
          expect(typeof messages[0].key).toBe('string')
          expect(typeof messages[1].key).toBe('string')
          expect(typeof messages[0].severity).toBe('string')
          expect(typeof messages[1].severity).toBe('string')
          expect(messages[0].name).toBe(null)
          expect(messages[1].name).toBe(null)
        }
      })

      messageRegistry.set({ buffer, linter, messages: [messageFirst, messageSecond] })
      messageRegistry.update()
      messageRegistry.set({ buffer, linter, messages: [messageFirst, messageThird] })
      messageRegistry.update()
      expect(called).toBe(2)
    })
  })

  describe('::deleteByBuffer', function() {
    it('deletes the messages and sends them in an event', function() {
      const linter = {}
      const buffer = {}
      const messageFirst = getMessage()
      const messageSecond = getMessage()

      let called = 0

      messageRegistry.onDidUpdateMessages(function({ added, removed, messages }) {
        called++
        if (called === 1) {
          expect(added.length).toBe(2)
          expect(removed.length).toBe(0)
          expect(messages.length).toBe(2)
          expect(added).toEqual(messages)
          expect(added[0]).toBe(messageFirst)
          expect(added[1]).toBe(messageSecond)
        } else if (called === 2) {
          expect(added.length).toBe(0)
          expect(removed.length).toBe(2)
          expect(messages.length).toBe(0)
          expect(removed[0]).toBe(messageFirst)
          expect(removed[1]).toBe(messageSecond)
        } else {
          throw new Error('Unnecessary update call')
        }
      })
      messageRegistry.set({ buffer, linter, messages: [messageFirst, messageSecond] })
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(1)
      messageRegistry.deleteByBuffer(buffer)
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(2)
    })
  })

  describe('::deleteByLinter', function() {
    it('deletes the messages and sends them in an event', function() {
      const linter = {}
      const buffer = {}
      const messageFirst = getMessage()
      const messageSecond = getMessage()

      let called = 0

      messageRegistry.onDidUpdateMessages(function({ added, removed, messages }) {
        called++
        if (called === 1) {
          expect(added.length).toBe(2)
          expect(removed.length).toBe(0)
          expect(messages.length).toBe(2)
          expect(added).toEqual(messages)
          expect(added[0]).toBe(messageFirst)
          expect(added[1]).toBe(messageSecond)
        } else if (called === 2) {
          expect(added.length).toBe(0)
          expect(removed.length).toBe(2)
          expect(messages.length).toBe(0)
          expect(removed[0]).toBe(messageFirst)
          expect(removed[1]).toBe(messageSecond)
        } else {
          throw new Error('Unnecessary update call')
        }
      })
      messageRegistry.set({ buffer, linter, messages: [messageFirst, messageSecond] })
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(1)
      messageRegistry.deleteByLinter(linter)
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(2)
    })
  })
})
<commit_msg>:white_check_mark: Add specs for message registry bug<commit_after>'use babel'

import MessageRegistry from '../lib/message-registry'

describe('Message Registry', function() {
  function getMessage() {
    return { type: 'Error', filePath: '/tmp/passwd', range: [[0, 1], [1, 0]], text: String(Math.random()) }
  }
  function getLinter(name) {
    return { name }
  }

  let messageRegistry
  beforeEach(function() {
    messageRegistry = new MessageRegistry()
    messageRegistry.debouncedUpdate = jasmine.createSpy('debouncedUpdate')
  })
  afterEach(function() {
    messageRegistry.dispose()
  })

  describe('::set', function() {
    it('stores results using both buffer and linter', function() {
      const messageFirst = getMessage()
      const messageSecond = getMessage()
      const messageThird = getMessage()
      const linter = getLinter('any')
      const buffer = {}
      let info

      messageRegistry.set({ linter, buffer: null, messages: [messageFirst] })
      expect(messageRegistry.debouncedUpdate.calls.length).toBe(1)
      expect(messageRegistry.messagesMap.size).toBe(1)
      info = [...messageRegistry.messagesMap][0]

      expect(info.changed).toBe(true)
      expect(info.linter).toBe(linter)
      expect(info.buffer).toBe(null)
      expect(info.oldMessages.length).toBe(0)
      expect(info.messages.length).toBe(1)
      expect(info.messages[0]).toBe(messageFirst)

      messageRegistry.set({ linter, buffer: null, messages: [messageFirst] })
      expect(messageRegistry.debouncedUpdate.calls.length).toBe(2)
      expect(messageRegistry.messagesMap.size).toBe(1)
      info = [...messageRegistry.messagesMap][0]

      expect(info.changed).toBe(true)
      expect(info.linter).toBe(linter)
      expect(info.buffer).toBe(null)
      expect(info.messages.length).toBe(1)
      expect(info.messages[0]).toBe(messageFirst)

      messageRegistry.set({ linter, buffer, messages: [messageThird] })
      expect(messageRegistry.debouncedUpdate.calls.length).toBe(3)
      expect(messageRegistry.messagesMap.size).toBe(2)
      info = [...messageRegistry.messagesMap][0]

      expect(info.changed).toBe(true)
      expect(info.linter).toBe(linter)
      expect(info.buffer).toBe(null)
      expect(info.messages.length).toBe(1)
      expect(info.messages[0]).toBe(messageFirst)

      info = [...messageRegistry.messagesMap][1]

      expect(info.changed).toBe(true)
      expect(info.linter).toBe(linter)
      expect(info.buffer).toBe(buffer)
      expect(info.messages.length).toBe(1)
      expect(info.messages[0]).toBe(messageThird)

      messageRegistry.set({ linter, buffer: null, messages: [messageFirst, messageSecond] })
      expect(messageRegistry.debouncedUpdate.calls.length).toBe(4)
      expect(messageRegistry.messagesMap.size).toBe(2)
      info = [...messageRegistry.messagesMap][0]

      expect(info.changed).toBe(true)
      expect(info.linter).toBe(linter)
      expect(info.buffer).toBe(null)
      expect(info.messages.length).toBe(2)
      expect(info.messages[0]).toBe(messageFirst)
      expect(info.messages[1]).toBe(messageSecond)

      info = [...messageRegistry.messagesMap][1]

      expect(info.changed).toBe(true)
      expect(info.linter).toBe(linter)
      expect(info.buffer).toBe(buffer)
      expect(info.messages.length).toBe(1)
      expect(info.messages[0]).toBe(messageThird)
    })
  })

  describe('updates (::update & ::onDidUpdateMessages)', function() {
    it('notifies on changes', function() {
      let called = 0
      const message = getMessage()
      messageRegistry.onDidUpdateMessages(function({ added, removed, messages }) {
        called++
        expect(added.length).toBe(1)
        expect(removed.length).toBe(0)
        expect(messages.length).toBe(1)
        expect(added).toEqual(messages)
        expect(added[0]).toBe(message)
      })
      messageRegistry.set({ linter: {}, buffer: null, messages: [message] })
      messageRegistry.update()
      expect(called).toBe(1)
    })
    it('notifies properly for as many linters as you want', function() {
      const buffer = {}
      const linterFirst = getLinter('named')
      const linterSecond = {}
      const messageFirst = getMessage()
      const messageSecond = getMessage()
      const messageThird = getMessage()
      let called = 0

      messageRegistry.onDidUpdateMessages(function({ added, removed, messages }) {
        called++

        if (called === 1) {
          expect(added.length).toBe(1)
          expect(removed.length).toBe(0)
          expect(added).toEqual(messages)
          expect(added[0]).toEqual(messageFirst)
          expect(added[0].name).toEqual('named')
        } else if (called === 2) {
          expect(added.length).toBe(2)
          expect(removed.length).toBe(0)
          expect(messages.length).toBe(3)
          expect(messages[0]).toBe(messageFirst)
          expect(messages[1]).toBe(messageSecond)
          expect(messages[2]).toBe(messageThird)
        } else if (called === 3) {
          expect(added.length).toBe(0)
          expect(removed.length).toBe(1)
          expect(removed[0]).toBe(messageFirst)
          expect(messages.length).toBe(2)
          expect(messages[0]).toBe(messageSecond)
          expect(messages[1]).toBe(messageThird)
        } else if (called === 4) {
          expect(added.length).toBe(0)
          expect(removed.length).toBe(2)
          expect(messages.length).toBe(0)
          expect(removed[0]).toBe(messageSecond)
          expect(removed[1]).toBe(messageThird)
        } else {
          throw new Error('Unnecessary update call')
        }
      })

      messageRegistry.set({ buffer, linter: linterFirst, messages: [messageFirst] })
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(1)
      messageRegistry.set({ buffer, linter: linterSecond, messages: [messageSecond, messageThird] })
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(2)
      messageRegistry.set({ buffer, linter: linterFirst, messages: [] })
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(3)
      messageRegistry.set({ buffer, linter: linterSecond, messages: [] })
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(4)
    })

    it('sets key, severity and name on messages', function() {
      const linter = {}
      const buffer = {}
      const messageFirst = getMessage()
      const messageSecond = getMessage()
      const messageThird = getMessage()

      let called = 0

      messageRegistry.onDidUpdateMessages(function({ added, removed, messages }) {
        called++
        if (called === 1) {
          // All messages are new
          expect(added.length).toBe(2)
          expect(removed.length).toBe(0)
          expect(messages.length).toBe(2)
          expect(added).toEqual(messages)
          expect(typeof messages[0].key).toBe('string')
          expect(typeof messages[1].key).toBe('string')
          expect(typeof messages[0].severity).toBe('string')
          expect(typeof messages[1].severity).toBe('string')
          expect(messages[0].name).toBe(null)
          expect(messages[1].name).toBe(null)
        } else {
          // One removed, one added
          expect(added.length).toBe(1)
          expect(removed.length).toBe(1)
          expect(messages.length).toBe(2)
          expect(messages.indexOf(added[0])).not.toBe(-1)
          expect(typeof messages[0].key).toBe('string')
          expect(typeof messages[1].key).toBe('string')
          expect(typeof messages[0].severity).toBe('string')
          expect(typeof messages[1].severity).toBe('string')
          expect(messages[0].name).toBe(null)
          expect(messages[1].name).toBe(null)
        }
      })

      messageRegistry.set({ buffer, linter, messages: [messageFirst, messageSecond] })
      messageRegistry.update()
      messageRegistry.set({ buffer, linter, messages: [messageFirst, messageThird] })
      messageRegistry.update()
      expect(called).toBe(2)
    })

    it('checks if an old message has updated, if so invalidates it properly', function() {
      let called = 0
      const messageFirst = getMessage()
      const messageSecond = Object.assign({}, messageFirst)
      const linter = {}
      const buffer = {}

      messageRegistry.onDidUpdateMessages(function({ added, removed, messages }) {
        called++
        if (called === 1) {
          expect(messages.length).toBe(1)
          expect(removed.length).toBe(0)
          expect(added.length).toBe(1)
          expect(added[0]).toBe(messageFirst)
        } else {
          expect(messages.length).toBe(1)
          expect(removed.length).toBe(1)
          expect(added.length).toBe(1)
          expect(added[0]).toBe(messageSecond)
          expect(removed[0]).toBe(messageFirst)
        }
      })

      expect(called).toBe(0)
      messageRegistry.set({ buffer, linter, messages: [messageFirst] })
      messageRegistry.update()
      messageRegistry.set({ buffer, linter, messages: [messageSecond] })
      messageRegistry.update()
      expect(called).toBe(1)
      messageFirst.text = 'Hellow'
      messageRegistry.set({ buffer, linter, messages: [messageSecond] })
      messageRegistry.update()
      expect(called).toBe(2)
    })
  })

  describe('::deleteByBuffer', function() {
    it('deletes the messages and sends them in an event', function() {
      const linter = {}
      const buffer = {}
      const messageFirst = getMessage()
      const messageSecond = getMessage()

      let called = 0

      messageRegistry.onDidUpdateMessages(function({ added, removed, messages }) {
        called++
        if (called === 1) {
          expect(added.length).toBe(2)
          expect(removed.length).toBe(0)
          expect(messages.length).toBe(2)
          expect(added).toEqual(messages)
          expect(added[0]).toBe(messageFirst)
          expect(added[1]).toBe(messageSecond)
        } else if (called === 2) {
          expect(added.length).toBe(0)
          expect(removed.length).toBe(2)
          expect(messages.length).toBe(0)
          expect(removed[0]).toBe(messageFirst)
          expect(removed[1]).toBe(messageSecond)
        } else {
          throw new Error('Unnecessary update call')
        }
      })
      messageRegistry.set({ buffer, linter, messages: [messageFirst, messageSecond] })
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(1)
      messageRegistry.deleteByBuffer(buffer)
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(2)
    })
  })

  describe('::deleteByLinter', function() {
    it('deletes the messages and sends them in an event', function() {
      const linter = {}
      const buffer = {}
      const messageFirst = getMessage()
      const messageSecond = getMessage()

      let called = 0

      messageRegistry.onDidUpdateMessages(function({ added, removed, messages }) {
        called++
        if (called === 1) {
          expect(added.length).toBe(2)
          expect(removed.length).toBe(0)
          expect(messages.length).toBe(2)
          expect(added).toEqual(messages)
          expect(added[0]).toBe(messageFirst)
          expect(added[1]).toBe(messageSecond)
        } else if (called === 2) {
          expect(added.length).toBe(0)
          expect(removed.length).toBe(2)
          expect(messages.length).toBe(0)
          expect(removed[0]).toBe(messageFirst)
          expect(removed[1]).toBe(messageSecond)
        } else {
          throw new Error('Unnecessary update call')
        }
      })
      messageRegistry.set({ buffer, linter, messages: [messageFirst, messageSecond] })
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(1)
      messageRegistry.deleteByLinter(linter)
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      messageRegistry.update()
      expect(called).toBe(2)
    })
  })
})
<|endoftext|>


<commit_before>var editor;

$(document).ready(function() {
    var tabs = {};
    var modelist = ace.require("ace/ext/modelist");
    var help_session = ace.createEditSession(" ____  _               _             \n\
|  _ \\(_)_ __ ___  ___| |_ ___  _ __ \n\
| | | | | '__/ _ \\/ __| __/ _ \\| '__|\n\
| |_| | | | |  __/ (__| || (_) | |   \n\
|____/|_|_|  \\___|\\___|\\__\\___/|_|   \n\
\n\
Use the panel on the right to select a file to edit.\n\
Press Ctrl + S to save your changes.\n\
\n\
You can right click files and folders for more options.\n\
You can also drag and drop files to folders to upload them.\n\
You can drag files and folders around to move them.");

    editor = ace.edit("editor");
    editor.setSession(help_session);
    editor.setOptions({
        "fontSize": "12pt",
        "showPrintMargin": false
    });
    function checkTabClean() {
        var tab = $("#tabs .tab.active");
        if (tab.length) {
            tab.toggleClass("unsaved", !editor.session.getUndoManager().isClean());
        }
    }
    editor.on("input", function() {
        checkTabClean();
    });
    function triggerDelete(item) {
        var filepath = get_path(item);
        if (item.hasClass("file")) {
            filepath += item.attr("data-name");
        }
        if (confirm("Are you sure you want to delete:\n" + filepath)) {
            $.post(delete_endpoint + "?name=" + encodeURIComponent(filepath), function(data) {
                if (data.error) {
                    Messenger().error(data.error);
                }
                else {
                    if (item.hasClass("folder")) {
                        var depth = parseInt(item.attr("data-depth"));
                        var children = item.nextUntil("div.folder[data-depth=" + depth + "]").filter(function(v) { return parseInt($(this).attr("data-depth")) > depth; });
                        children.remove();
                    }
                    item.remove();
                }
            });
        }
    }
    function triggerCreate(item, type) {
        var filepath = get_path(item);
        var name = prompt("Enter a name for your new " + (type ? "file" : "directory") + ".");
        if (name) {
            $.post(create_endpoint, { name: name, path: filepath, type: (type ? "f" : "d") }, function(data) {
                if (data.error) {
                    Messenger().error(data.error);
                }
                else {
                    if (filepath == "") {
                        initFiles();
                    }
                    else {
                        var folder = item;
                        if (!item.hasClass("folder")) {
                            folder = item.prevAll("div.folder[data-depth=" + (parseInt(item.attr("data-depth")) - 1) + "]:first");
                        }
                        var depth = parseInt(folder.attr("data-depth"));
                        var children = folder.nextUntil("div.folder[data-depth=" + depth + "]").filter(function(v) { return parseInt($(this).attr("data-depth")) > depth; });
                        children.remove();
                        folder.removeClass("loaded");
                        folder.click();
                    }
                }
            });
        }
    }
    function triggerDownload(item) {
        var filepath = get_path(item);
        if (item.hasClass("file")) {
            filepath += item.attr("data-name");
        }
        $("#download").attr("src", download_endpoint + "?name=" + encodeURIComponent(filepath));
    }
    function triggerRename(item) {
        var filepath = get_path(item);
        if (item.hasClass("file")) {
            filepath += item.attr("data-name");
        }
        var name = prompt("Enter a new name for the file or directory:\n" + filepath);
        if (name) {
            $.post(rename_endpoint, { name: filepath, newname: name }, function(data) {
                if (data.error) {
                    Messenger().error(data.error);
                }
                else {
                    item.attr("data-name", name);
                    item.find("span").text(name);
                }
            });
        }

    }
    $("#files").on("dragover", function(e) {
        e.preventDefault();
        e.stopPropagation();
    });
    $("#files").on("drop", function(e) {
        if (e.originalEvent.dataTransfer) {
            if (e.originalEvent.dataTransfer.files.length) {
                e.preventDefault();
                e.stopPropagation();
                var files = e.originalEvent.dataTransfer.files;
                var path = "";
                if (e.target !== $("#files")[0]) {
                    path = get_path($(e.target).closest("div.folder"));
                }
                var formData = new FormData();
                formData.append("path", path);
                for (var i = 0; i < files.length; i++) {
                    formData.append("file[]", files[i], files[i].name);
                }
                $.ajax({
                    url: upload_endpoint,
                    type: "POST",
                    data: formData,
                    processData: false,
                    contentType: false,
                    success: function(data) {
                        if (data.error) {
                            Messenger().error(data.error);
                        }
                        else {
                            if (path == "") {
                                initFiles();
                            }
                            else {
                                var folder = $(e.target).closest("div.folder");
                                var depth = parseInt(folder.attr("data-depth"));
                                var children = folder.nextUntil("div.folder[data-depth=" + depth + "]").filter(function(v) { return parseInt($(this).attr("data-depth")) > depth; });
                                children.remove();
                                folder.removeClass("loaded");
                                folder.click();
                            }
                        }
                    }
                });
            }
        }
    });
    $.contextMenu({
        "selector": "#files .file",
        build: function(trigger, e) {
            return {
                callback: function(key, options) {
                    if (key == "open") {
                        trigger.click();
                    }
                    if (key == "delete") {
                        triggerDelete(trigger);
                    }
                    if (key == "new_file") {
                        triggerCreate(trigger, true);
                    }
                    if (key == "download") {
                        triggerDownload(trigger);
                    }
                    if (key == "new_folder") {
                        triggerCreate(trigger, false);
                    }
                    if (key == "rename") {
                        triggerRename(trigger);
                    }
                },
                items: {
                    "open": {name: "Open", icon: "fa-pencil"},
                    "download": {name: "Download", icon: "fa-download"},
                    "sep1": "--------",
                    "rename": {name: "Rename", icon: "fa-pencil-square-o"},
                    "delete": {name: "Delete", icon: "fa-trash-o"},
                    "sep2": "--------",
                    "new_file": {name: "New File", icon: "fa-file"},
                    "new_folder": {name: "New Folder", icon: "fa-folder"}
                }
            };
        },
        events: {
            show: function(opt) {
                this.addClass("active");
            },
            hide: function(opt) {
                this.removeClass("active");
            }
        }
    });
    $.contextMenu({
        "selector": "#files .folder",
        build: function(trigger, e) {
            return {
                callback: function(key, options) {
                    if (key == "toggle") {
                        trigger.click();
                    }
                    if (key == "delete") {
                        triggerDelete(trigger);
                    }
                    if (key == "download") {
                        triggerDownload(trigger);
                    }
                    if (key == "new_file") {
                        triggerCreate(trigger, true);
                    }
                    if (key == "new_folder") {
                        triggerCreate(trigger, false);
                    }
                    if (key == "rename") {
                        triggerRename(trigger);
                    }
                },
                items: {
                    "toggle": {name: "Toggle", icon: "fa-expand"},
                    "download": {name: "Download as ZIP", icon: "fa-download"},
                    "sep1": "--------",
                    "rename": {name: "Rename", icon: "fa-pencil-square-o"},
                    "delete": {name: "Delete", icon: "fa-trash-o"},
                    "sep2": "--------",
                    "new_file": {name: "New File", icon: "fa-file"},
                    "new_folder": {name: "New Folder", icon: "fa-folder"}
                }
            };
        },
        events: {
            show: function(opt) {
                this.addClass("active");
            },
            hide: function(opt) {
                this.removeClass("active");
            }
        }
    });
    editor.setTheme("ace/theme/chrome");
    $(document).keydown(function(e) {
        if (((e.which == 115 || e.which == 83) && e.ctrlKey) || e.which == 19) {
            var tab = $("#tabs .tab.active");
            if (tab.length) {
                if (tab.hasClass("tab-help")) {
                    Messenger().error("No file selected to save!");
                }
                else {
                    var filepath = tab.attr("data-file");
                    $.post(save_endpoint + "?name=" + encodeURIComponent(filepath), { contents: editor.session.getValue() }, function(data) {
                        if (data.error) {
                            Messenger().error(data.error);
                        }
                        else {
                            editor.session.getUndoManager().markClean();
                            checkTabClean();
                        }
                    });
                }
            }
            e.preventDefault();
            e.stopPropagation();
        }
    });
    $("#tabs").on("click", ".tab", function(e) {
        var t = $(this);
        $("#tabs .tab").removeClass("active");
        t.addClass("active");
        if (t.hasClass("tab-help")) {
            editor.setSession(help_session);
        }
        else {
            var filepath = t.attr("data-file");
            editor.setSession(tabs[filepath]);
        }
        e.preventDefault();
    });
    $("#tabs").on("click", ".tab .fa-times", function(e) {
        e.preventDefault();
        e.stopPropagation();
        var t = $(this).parent();
        delete tabs[t.attr("data-file")];
        t.remove();
        $("#tabs .tab:last").click();
    });
    $("#files").on("click", ".file", function(e) {
        e.preventDefault();
        var t = $(this);
        var filepath = get_path(t) + t.attr("data-name");
        var existing_tab = $("#tabs .tab[data-file='" + filepath.replace("'", "\\'") + "']");
        if (existing_tab.length) {
            existing_tab.click();
        }
        else {
            $.get(load_endpoint + "?name=" + encodeURIComponent(filepath), function(data) {
                if (data.error) {
                    Messenger().error(data.error);
                }
                else {
                    var session = ace.createEditSession(data.contents);
                    session.setMode(modelist.getModeForPath(t.attr("data-name")).mode);
                    editor.setSession(session);
                    $("#tabs .tab").removeClass("active");
                    var tab = $("
");
                    tab.addClass("tab active");
                    tab.text(t.attr("data-name"));
                    tab.attr("data-file", filepath);
                    tab.prepend(" ");
                    tab.append(" ");
                    tabs[filepath] = session;
                    $("#tabs").append(tab);
                }
            });
        }
    });
    $("#files").on("click", ".folder", function(e) {
        e.preventDefault();
        var t = $(this);
        if (t.hasClass("loaded")) {
            var depth = parseInt(t.attr("data-depth"));
            var contracted = t.find(".fa-fw").hasClass("fa-folder-o");
            var children = t.nextUntil("div.folder[data-depth=" + depth + "]");
            if (contracted) {
                t.find(".fa-fw").removeClass("fa-folder-o").addClass("fa-folder-open-o");
                children.show();
                children.find(".fa-fw").each(function(k, v) {
                    var folder = $(this).parent();
                    if (!folder.hasClass("folder")) {
                        return;
                    }
                    var expand = $(this).hasClass("fa-folder-open-o");
                    var children = folder.nextUntil("div.folder[data-depth=" + folder.attr("data-depth") + "]").filter(function(v) { return parseInt($(this).attr("data-depth")) > parseInt(folder.attr("data-depth")); });
                    if (!expand) {
                        children.hide();
                    }
                });
            }
            else {
                t.find(".fa-fw").removeClass("fa-folder-open-o").addClass("fa-folder-o");
                children.filter(function(v) { return parseInt($(this).attr("data-depth")) > depth; }).hide();
            }
        }
        else {
            var depth = parseInt(t.attr("data-depth"));
            t.addClass("loaded");
            $.get(path_endpoint + "?path=" + encodeURIComponent(get_path(t)), function(data) {
                if (data.error) {
                    Messenger().error(data.error);
                }
                else {
                    $.each(data.files, function(k, v) {
                        var c = (v.type == "f" ? "file" : "folder");
                        var node = $("
 " + $("
").text(v.name).html() + "
");
                        node.addClass(c);
                        node.attr("data-name", v.name);
                        node.attr("data-depth", depth + 1);
                        t.after(node);
                    });
                    t.find(".fa-fw").removeClass("fa-folder-o").addClass("fa-folder-open-o");
                }
            });
        }
    });
    function initFiles(firstRun) {
        firstRun = firstRun || false;
        $.get(path_endpoint, function(data) {
            if (data.error) {
                Messenger().error(data.error);
            }
            else {
                $.each(data.files, function(k, v) {
                    var c = (v.type == "f" ? "file" : "folder");
                    var node = $("
 " + $("
").text(v.name).html() + "
");
                    node.addClass(c);
                    node.attr("data-name", v.name);
                    node.attr("data-depth", 0);
                    if (!$("#files div[data-depth=0][data-name='" + v.name.replace("'", "\\'") + "']").length) {
                        $("#files").append(node);
                    }
                });
                if (firstRun) {
                    $("div.folder[data-name='public']").click();
                }
            }
        });
    }
    initFiles(true);

    var sep_dragging = false;
    $("#sep").mousedown(function() {
        sep_dragging = true;
    }).mouseup(function() {
        sep_dragging = false;
    }).mousemove(function(e) {
        if (sep_dragging) {
            $("#files").width(e.clientX);
            $("#editor-wrapper").css("width", "calc(100vw - 5px - " + e.clientX + "px)");
        }
    });
});
function get_path(t) {
    var depth = parseInt(t.attr("data-depth"));
    var loop_depth = depth;
    var loop_path = "";
    var loop_t = t;
    while (loop_depth >= 1) {
        loop_depth -= 1;
        var new_t = loop_t.prevAll("div.folder[data-depth=" + loop_depth + "]:first");
        loop_path = new_t.attr("data-name") + "/" + loop_path;
        loop_t = new_t;
    }
    if (t.hasClass("folder")) {
        loop_path += t.attr("data-name");
    }
    return loop_path;
}
<commit_msg>offset<commit_after>var editor;

$(document).ready(function() {
    var tabs = {};
    var modelist = ace.require("ace/ext/modelist");
    var help_session = ace.createEditSession(" ____  _               _             \n\
|  _ \\(_)_ __ ___  ___| |_ ___  _ __ \n\
| | | | | '__/ _ \\/ __| __/ _ \\| '__|\n\
| |_| | | | |  __/ (__| || (_) | |   \n\
|____/|_|_|  \\___|\\___|\\__\\___/|_|   \n\
\n\
Use the panel on the right to select a file to edit.\n\
Press Ctrl + S to save your changes.\n\
\n\
You can right click files and folders for more options.\n\
You can also drag and drop files to folders to upload them.\n\
You can drag files and folders around to move them.");

    editor = ace.edit("editor");
    editor.setSession(help_session);
    editor.setOptions({
        "fontSize": "12pt",
        "showPrintMargin": false
    });
    function checkTabClean() {
        var tab = $("#tabs .tab.active");
        if (tab.length) {
            tab.toggleClass("unsaved", !editor.session.getUndoManager().isClean());
        }
    }
    editor.on("input", function() {
        checkTabClean();
    });
    function triggerDelete(item) {
        var filepath = get_path(item);
        if (item.hasClass("file")) {
            filepath += item.attr("data-name");
        }
        if (confirm("Are you sure you want to delete:\n" + filepath)) {
            $.post(delete_endpoint + "?name=" + encodeURIComponent(filepath), function(data) {
                if (data.error) {
                    Messenger().error(data.error);
                }
                else {
                    if (item.hasClass("folder")) {
                        var depth = parseInt(item.attr("data-depth"));
                        var children = item.nextUntil("div.folder[data-depth=" + depth + "]").filter(function(v) { return parseInt($(this).attr("data-depth")) > depth; });
                        children.remove();
                    }
                    item.remove();
                }
            });
        }
    }
    function triggerCreate(item, type) {
        var filepath = get_path(item);
        var name = prompt("Enter a name for your new " + (type ? "file" : "directory") + ".");
        if (name) {
            $.post(create_endpoint, { name: name, path: filepath, type: (type ? "f" : "d") }, function(data) {
                if (data.error) {
                    Messenger().error(data.error);
                }
                else {
                    if (filepath == "") {
                        initFiles();
                    }
                    else {
                        var folder = item;
                        if (!item.hasClass("folder")) {
                            folder = item.prevAll("div.folder[data-depth=" + (parseInt(item.attr("data-depth")) - 1) + "]:first");
                        }
                        var depth = parseInt(folder.attr("data-depth"));
                        var children = folder.nextUntil("div.folder[data-depth=" + depth + "]").filter(function(v) { return parseInt($(this).attr("data-depth")) > depth; });
                        children.remove();
                        folder.removeClass("loaded");
                        folder.click();
                    }
                }
            });
        }
    }
    function triggerDownload(item) {
        var filepath = get_path(item);
        if (item.hasClass("file")) {
            filepath += item.attr("data-name");
        }
        $("#download").attr("src", download_endpoint + "?name=" + encodeURIComponent(filepath));
    }
    function triggerRename(item) {
        var filepath = get_path(item);
        if (item.hasClass("file")) {
            filepath += item.attr("data-name");
        }
        var name = prompt("Enter a new name for the file or directory:\n" + filepath);
        if (name) {
            $.post(rename_endpoint, { name: filepath, newname: name }, function(data) {
                if (data.error) {
                    Messenger().error(data.error);
                }
                else {
                    item.attr("data-name", name);
                    item.find("span").text(name);
                }
            });
        }

    }
    $("#files").on("dragover", function(e) {
        e.preventDefault();
        e.stopPropagation();
    });
    $("#files").on("drop", function(e) {
        if (e.originalEvent.dataTransfer) {
            if (e.originalEvent.dataTransfer.files.length) {
                e.preventDefault();
                e.stopPropagation();
                var files = e.originalEvent.dataTransfer.files;
                var path = "";
                if (e.target !== $("#files")[0]) {
                    path = get_path($(e.target).closest("div.folder"));
                }
                var formData = new FormData();
                formData.append("path", path);
                for (var i = 0; i < files.length; i++) {
                    formData.append("file[]", files[i], files[i].name);
                }
                $.ajax({
                    url: upload_endpoint,
                    type: "POST",
                    data: formData,
                    processData: false,
                    contentType: false,
                    success: function(data) {
                        if (data.error) {
                            Messenger().error(data.error);
                        }
                        else {
                            if (path == "") {
                                initFiles();
                            }
                            else {
                                var folder = $(e.target).closest("div.folder");
                                var depth = parseInt(folder.attr("data-depth"));
                                var children = folder.nextUntil("div.folder[data-depth=" + depth + "]").filter(function(v) { return parseInt($(this).attr("data-depth")) > depth; });
                                children.remove();
                                folder.removeClass("loaded");
                                folder.click();
                            }
                        }
                    }
                });
            }
        }
    });
    $.contextMenu({
        "selector": "#files .file",
        build: function(trigger, e) {
            return {
                callback: function(key, options) {
                    if (key == "open") {
                        trigger.click();
                    }
                    if (key == "delete") {
                        triggerDelete(trigger);
                    }
                    if (key == "new_file") {
                        triggerCreate(trigger, true);
                    }
                    if (key == "download") {
                        triggerDownload(trigger);
                    }
                    if (key == "new_folder") {
                        triggerCreate(trigger, false);
                    }
                    if (key == "rename") {
                        triggerRename(trigger);
                    }
                },
                items: {
                    "open": {name: "Open", icon: "fa-pencil"},
                    "download": {name: "Download", icon: "fa-download"},
                    "sep1": "--------",
                    "rename": {name: "Rename", icon: "fa-pencil-square-o"},
                    "delete": {name: "Delete", icon: "fa-trash-o"},
                    "sep2": "--------",
                    "new_file": {name: "New File", icon: "fa-file"},
                    "new_folder": {name: "New Folder", icon: "fa-folder"}
                }
            };
        },
        events: {
            show: function(opt) {
                this.addClass("active");
            },
            hide: function(opt) {
                this.removeClass("active");
            }
        }
    });
    $.contextMenu({
        "selector": "#files .folder",
        build: function(trigger, e) {
            return {
                callback: function(key, options) {
                    if (key == "toggle") {
                        trigger.click();
                    }
                    if (key == "delete") {
                        triggerDelete(trigger);
                    }
                    if (key == "download") {
                        triggerDownload(trigger);
                    }
                    if (key == "new_file") {
                        triggerCreate(trigger, true);
                    }
                    if (key == "new_folder") {
                        triggerCreate(trigger, false);
                    }
                    if (key == "rename") {
                        triggerRename(trigger);
                    }
                },
                items: {
                    "toggle": {name: "Toggle", icon: "fa-expand"},
                    "download": {name: "Download as ZIP", icon: "fa-download"},
                    "sep1": "--------",
                    "rename": {name: "Rename", icon: "fa-pencil-square-o"},
                    "delete": {name: "Delete", icon: "fa-trash-o"},
                    "sep2": "--------",
                    "new_file": {name: "New File", icon: "fa-file"},
                    "new_folder": {name: "New Folder", icon: "fa-folder"}
                }
            };
        },
        events: {
            show: function(opt) {
                this.addClass("active");
            },
            hide: function(opt) {
                this.removeClass("active");
            }
        }
    });
    editor.setTheme("ace/theme/chrome");
    $(document).keydown(function(e) {
        if (((e.which == 115 || e.which == 83) && e.ctrlKey) || e.which == 19) {
            var tab = $("#tabs .tab.active");
            if (tab.length) {
                if (tab.hasClass("tab-help")) {
                    Messenger().error("No file selected to save!");
                }
                else {
                    var filepath = tab.attr("data-file");
                    $.post(save_endpoint + "?name=" + encodeURIComponent(filepath), { contents: editor.session.getValue() }, function(data) {
                        if (data.error) {
                            Messenger().error(data.error);
                        }
                        else {
                            editor.session.getUndoManager().markClean();
                            checkTabClean();
                        }
                    });
                }
            }
            e.preventDefault();
            e.stopPropagation();
        }
    });
    $("#tabs").on("click", ".tab", function(e) {
        var t = $(this);
        $("#tabs .tab").removeClass("active");
        t.addClass("active");
        if (t.hasClass("tab-help")) {
            editor.setSession(help_session);
        }
        else {
            var filepath = t.attr("data-file");
            editor.setSession(tabs[filepath]);
        }
        e.preventDefault();
    });
    $("#tabs").on("click", ".tab .fa-times", function(e) {
        e.preventDefault();
        e.stopPropagation();
        var t = $(this).parent();
        delete tabs[t.attr("data-file")];
        t.remove();
        $("#tabs .tab:last").click();
    });
    $("#files").on("click", ".file", function(e) {
        e.preventDefault();
        var t = $(this);
        var filepath = get_path(t) + t.attr("data-name");
        var existing_tab = $("#tabs .tab[data-file='" + filepath.replace("'", "\\'") + "']");
        if (existing_tab.length) {
            existing_tab.click();
        }
        else {
            $.get(load_endpoint + "?name=" + encodeURIComponent(filepath), function(data) {
                if (data.error) {
                    Messenger().error(data.error);
                }
                else {
                    var session = ace.createEditSession(data.contents);
                    session.setMode(modelist.getModeForPath(t.attr("data-name")).mode);
                    editor.setSession(session);
                    $("#tabs .tab").removeClass("active");
                    var tab = $("
");
                    tab.addClass("tab active");
                    tab.text(t.attr("data-name"));
                    tab.attr("data-file", filepath);
                    tab.prepend(" ");
                    tab.append(" ");
                    tabs[filepath] = session;
                    $("#tabs").append(tab);
                }
            });
        }
    });
    $("#files").on("click", ".folder", function(e) {
        e.preventDefault();
        var t = $(this);
        if (t.hasClass("loaded")) {
            var depth = parseInt(t.attr("data-depth"));
            var contracted = t.find(".fa-fw").hasClass("fa-folder-o");
            var children = t.nextUntil("div.folder[data-depth=" + depth + "]");
            if (contracted) {
                t.find(".fa-fw").removeClass("fa-folder-o").addClass("fa-folder-open-o");
                children.show();
                children.find(".fa-fw").each(function(k, v) {
                    var folder = $(this).parent();
                    if (!folder.hasClass("folder")) {
                        return;
                    }
                    var expand = $(this).hasClass("fa-folder-open-o");
                    var children = folder.nextUntil("div.folder[data-depth=" + folder.attr("data-depth") + "]").filter(function(v) { return parseInt($(this).attr("data-depth")) > parseInt(folder.attr("data-depth")); });
                    if (!expand) {
                        children.hide();
                    }
                });
            }
            else {
                t.find(".fa-fw").removeClass("fa-folder-open-o").addClass("fa-folder-o");
                children.filter(function(v) { return parseInt($(this).attr("data-depth")) > depth; }).hide();
            }
        }
        else {
            var depth = parseInt(t.attr("data-depth"));
            t.addClass("loaded");
            $.get(path_endpoint + "?path=" + encodeURIComponent(get_path(t)), function(data) {
                if (data.error) {
                    Messenger().error(data.error);
                }
                else {
                    $.each(data.files, function(k, v) {
                        var c = (v.type == "f" ? "file" : "folder");
                        var node = $("
 " + $("
").text(v.name).html() + "
");
                        node.addClass(c);
                        node.attr("data-name", v.name);
                        node.attr("data-depth", depth + 1);
                        t.after(node);
                    });
                    t.find(".fa-fw").removeClass("fa-folder-o").addClass("fa-folder-open-o");
                }
            });
        }
    });
    function initFiles(firstRun) {
        firstRun = firstRun || false;
        $.get(path_endpoint, function(data) {
            if (data.error) {
                Messenger().error(data.error);
            }
            else {
                $.each(data.files, function(k, v) {
                    var c = (v.type == "f" ? "file" : "folder");
                    var node = $("
 " + $("
").text(v.name).html() + "
");
                    node.addClass(c);
                    node.attr("data-name", v.name);
                    node.attr("data-depth", 0);
                    if (!$("#files div[data-depth=0][data-name='" + v.name.replace("'", "\\'") + "']").length) {
                        $("#files").append(node);
                    }
                });
                if (firstRun) {
                    $("div.folder[data-name='public']").click();
                }
            }
        });
    }
    initFiles(true);

    var sep_dragging = false;
    $("#sep").mousedown(function() {
        sep_dragging = true;
    }).mouseup(function() {
        sep_dragging = false;
    }).mousemove(function(e) {
        if (sep_dragging) {
            var newPos = e.clientX + 2;
            $("#files").width(newPos);
            $("#editor-wrapper").css("width", "calc(100vw - 5px - " + newPos + "px)");
        }
    });
});
function get_path(t) {
    var depth = parseInt(t.attr("data-depth"));
    var loop_depth = depth;
    var loop_path = "";
    var loop_t = t;
    while (loop_depth >= 1) {
        loop_depth -= 1;
        var new_t = loop_t.prevAll("div.folder[data-depth=" + loop_depth + "]:first");
        loop_path = new_t.attr("data-name") + "/" + loop_path;
        loop_t = new_t;
    }
    if (t.hasClass("folder")) {
        loop_path += t.attr("data-name");
    }
    return loop_path;
}
<|endoftext|>