<commit_before>// === Philosophy

// This is a complex system. Inspired by both Ruby (packages are
// folders with lib/ inside them, and file extensions are optional) and Python
// (the ability to import the specific things that you want from a package,
// the equivalent of an __init__.py file), this system also introduces several
// new or different features and concepts:
//
//  - Packages are as absolutely simple as possible; a ‘package’ is really
//    nothing more than some `.tar.gz`ipped code. This frees the community up
//    to use any distribution, organization, and dependency resolution systems
//    they please; a `.tar.gz` on S3 is just as valid as a GitHub repository
//    or a `.zip` you e-mail to a friend. This also leaves us free, as a
//    community, to build more complex systems that build on top of this
//    system *without* precluding the use of this simple system by any
//    authors, opening up the possibility of systems like RubyForge, CPAN, or
//    Gemcutter.
//  - Packages can be organized Ruby style, with `./submodule.js` adjacent to
//    `./submodule/`… or Python style, with a twist: `submodule.js` can
//    instead be placed *inside* `./submodule/` as `./submodule/submodule.js`,
//    preforming the function of Python’s __ini__.py., but with a more
//    semantic name (submodule.js is welcome to do far more than ‘init’ the
//    submodule; you could store as much of the ‘submodule’ related code
//    inside that file as you want, and only move things to seperate files
//    inside `./submodule/` as the code grew large and semantically separate)
//  - Since this is JavaScript, there is a good chance that you may want your
//    package to be as useful client-side as it is server-side; to this
//    purpose, the `acquire()` system can be used in a fully filesystem-
//    agnostic manner: any basic acquire (not including Python-style
//    `import()` acquires) can be replaced *inline* with the content of the
//    file that acquire resolves to (possibly wrapped in `(function(){ … })()`
//    to be safe), and a package (or even an application, with all of the
//    packages it acquires) can be served to the client as a *single GZIPped
//    file*, that requires no filesystem traversal or extra HTTP requests.
//  - Compilation is fully object-agnostic (the original reason I wrote this
//    system, as the CommonJS ‘securable modules’ system existing in Node.js
//    at the time handed you an object and forced you to use it); the final
//    return value of your package/file is the return value of the acquire
//    functions. This means that creating and manipulating the object
//    representing your package to the rest of the JavaScript world is fully
//    and completely up to you; you’re free to return an Object, a prototype
//    (as per poopy.js), a Function… even an array or number or other
//    primative (if you really want to). It’s completely up to you.

// === Usage

// The basic acquires will all asynchronously resolve and compile a JavaScript
// or object file, and pass the result to a callback. These are Promises, so
// you can synchronously wait() on them.
//
// Since the result of compilation is passed directly, you can expect to get
// an object representing the entire package/module.
//
// `acquire()` is identical to `acquire.package()`.
//
//     var foo = acquire.absolute('/path/to/foo.js').wait()
//     var foo = acquire.relative('to/foo.js').wait()
//     var foo = acquire.package('to/foo') or acquire('to/foo')

// However, you can also import the module directly into a target.
//
// This will take the value of the slot 'foo' on the compiled package, and
// apply it to the slot 'foo' on the target.
//
// Multiple slots may be specified, in which case all specified slots will be
// applied; if no slots are specified, then *all* slots on the target package
// will be applied (thus, fully importing the target module). If no target is
// specified, `GLOBAL` will be substituted.
//
// `acquire.import().from()` is identical to `acquire.import().from.package()`;
// generally speaking, the methods on the `from` proxy object correspond to
// their relative basic `acquire` methods.
//
//     acquire.import('aSlot', 'anotherSlot', this).from.relative('to/foo.js')
//     acquire.import(this).from.absolute('/path/to/foo.js')
//     acquire.import('aSlot').from.package('to/foo')
//       or acquire.import('aSlot').from('to/foo')

// === Package structure

// This acquire system is very flexible, but that is also its downfall. It is
// important that we use that flexibility for good, not evil.
//
// As a package author, this means several things to you: generally, you need
// to put theflexibility in the hands of the user of your package, instead of
// abusing it yourself. For instance:
//
//  - Write the submodules of your package such that they are independant of
//    eachother as much as possible. This ensures that if a user of your
//    package only wants to `acquire('yourPackage/subModule')`, they are free
//    to do so, as nothing in `lib/yourPackage/submodule/` will depend upon
//    code in `lib/yourPackage/`.
//
//    This acquire system attempts to generally facilitate this through the
//    submodule named initfile; in fact, there are three different ways that
//    submodules of your package may be utilized separately from your package
//    itself:
//     - `acquire.import('subModule', this).from('myPackage');`
//     - `var subModule = acquire('myPackage/subModule');`
//     - Tearing the `lib/myPackage/submodule/` folder out of your package,
//       and using (or even distributing, depending on your choice of license)
//       it as its own package
//  - Modularize your code iteratively; when the code relevant to the
//    `subModule` section of your package is light, there is no reason not to
//    keep it in `lib/myPackage.js`. However, as your package grows, you may
//    find that you wish to keep it in a separate file; this is a simple
//    matter of moving `lib/myPackage.js` to `lib/myPackage/myPackage.js` (if
//    you wish; I personally prefer to keep all related code inside a single
//    folder, but leaving `myPackage.js` directly inside `lib/` is perfectly
//    valid) and then moving some of the `subModule`-related code *out* of
//    `myPackage.js` into `lib/myPackage/subModule.js`.
//
//    This applies in the same way to the submodules themselves; you may
//    eventually wish to move `lib/myPackage/subModule.js` into
//    `lib/myPackage/subModule/subModule.js`, and then some of the subModule
//    code into `lib/myPackage/subModule/subSubModule.js`. And so on, and so
//    forth.
//  - Your sourcefiles themselves should generally be wrapped in an anonymous
//    function to give them private anonymous scope; although *this* acquire
//    function will wrap them in such an anonymous function before compiling
//    them, others using your code in other ways may not.
//
//    As well, although this system leaves the details entirely up to you,
//    you’ll almost certainly want to create and return an object that
//    represents your package to the outside world. It is good practice to
//    utilize a local variable of the same name as your package to hold this
//    object, as that means your slot definitions (and internal dogfooding of
//    your own package’s methods) will have a good chance of directly relating
//    to your documentation, which is beneficial to people trying to learn
//    from your source code.
//
// All that said, here’s a basic template for a module/submodule within your
// package (this example uses the poopy.js method of object creation; but
// a literal like `{}`, or a constructor invocation such as `new(Object)`, is
// just as valid):
//
//     /* lib/myPackage/myPackage.js */
//     (function(){
//       var myPackage = {}.beget();
//       myPackage['subModule'] = acquire.relative('subModule.js');
//
//       // …
//
//       return myPackage;
//     })();
//
//     /* lib/myPackage/subModule.js */
//     (function(){
//       var subModule = {}.beget();
//
//       // …
//
//       return subModule;
//     })();
(function () {

  // ============
  // = Plumbing =
  // ============

  // =============
  // = Porcelain =
  // =============

  // === Path resolvers
  var resolvePackage = function () {};
  var resolveRelative = function () {};
  var resolveAbsolute = function () {};

  // === Basic acquires
  var acquirePackage = function () {};
  var acquireRelative = function () {};
  var acquireAbsolute = function () {};

  // === API

  // We have to define our `acquire` object (the namespace for the entire API)
  // all the way down here, because it has to be the same function called by
  // `acquire.package()`.
  var acquire = acquirePackage;
  acquire['package'] = acquirePackage;
  acquire['relative'] = acquireRelative;
  acquire['absolute'] = acquireAbsolute;

  acquire['resolve'] = resolvePackage;
  resolve.resolve['package'] = resolvePackage;
  resolve.resolve['relative'] = resolveRelative;
  resolve.resolve['absolute'] = resolveAbsolute;

  // For the moment, this file has to be dual-compatible with both `acquire()`
  // *and* the old ‘securable modules’ `require()` system (as that is the only
  // easy method for people wishing to get `acquire()` into their code to do
  // so). For that reason, we’re going to ensure `module.exports` exists, and
  // then attach our `acquire` namespace to that.
  //
  // In ‘the future’ (/dun dun dun+/), I’m hoping that this code will be
  // merged into Node.js and used to replace the ‘securable modules’
  // system as the primary code-acquisition system, and `acquire` will simply
  // be available. Until then, to make upgrading (if that happens) easier, I
  // suggest something like `process.mixin(require('poopy.js/acquire'));`, or,
  // if you don’t want to screw with the `GLOBAL` namespace,
  // `var acquire = require('poopy.js/acquire')['acquire'];`. It’s hacky, I
  // know, but necessary, if I want the `acquire` namespace object itself to
  // be a function… and for ease of use, I do.
  if (typeof module == 'undefined') {
    var module = {} && module['exports'] = {}}

  module.exports['acquire'] = acquire;
  return module.exports;
})();
<commit_msg>Added a note about the `Promise`s that I’ll be using heavily<commit_after>// === Philosophy

// This is a complex system. Inspired by both Ruby (packages are
// folders with lib/ inside them, and file extensions are optional) and Python
// (the ability to import the specific things that you want from a package,
// the equivalent of an __init__.py file), this system also introduces several
// new or different features and concepts:
//
//  - Packages are as absolutely simple as possible; a ‘package’ is really
//    nothing more than some `.tar.gz`ipped code. This frees the community up
//    to use any distribution, organization, and dependency resolution systems
//    they please; a `.tar.gz` on S3 is just as valid as a GitHub repository
//    or a `.zip` you e-mail to a friend. This also leaves us free, as a
//    community, to build more complex systems that build on top of this
//    system *without* precluding the use of this simple system by any
//    authors, opening up the possibility of systems like RubyForge, CPAN, or
//    Gemcutter.
//  - Packages can be organized Ruby style, with `./submodule.js` adjacent to
//    `./submodule/`… or Python style, with a twist: `submodule.js` can
//    instead be placed *inside* `./submodule/` as `./submodule/submodule.js`,
//    preforming the function of Python’s __ini__.py., but with a more
//    semantic name (submodule.js is welcome to do far more than ‘init’ the
//    submodule; you could store as much of the ‘submodule’ related code
//    inside that file as you want, and only move things to seperate files
//    inside `./submodule/` as the code grew large and semantically separate)
//  - Since this is JavaScript, there is a good chance that you may want your
//    package to be as useful client-side as it is server-side; to this
//    purpose, the `acquire()` system can be used in a fully filesystem-
//    agnostic manner: any basic acquire (not including Python-style
//    `import()` acquires) can be replaced *inline* with the content of the
//    file that acquire resolves to (possibly wrapped in `(function(){ … })()`
//    to be safe), and a package (or even an application, with all of the
//    packages it acquires) can be served to the client as a *single GZIPped
//    file*, that requires no filesystem traversal or extra HTTP requests.
//  - Compilation is fully object-agnostic (the original reason I wrote this
//    system, as the CommonJS ‘securable modules’ system existing in Node.js
//    at the time handed you an object and forced you to use it); the final
//    return value of your package/file is the return value of the acquire
//    functions. This means that creating and manipulating the object
//    representing your package to the rest of the JavaScript world is fully
//    and completely up to you; you’re free to return an Object, a prototype
//    (as per poopy.js), a Function… even an array or number or other
//    primative (if you really want to). It’s completely up to you.

// === Usage

// The basic acquires will all asynchronously resolve and compile a JavaScript
// or object file, and pass the result to a callback. These are Promises, so
// you can synchronously wait() on them.
//
// Since the result of compilation is passed directly, you can expect to get
// an object representing the entire package/module.
//
// `acquire()` is identical to `acquire.package()`.
//
//     var foo = acquire.absolute('/path/to/foo.js').wait()
//     var foo = acquire.relative('to/foo.js').wait()
//     var foo = acquire.package('to/foo') or acquire('to/foo')

// However, you can also import the module directly into a target.
//
// This will take the value of the slot 'foo' on the compiled package, and
// apply it to the slot 'foo' on the target.
//
// Multiple slots may be specified, in which case all specified slots will be
// applied; if no slots are specified, then *all* slots on the target package
// will be applied (thus, fully importing the target module). If no target is
// specified, `GLOBAL` will be substituted.
//
// `acquire.import().from()` is identical to `acquire.import().from.package()`;
// generally speaking, the methods on the `from` proxy object correspond to
// their relative basic `acquire` methods.
//
//     acquire.import('aSlot', 'anotherSlot', this).from.relative('to/foo.js')
//     acquire.import(this).from.absolute('/path/to/foo.js')
//     acquire.import('aSlot').from.package('to/foo')
//       or acquire.import('aSlot').from('to/foo')

// === Package structure

// This acquire system is very flexible, but that is also its downfall. It is
// important that we use that flexibility for good, not evil.
//
// As a package author, this means several things to you: generally, you need
// to put theflexibility in the hands of the user of your package, instead of
// abusing it yourself. For instance:
//
//  - Write the submodules of your package such that they are independant of
//    eachother as much as possible. This ensures that if a user of your
//    package only wants to `acquire('yourPackage/subModule')`, they are free
//    to do so, as nothing in `lib/yourPackage/submodule/` will depend upon
//    code in `lib/yourPackage/`.
//
//    This acquire system attempts to generally facilitate this through the
//    submodule named initfile; in fact, there are three different ways that
//    submodules of your package may be utilized separately from your package
//    itself:
//     - `acquire.import('subModule', this).from('myPackage');`
//     - `var subModule = acquire('myPackage/subModule');`
//     - Tearing the `lib/myPackage/submodule/` folder out of your package,
//       and using (or even distributing, depending on your choice of license)
//       it as its own package
//  - Modularize your code iteratively; when the code relevant to the
//    `subModule` section of your package is light, there is no reason not to
//    keep it in `lib/myPackage.js`. However, as your package grows, you may
//    find that you wish to keep it in a separate file; this is a simple
//    matter of moving `lib/myPackage.js` to `lib/myPackage/myPackage.js` (if
//    you wish; I personally prefer to keep all related code inside a single
//    folder, but leaving `myPackage.js` directly inside `lib/` is perfectly
//    valid) and then moving some of the `subModule`-related code *out* of
//    `myPackage.js` into `lib/myPackage/subModule.js`.
//
//    This applies in the same way to the submodules themselves; you may
//    eventually wish to move `lib/myPackage/subModule.js` into
//    `lib/myPackage/subModule/subModule.js`, and then some of the subModule
//    code into `lib/myPackage/subModule/subSubModule.js`. And so on, and so
//    forth.
//  - Your sourcefiles themselves should generally be wrapped in an anonymous
//    function to give them private anonymous scope; although *this* acquire
//    function will wrap them in such an anonymous function before compiling
//    them, others using your code in other ways may not.
//
//    As well, although this system leaves the details entirely up to you,
//    you’ll almost certainly want to create and return an object that
//    represents your package to the outside world. It is good practice to
//    utilize a local variable of the same name as your package to hold this
//    object, as that means your slot definitions (and internal dogfooding of
//    your own package’s methods) will have a good chance of directly relating
//    to your documentation, which is beneficial to people trying to learn
//    from your source code.
//
// All that said, here’s a basic template for a module/submodule within your
// package (this example uses the poopy.js method of object creation; but
// a literal like `{}`, or a constructor invocation such as `new(Object)`, is
// just as valid):
//
//     /* lib/myPackage/myPackage.js */
//     (function(){
//       var myPackage = {}.beget();
//       myPackage['subModule'] = acquire.relative('subModule.js');
//
//       // …
//
//       return myPackage;
//     })();
//
//     /* lib/myPackage/subModule.js */
//     (function(){
//       var subModule = {}.beget();
//
//       // …
//
//       return subModule;
//     })();
(function () {

  // TODO: The `Promise`s utilized throughout this code *really* should be raw
  //       `EventEmitter`s, but I absolutely *have* to support `wait()`
  //       functionality on all acquires.
  //
  //       The truth is that `Promise` really shouldn’t exist; everything
  //       implemented on `Promise` could be re-implemented on top of
  //       `EventEmitter` directly. However, that will have to be hashed out
  //       with @ryah.

  // ============
  // = Plumbing =
  // ============

  // =============
  // = Porcelain =
  // =============

  // === Path resolvers
  var resolvePackage = function () {};
  var resolveRelative = function () {};
  var resolveAbsolute = function () {};

  // === Basic acquires
  var acquirePackage = function () {};
  var acquireRelative = function () {};
  var acquireAbsolute = function () {};

  // === API

  // We have to define our `acquire` object (the namespace for the entire API)
  // all the way down here, because it has to be the same function called by
  // `acquire.package()`.
  var acquire = acquirePackage;
  acquire['package'] = acquirePackage;
  acquire['relative'] = acquireRelative;
  acquire['absolute'] = acquireAbsolute;

  acquire['resolve'] = resolvePackage;
  resolve.resolve['package'] = resolvePackage;
  resolve.resolve['relative'] = resolveRelative;
  resolve.resolve['absolute'] = resolveAbsolute;

  // For the moment, this file has to be dual-compatible with both `acquire()`
  // *and* the old ‘securable modules’ `require()` system (as that is the only
  // easy method for people wishing to get `acquire()` into their code to do
  // so). For that reason, we’re going to ensure `module.exports` exists, and
  // then attach our `acquire` namespace to that.
  //
  // In ‘the future’ (/dun dun dun+/), I’m hoping that this code will be
  // merged into Node.js and used to replace the ‘securable modules’
  // system as the primary code-acquisition system, and `acquire` will simply
  // be available. Until then, to make upgrading (if that happens) easier, I
  // suggest something like `process.mixin(require('poopy.js/acquire'));`, or,
  // if you don’t want to screw with the `GLOBAL` namespace,
  // `var acquire = require('poopy.js/acquire')['acquire'];`. It’s hacky, I
  // know, but necessary, if I want the `acquire` namespace object itself to
  // be a function… and for ease of use, I do.
  if (typeof module == 'undefined') {
    var module = {} && module['exports'] = {}}

  module.exports['acquire'] = acquire;
  return module.exports;
})();
<|endoftext|>


<commit_before>'use strict';
nwmApplication.service('CanvasService', ['$routeParams', '$window', 'SocketIoService', 'UserService', 'Utils', 'FabricService', 'ListenerService', function ($routeParams, $window, SocketIoService, UserService, Utils, FabricService, ListenerService) {
    var self = this;
    var canvasToolOptions = [
        {name: 'None', glyphiconicon: 'glyphicon-off', active: false, fn: canvasToolNone},
        {name: 'Write', glyphiconicon: 'glyphicon-font', active: false, fn: canvasToolWrite},
        {name: 'Draw', glyphiconicon: 'glyphicon-pencil', active: false, fn: canvasToolDraw},
        {name: 'Rectangle', glyphiconicon: 'glyphicon-unchecked', active: false, fn: canvasToolRect},
        {name: 'Arrow', glyphiconicon: 'glyphicon-arrow-right', active: false, fn: canvasToolArrow},
        {name: 'Duplicate', glyphiconicon: 'glyphicon-duplicate', active: false, fn: duplicateObject}
    ];

    this.start = function () {
        SocketIoService.send().joinRoom(UserService.user());
        FabricService.createCanvas();
        FabricService.addObjectIdToPrototype();
        ListenerService.bindListeners();

        //FabricService.selectionCreated(selectionCreated);
        function selectionCreated(options) {
            var fabricObject = options.target;
            fabricObject.objectId = Utils.guid();
            var fabricObjectJson = JSON.stringify(fabricObject);
            //todo: when selecting multiple, position might be incorrect in clients
            //todo: sometimes duplication when moving
            SocketIoService.send().addObject(fabricObjectJson);
            ListenerService.attachListenersToFabricObject(fabricObject);
        }
    };

    function duplicateObject(){
        var fabricObject = FabricService.canvas().getActiveObject();
        if(fabricObject){
            var clonedFabricObject = fabricObject.clone();
            correctPosition(clonedFabricObject, fabricObject);
            clonedFabricObject.objectId = Utils.guid();
            ListenerService.removeAllListeners(clonedFabricObject);
            ListenerService.attachListenersToFabricObject(clonedFabricObject);
            FabricService.canvas().add(clonedFabricObject).renderAll();
            sendOverWire(clonedFabricObject);
        }

        function correctPosition(clonedFabricObject, fabricObject) {
            var random = Math.floor((Math.random() * 50) + 1);
            clonedFabricObject.set("top", fabricObject.top + random);
            clonedFabricObject.set("left", fabricObject.left + random);
        }

        function sendOverWire(clonedFabricObject) {
            var json = JSON.stringify(clonedFabricObject);
            SocketIoService.send().addObject(json);
        }
        self.setActiveCanvasTool('None');

    };
    this.getCanvasAsBase64 = function(imagetype){
        return FabricService.canvas().toDataURL(imagetype);
    };
    this.canvasTools = function () {
        return canvasToolOptions;
    };
    this.saveCanvas = function(){
        SocketIoService.send().saveCanvas(
            {
                randomString: UserService.user().randomString,
                room: UserService.user().room,
                canvas: JSON.stringify(FabricService.canvas())
            });
    };
    function canvasToolNone() {
        FabricService.canvas().isDrawingMode = false;
        FabricService.removeMouseDown();
        FabricService.removePathCreated();
    }

    function canvasToolWrite() {
        FabricService.mouseDown(iTextMouseDown);
        function iTextMouseDown(options) {
            if (!options.target) {
                var iText = FabricService.createItext(options);
                createAndSyncFrom(iText);
                ListenerService.attachListenersToFabricObject(iText);
                iText.enterEditing();
                iText.selectionStart = 0;
                iText.selectionEnd = iText.text.length;
                self.setActiveCanvasTool('None');
                FabricService.canvas().renderAll();
            }
        }
    }

    function canvasToolRect() {
        FabricService.mouseDown(rectMouseDown);
        function rectMouseDown(options) {
            if (!options.target) {
                var rect = FabricService.createRect(options);
                createAndSyncFrom(rect);
                ListenerService.attachListenersToFabricObject(rect);
                self.setActiveCanvasTool('None');
                FabricService.canvas().renderAll();
            }
        }
    }

    function canvasToolDraw() {
        FabricService.canvas().isDrawingMode = true;
        FabricService.pathCreated(drawPath);
        function drawPath(options) {
            var fabricObject = options.path;
            fabricObject.objectId = Utils.guid();
            var fabricObjectJson = JSON.stringify(fabricObject);
            SocketIoService.send().addObject(fabricObjectJson);
            ListenerService.attachListenersToFabricObject(fabricObject)
        }
    }

    function canvasToolArrow() {
        FabricService.mouseDown(createArrow);

        function createArrow(options) {
            if (!options.target) {
                var arrow = FabricService.createArrow(options);
                createAndSyncFrom(arrow);
                ListenerService.attachListenersToFabricObject(arrow);
                self.setActiveCanvasTool('None');
                FabricService.canvas().renderAll();
            }
        }
    }

    function createAndSyncFrom(fabricObject) {
        var json = JSON.stringify(fabricObject);
        SocketIoService.send().addObject(json);
        FabricService.canvas().calcOffset();
        FabricService.canvas().add(fabricObject);
        FabricService.canvas().setActiveObject(fabricObject);
    }

    this.findActiveCanvasTool = function () {
        return canvasToolOptions.filter(function (tool) {
            if (tool.active) {
                return true;
            }
            return false;
        })[0];
    }
    this.findCanvasTool = function (name) {
        return canvasToolOptions.filter(function (tool) {
            if (tool.name === name) {
                return true;
            }
            return false;
        })[0];
    }
    this.setActiveCanvasTool = function (name) {
        var activeCanvasTool = this.findActiveCanvasTool();
        if (activeCanvasTool) {
            if (!name || activeCanvasTool.name === name) {
                return;
            }
            activeCanvasTool.active = false;
        }
        var tool = this.findCanvasTool(name);
        tool.active = true;
        tool.fn();
    }
    this.isDrawingMode = function (value) {
        FabricService.canvas().isDrawingMode = value;
    }
    this.calculateOffset = function () {
        FabricService.canvas().calcOffset();
    }

}])<commit_msg>use fabric.util.clone<commit_after>'use strict';
nwmApplication.service('CanvasService', ['$routeParams', '$window', 'SocketIoService', 'UserService', 'Utils', 'FabricService', 'ListenerService', function ($routeParams, $window, SocketIoService, UserService, Utils, FabricService, ListenerService) {
    var self = this;
    var canvasToolOptions = [
        {name: 'None', glyphiconicon: 'glyphicon-off', active: false, fn: canvasToolNone},
        {name: 'Write', glyphiconicon: 'glyphicon-font', active: false, fn: canvasToolWrite},
        {name: 'Draw', glyphiconicon: 'glyphicon-pencil', active: false, fn: canvasToolDraw},
        {name: 'Rectangle', glyphiconicon: 'glyphicon-unchecked', active: false, fn: canvasToolRect},
        {name: 'Arrow', glyphiconicon: 'glyphicon-arrow-right', active: false, fn: canvasToolArrow},
        {name: 'Duplicate', glyphiconicon: 'glyphicon-duplicate', active: false, fn: duplicateObject}
    ];

    this.start = function () {
        SocketIoService.send().joinRoom(UserService.user());
        FabricService.createCanvas();
        FabricService.addObjectIdToPrototype();
        ListenerService.bindListeners();

        //FabricService.selectionCreated(selectionCreated);
        function selectionCreated(options) {
            var fabricObject = options.target;
            fabricObject.objectId = Utils.guid();
            var fabricObjectJson = JSON.stringify(fabricObject);
            //todo: when selecting multiple, position might be incorrect in clients
            //todo: sometimes duplication when moving
            SocketIoService.send().addObject(fabricObjectJson);
            ListenerService.attachListenersToFabricObject(fabricObject);
        }
    };

    function duplicateObject(){
        var fabricObject = FabricService.canvas().getActiveObject();
        if(fabricObject){
            var clonedFabricObject = fabric.util.object.clone(fabricObject);
            correctPosition(clonedFabricObject, fabricObject);
            clonedFabricObject.objectId = Utils.guid();
            ListenerService.removeAllListeners(clonedFabricObject);
            ListenerService.attachListenersToFabricObject(clonedFabricObject);
            FabricService.canvas().add(clonedFabricObject).renderAll();
            sendOverWire(clonedFabricObject);
        }

        function correctPosition(clonedFabricObject, fabricObject) {
            var random = Math.floor((Math.random() * 50) + 1);
            clonedFabricObject.set("top", fabricObject.top + random);
            clonedFabricObject.set("left", fabricObject.left + random);
        }

        function sendOverWire(clonedFabricObject) {
            var json = JSON.stringify(clonedFabricObject);
            SocketIoService.send().addObject(json);
        }
        self.setActiveCanvasTool('None');

    };
    this.getCanvasAsBase64 = function(imagetype){
        return FabricService.canvas().toDataURL(imagetype);
    };
    this.canvasTools = function () {
        return canvasToolOptions;
    };
    this.saveCanvas = function(){
        SocketIoService.send().saveCanvas(
            {
                randomString: UserService.user().randomString,
                room: UserService.user().room,
                canvas: JSON.stringify(FabricService.canvas())
            });
    };
    function canvasToolNone() {
        FabricService.canvas().isDrawingMode = false;
        FabricService.removeMouseDown();
        FabricService.removePathCreated();
    }

    function canvasToolWrite() {
        FabricService.mouseDown(iTextMouseDown);
        function iTextMouseDown(options) {
            if (!options.target) {
                var iText = FabricService.createItext(options);
                createAndSyncFrom(iText);
                ListenerService.attachListenersToFabricObject(iText);
                iText.enterEditing();
                iText.selectionStart = 0;
                iText.selectionEnd = iText.text.length;
                self.setActiveCanvasTool('None');
                FabricService.canvas().renderAll();
            }
        }
    }

    function canvasToolRect() {
        FabricService.mouseDown(rectMouseDown);
        function rectMouseDown(options) {
            if (!options.target) {
                var rect = FabricService.createRect(options);
                createAndSyncFrom(rect);
                ListenerService.attachListenersToFabricObject(rect);
                self.setActiveCanvasTool('None');
                FabricService.canvas().renderAll();
            }
        }
    }

    function canvasToolDraw() {
        FabricService.canvas().isDrawingMode = true;
        FabricService.pathCreated(drawPath);
        function drawPath(options) {
            var fabricObject = options.path;
            fabricObject.objectId = Utils.guid();
            var fabricObjectJson = JSON.stringify(fabricObject);
            SocketIoService.send().addObject(fabricObjectJson);
            ListenerService.attachListenersToFabricObject(fabricObject)
        }
    }

    function canvasToolArrow() {
        FabricService.mouseDown(createArrow);

        function createArrow(options) {
            if (!options.target) {
                var arrow = FabricService.createArrow(options);
                createAndSyncFrom(arrow);
                ListenerService.attachListenersToFabricObject(arrow);
                self.setActiveCanvasTool('None');
                FabricService.canvas().renderAll();
            }
        }
    }

    function createAndSyncFrom(fabricObject) {
        var json = JSON.stringify(fabricObject);
        SocketIoService.send().addObject(json);
        FabricService.canvas().calcOffset();
        FabricService.canvas().add(fabricObject);
        FabricService.canvas().setActiveObject(fabricObject);
    }

    this.findActiveCanvasTool = function () {
        return canvasToolOptions.filter(function (tool) {
            if (tool.active) {
                return true;
            }
            return false;
        })[0];
    }
    this.findCanvasTool = function (name) {
        return canvasToolOptions.filter(function (tool) {
            if (tool.name === name) {
                return true;
            }
            return false;
        })[0];
    }
    this.setActiveCanvasTool = function (name) {
        var activeCanvasTool = this.findActiveCanvasTool();
        if (activeCanvasTool) {
            if (!name || activeCanvasTool.name === name) {
                return;
            }
            activeCanvasTool.active = false;
        }
        var tool = this.findCanvasTool(name);
        tool.active = true;
        tool.fn();
    }
    this.isDrawingMode = function (value) {
        FabricService.canvas().isDrawingMode = value;
    }
    this.calculateOffset = function () {
        FabricService.canvas().calcOffset();
    }

}])<|endoftext|>


<commit_before>(function(){
"use strict";

/*** setup ***/
var version = "3.4.0", _gaq = window._gaq = _gaq || [];

_gaq.push(
	["_setAccount", "##GA##"],
	["_setSessionCookieTimeout", 0],
	["_setCustomVar", 1, "Version", version, 2],
	["_trackPageview", "/"]
);

localStorage.sessions = localStorage.sessions || '{}';
localStorage.open = localStorage.open || '{"add":"click", "replace":"shift+click", "new":"ctrl/cmd+click", "incognito":"alt+click"}';
localStorage.pinned = localStorage.pinned || "skip";

if (localStorage.version === version) {
	if ("temp" in localStorage) {
		JSON.parse(localStorage.temp).forEach(function(v){
			chrome.tabs.create({ url: v });
		});

		delete localStorage.temp;

		_gaq.push(["_trackEvent", "Temp", "Restore"]);
	}
} else {
	localStorage.readchanges = false;
	localStorage.version = version;
}

var browser = navigator.userAgent.match(/Chrome\/(\d\d?\.\d\d?)/);
if (browser && browser[1] && browser[1] < 17) {
	if (isNaN(localStorage.outdated) || Date.now() - localStorage.outdated > 1000 * 60 * 60 * 24 * 10) {
		localStorage.outdated = "true";
	}
} else {
	delete localStorage.outdated;
}

/*** omnibox ***/
chrome.omnibox.onInputChanged.addListener(function(text, suggest){
	var sessions = JSON.parse(localStorage.sessions), text = text.trim(), ltext = text.toLowerCase(), suggestions = [];

	if (text.length) {
		chrome.omnibox.setDefaultSuggestion({
			description: "Open <match>" + text + "</match>" + (sessions[text] ? "" : " ...") + " in this window"
		});

		Object.keys(sessions).forEach(function(name){
			var index = name.toLowerCase().indexOf(ltext);

			index !== -1 && suggestions.push({
				content: name,
				description: name.substring(0, index) + "<match>" + name.substr(index, text.length) + "</match>" + name.substr(index + text.length),
				index: index
			});
		});

		suggest(suggestions.sort(function(a, b){
			return a.index === b.index ? (a.content.length === b.content.length ? 0 : a.content.length - b.content.length) : a.index - b.index;
		}));
	} else {
		chrome.omnibox.setDefaultSuggestion({ description: "Open a session in this window" });
	}
});

chrome.omnibox.onInputEntered.addListener(function(name){
	var sessions = JSON.parse(localStorage.sessions);

	if (sessions[name]) {
		openSession(undefined, sessions[name]);

		_gaq.push(["_trackEvent", "Session", "Omnibox"]);
	}
});

chrome.omnibox.setDefaultSuggestion({ description: "Open a session in this window" });

/*** open ***/
window.openSession = function(cwinId, urls, e, isTemp){
	var open = JSON.parse(localStorage.open),
		action = e ? (((e.ctrlKey || e.metaKey) && "ctrl/cmd+click") || (e.shiftKey && "shift+click") || (e.altKey && "alt+click") || "click") : open.add;

	for (var k in open) {
		if (action === open[k]) {
			action = k;
			break;
		}
	}

	if (action === "add") {
		urls.forEach(function(v){
			chrome.tabs.create({ windowId: cwinId, url: v });
		});
	} else if (action === "replace") {
		chrome.tabs.getAllInWindow(cwinId, function(tabs){
			openSession(cwinId, urls);

			if (localStorage.noreplacingpinned) {
				tabs = tabs.filter(function(t){ return !t.pinned; });
			}

			tabs.forEach(function(tab){
				chrome.tabs.remove(tab.id);
			});
		});
	} else if (action === "new" || action === "incognito") {
		chrome.windows.create({ url: urls.shift(), incognito: action === "incognito" }, function(win){
			openSession(win.id, urls);
		});
	}

	e && _gaq.push(["_trackEvent", isTemp ? "Temp" : "Session", "Open", action]);
};

})();<commit_msg>Ahahahaaha :)<commit_after>var _gaq = _gaq || [];

(function(){
"use strict";

/*** setup ***/
var version = "3.4.0";

localStorage.sessions = localStorage.sessions || '{}';
localStorage.open = localStorage.open || '{"add":"click", "replace":"shift+click", "new":"ctrl/cmd+click", "incognito":"alt+click"}';
localStorage.pinned = localStorage.pinned || "skip";

_gaq.push(
	["_setAccount", "##GA##"],
	["_setSessionCookieTimeout", 0],
	["_setCustomVar", 1, "Version", version, 2],
	["_trackPageview", "/"]
);

if (localStorage.version === version) {
	if ("temp" in localStorage) {
		JSON.parse(localStorage.temp).forEach(function(v){
			chrome.tabs.create({ url: v });
		});

		delete localStorage.temp;

		_gaq.push(["_trackEvent", "Temp", "Restore"]);
	}
} else {
	localStorage.readchanges = false;
	localStorage.version = version;
}

var browser = navigator.userAgent.match(/Chrome\/(\d\d?\.\d\d?)/);
if (browser && browser[1] && browser[1] < 17) {
	if (isNaN(localStorage.outdated) || Date.now() - localStorage.outdated > 1000 * 60 * 60 * 24 * 10) {
		localStorage.outdated = "true";
	}
} else {
	delete localStorage.outdated;
}

/*** omnibox ***/
chrome.omnibox.onInputChanged.addListener(function(text, suggest){
	var sessions = JSON.parse(localStorage.sessions), text = text.trim(), ltext = text.toLowerCase(), suggestions = [];

	if (text.length) {
		chrome.omnibox.setDefaultSuggestion({
			description: "Open <match>" + text + "</match>" + (sessions[text] ? "" : " ...") + " in this window"
		});

		Object.keys(sessions).forEach(function(name){
			var index = name.toLowerCase().indexOf(ltext);

			index !== -1 && suggestions.push({
				content: name,
				description: name.substring(0, index) + "<match>" + name.substr(index, text.length) + "</match>" + name.substr(index + text.length),
				index: index
			});
		});

		suggest(suggestions.sort(function(a, b){
			return a.index === b.index ? (a.content.length === b.content.length ? 0 : a.content.length - b.content.length) : a.index - b.index;
		}));
	} else {
		chrome.omnibox.setDefaultSuggestion({ description: "Open a session in this window" });
	}
});

chrome.omnibox.onInputEntered.addListener(function(name){
	var sessions = JSON.parse(localStorage.sessions);

	if (sessions[name]) {
		openSession(undefined, sessions[name]);

		_gaq.push(["_trackEvent", "Session", "Omnibox"]);
	}
});

chrome.omnibox.setDefaultSuggestion({ description: "Open a session in this window" });

/*** open ***/
window.openSession = function(cwinId, urls, e, isTemp){
	var open = JSON.parse(localStorage.open),
		action = e ? (((e.ctrlKey || e.metaKey) && "ctrl/cmd+click") || (e.shiftKey && "shift+click") || (e.altKey && "alt+click") || "click") : open.add;

	for (var k in open) {
		if (action === open[k]) {
			action = k;
			break;
		}
	}

	if (action === "add") {
		urls.forEach(function(v){
			chrome.tabs.create({ windowId: cwinId, url: v });
		});
	} else if (action === "replace") {
		chrome.tabs.getAllInWindow(cwinId, function(tabs){
			openSession(cwinId, urls);

			if (localStorage.noreplacingpinned) {
				tabs = tabs.filter(function(t){ return !t.pinned; });
			}

			tabs.forEach(function(tab){
				chrome.tabs.remove(tab.id);
			});
		});
	} else if (action === "new" || action === "incognito") {
		chrome.windows.create({ url: urls.shift(), incognito: action === "incognito" }, function(win){
			openSession(win.id, urls);
		});
	}

	e && _gaq.push(["_trackEvent", isTemp ? "Temp" : "Session", "Open", action]);
};

})();<|endoftext|>


<commit_before>(function(factory) {

  if (typeof define === 'function') {
    define(factory);
  }
  else if (typeof exports !== 'undefined') {
    factory(require, exports);
  }
  else {
    factory(null, (this['test'] = {}));
  }

})(function(require, exports) {

  function sendMessage(fn, msg, type) {
    var p = this;
    if (this != this.parent) {
      p = this.parent;
    }

    if (p && p[fn]) {
      p[fn](msg, type);
    }
    else if (msg && typeof console !== 'undefined') {
      console.log(color(msg, type));
    }
  }

  // https://github.com/loopj/commonjs-ansi-color/blob/master/lib/ansi-color.js
  var ANSI_CODES = {
    'fail': 31, // red
    'pass': 32, // green
    'info': 36 // cyan
  };

  function color(str, type) {
    return '\033[' + ANSI_CODES[type] + 'm  ' + str + '\033[0m';
  }

  exports.print = function(txt, style) {
    sendMessage('printResults', txt, style || 'info');
  };

  exports.assert = function (guard, message) {
    if (typeof message === 'undefined') {
      message = '';
    }

    if (guard) {
      exports.print('[PASS] ' + message, 'pass');
    } else {
      exports.print('[FAIL] ' + message, 'fail');
    }
  };

  exports.next = function() {
    setTimeout(function() {
      sendMessage('testNext');
    }, 500); // 留 500ms 空隙，使得异步操作能有时间完成。
  };

  exports.done = function() {
    exports.print('[DONE]');
    exports.next();
  };

});
<commit_msg>Catch errors in iframe pages<commit_after>(function(factory) {

  if (typeof define === 'function') {
    define(factory)
  }
  else if (typeof exports !== 'undefined') {
    factory(require, exports)
  }
  else {
    factory(null, (this['test'] = {}))
  }

})(function(require, exports) {

  exports.print = function(txt, style) {
    sendMessage('printResults', txt, style || 'info')
  }

  exports.assert = function (guard, message) {
    if (typeof message === 'undefined') {
      message = ''
    }

    if (guard) {
      exports.print('[PASS] ' + message, 'pass')
    } else {
      exports.print('[FAIL] ' + message, 'fail')
    }
  }

  exports.next = function() {
    setTimeout(function() {
      sendMessage('testNext')
    }, 500) // 留 500ms 空隙，使得异步操作能有时间完成。
  }

  exports.done = function() {
    exports.print('[DONE]')
    exports.next()
  }

  function error(err) {
    exports.print('[ERROR] ' + err, 'error')
    exports.next()
  }

  // Catch errors
  if (typeof process !== 'undefined') {
    process.on('uncaughtException', error)
  }
  else {
    window.onerror = error
  }

  // Helpers
  // -------

  function sendMessage(fn, msg, type) {
    var p = this
    if (this != this.parent) {
      p = this.parent
    }

    if (p && p[fn]) {
      p[fn](msg, type)
    }
    else if (typeof console !== 'undefined') {
      console.log(color(msg, type))
    }
  }

  // https://github.com/loopj/commonjs-ansi-color/blob/master/lib/ansi-color.js
  var ANSI_CODES = {
    'fail': 31, // red
    'error': 31, // red
    'pass': 32, // green
    'info': 36 // cyan
  }

  function color(str, type) {
    return '\033[' + ANSI_CODES[type] + 'm  ' + str + '\033[0m'
  }

});
<|endoftext|>


<commit_before>'use strict';

nwmApplication.service('CanvasService',['$routeParams', '$window', 'SocketIoService','UserService','Utils','FabricService', function($routeParams, $window, SocketIoService,UserService, Utils, FabricService){
    var self = this;
    var canvasToolOptions = [
        {name: 'None', glyphiconicon: 'glyphicon-off', active: true, isDrawingMode: false},
        {name: 'Write', glyphiconicon: 'glyphicon-font', active: false, isDrawingMode: false},
        {name: 'Draw', glyphiconicon: 'glyphicon-pencil', active: false, isDrawingMode: true}
    ];

    this.canvasTools = function() {
        return canvasToolOptions;
    }

    this.findActiveCanvasTool = function(){
        return canvasToolOptions.filter(function(tool){
            if(tool.active){
                return true;
            }
            return false;
        })[0];
    }

    this.findCanvasTool = function(name){
        return canvasToolOptions.filter(function(tool){
            if(tool.name === name ){
                return true;
            }
            return false;
        })[0];
    }

    this.setActiveCanvasTool = function(name) {
        var activeCanvasTool = this.findActiveCanvasTool();
        if(!name || activeCanvasTool.name === name){
            return;
        }
        activeCanvasTool.active = false;
        var tool = this.findCanvasTool(name);
        tool.active = true;
        FabricService.canvas().isDrawingMode = tool.isDrawingMode;
    }

    this.isDrawingMode = function(value){
        FabricService.canvas().isDrawingMode = value;
    }

    this.calculateOffset = function(){
        FabricService.canvas().calcOffset();
    }

    this.start = function($scope){

        SocketIoService.joinRoom(UserService.user());
        FabricService.createCanvas();
        FabricService.addObjectIdToPrototype();

        SocketIoService.reconnect(connect);
        SocketIoService.syncClient(syncClient);
        SocketIoService.updateCanvas(updateCanvas);
        SocketIoService.writing(writing);
        SocketIoService.moving(moving);
        SocketIoService.rotating(rotating);
        SocketIoService.scaling(scaling);
        SocketIoService.addObject(addObject);

        FabricService.pathCreated(pathCreated);
        FabricService.selectionCreated(selectionCreated);
        FabricService.mouseDown(mouseDown);

        function selectionCreated(options){
            var fabricObject = options.target;
            fabricObject.objectId = Utils.guid();
            var fabricObjectJson = JSON.stringify(fabricObject);
            SocketIoService.emit('addObject', fabricObjectJson);
            attachCommonListeners(fabricObject);
        }

        function pathCreated(options){
            var fabricObject = options.path;
            fabricObject.objectId = Utils.guid();
            var fabricObjectJson = JSON.stringify(fabricObject);
            SocketIoService.emit('addObject', fabricObjectJson);
            attachCommonListeners(fabricObject)
        }

        function mouseDown(options){
            if (!options.target && self.findActiveCanvasTool().name === 'Write') {
                self.setActiveCanvasTool('None');
                var iText = FabricService.createItext(options);
                var iTextJson = JSON.stringify(iText);
                SocketIoService.emit('addObject', iTextJson);
                attachListenersToText(iText);
                FabricService.canvas().calcOffset();
                FabricService.canvas().add(iText);
                FabricService.canvas().renderAll();
            }
        }

        function attachListenersToText(iText){
            iText.on('text:changed', function(event){
                SocketIoService.emit('writing', {'objectId':iText.objectId,'text':iText.text});
            });
            attachCommonListeners(iText);
        }

        function attachCommonListeners(fabricObject){
            fabricObject.on('moving', function(event){
                SocketIoService.emit('moving', {'objectId':fabricObject.objectId,'left': fabricObject.left, 'top': fabricObject.top,'originX': fabricObject.originX, 'originY':fabricObject.originY})
            });

            fabricObject.on('rotating', function(event){
                SocketIoService.emit('rotating',{'objectId':fabricObject.objectId, 'angle':fabricObject.angle,'originX': fabricObject.originX, 'originY':fabricObject.originY, 'left': fabricObject.left, 'top': fabricObject.top});
            });

            fabricObject.on('scaling', function(event){
                SocketIoService.emit('scaling', {'objectId': fabricObject.objectId, 'originX': fabricObject.originX, 'originY':fabricObject.originY, 'scaleX': fabricObject.scaleX, 'scaleY':fabricObject.scaleY, 'left': fabricObject.left, 'top': fabricObject.top});
            });
        }

        function connect(){
            SocketIoService.joinRoom(UserService.user());
        }

        function syncClient(message){
            SocketIoService.emit('syncClient', {clientId:message.clientId, canvas:JSON.stringify(FabricService.canvas())});
        }

        function updateCanvas(message){
            FabricService.canvas().loadFromJSON(message.canvas);
            FabricService.canvas().renderAll();
        }

        function writing(message){
            var object = FabricService.findObjectFromCanvasWith(message.objectId);
            object.text = message.text;
            FabricService.canvas().renderAll();
        }

        function moving(message){
            var object = FabricService.findObjectFromCanvasWith(message.objectId);
            object.top = message.top;
            object.left= message.left;
            object.originX = message.originX;
            object.originY = message.originY;
            FabricService.canvas().renderAll();
        }

        function rotating(message){
            var object = FabricService.findObjectFromCanvasWith(message.objectId);
            object.angle = message.angle;
            object.originX = message.originX;
            object.originY = message.originY;
            object.top = message.top;
            object.left= message.left;
            FabricService.canvas().renderAll();
        }

        function scaling(message){
            var object = FabricService.findObjectFromCanvasWith(message.objectId);
            object.scaleX = message.scaleX;
            object.scaleY = message.scaleY;
            object.top = message.top;
            object.left= message.left;
            object.originX = message.originX;
            object.originY = message.originY;
            FabricService.canvas().renderAll();
        }

        function attachFabricObjectListeners(fabricObject) {
            if (fabricObject.type === 'i-text') {
                attachListenersToText(fabricObject);
            } else if (fabricObject.type === 'path') {
                attachCommonListeners(fabricObject);
            } else if (fabricObject.type === 'group') {

                //todo: fix bug when other canvas has group selected and another also.. then moving duoplicates
                var toRemove = [];
                fabricObject._objects.forEach(function (element) {
                    toRemove.push(FabricService.findObjectFromCanvasWith(element.objectId));
                });
                toRemove.forEach(function (element) {
                    FabricService.canvas().remove(element);
                });
                attachCommonListeners(fabricObject);
            }
        }

        function addObject(message){
            var jsonObject = JSON.parse(message);
            fabric.util.enlivenObjects([jsonObject], function(objects){
                var origRenderOnAddRemove = FabricService.canvas().renderOnAddRemove;
                FabricService.canvas().renderOnAddRemove = false;

                objects.forEach(function(fabricObject) {
                    attachFabricObjectListeners(fabricObject);

                    FabricService.canvas().add(fabricObject);
                });

                FabricService.canvas().renderOnAddRemove = origRenderOnAddRemove;
                FabricService.canvas().renderAll();
                FabricService.canvas().calcOffset();
            });
        }
    }
}])<commit_msg>added comment<commit_after>'use strict';

nwmApplication.service('CanvasService',['$routeParams', '$window', 'SocketIoService','UserService','Utils','FabricService', function($routeParams, $window, SocketIoService,UserService, Utils, FabricService){
    var self = this;
    var canvasToolOptions = [
        {name: 'None', glyphiconicon: 'glyphicon-off', active: true, isDrawingMode: false},
        {name: 'Write', glyphiconicon: 'glyphicon-font', active: false, isDrawingMode: false},
        {name: 'Draw', glyphiconicon: 'glyphicon-pencil', active: false, isDrawingMode: true}
    ];

    this.canvasTools = function() {
        return canvasToolOptions;
    }

    this.findActiveCanvasTool = function(){
        return canvasToolOptions.filter(function(tool){
            if(tool.active){
                return true;
            }
            return false;
        })[0];
    }

    this.findCanvasTool = function(name){
        return canvasToolOptions.filter(function(tool){
            if(tool.name === name ){
                return true;
            }
            return false;
        })[0];
    }

    this.setActiveCanvasTool = function(name) {
        var activeCanvasTool = this.findActiveCanvasTool();
        if(!name || activeCanvasTool.name === name){
            return;
        }
        activeCanvasTool.active = false;
        var tool = this.findCanvasTool(name);
        tool.active = true;
        FabricService.canvas().isDrawingMode = tool.isDrawingMode;
    }

    this.isDrawingMode = function(value){
        FabricService.canvas().isDrawingMode = value;
    }

    this.calculateOffset = function(){
        FabricService.canvas().calcOffset();
    }

    this.start = function($scope){

        SocketIoService.joinRoom(UserService.user());
        FabricService.createCanvas();
        FabricService.addObjectIdToPrototype();

        SocketIoService.reconnect(connect);
        SocketIoService.syncClient(syncClient);
        SocketIoService.updateCanvas(updateCanvas);
        SocketIoService.writing(writing);
        SocketIoService.moving(moving);
        SocketIoService.rotating(rotating);
        SocketIoService.scaling(scaling);
        SocketIoService.addObject(addObject);

        FabricService.pathCreated(pathCreated);
        FabricService.selectionCreated(selectionCreated);
        FabricService.mouseDown(mouseDown);

        function selectionCreated(options){
            var fabricObject = options.target;
            fabricObject.objectId = Utils.guid();
            var fabricObjectJson = JSON.stringify(fabricObject);
            //todo: when sending, do full clear to canvas. So this needs separate function/stream
            SocketIoService.emit('addObject', fabricObjectJson);
            attachCommonListeners(fabricObject);
        }

        function pathCreated(options){
            var fabricObject = options.path;
            fabricObject.objectId = Utils.guid();
            var fabricObjectJson = JSON.stringify(fabricObject);
            SocketIoService.emit('addObject', fabricObjectJson);
            attachCommonListeners(fabricObject)
        }

        function mouseDown(options){
            if (!options.target && self.findActiveCanvasTool().name === 'Write') {
                self.setActiveCanvasTool('None');
                var iText = FabricService.createItext(options);
                var iTextJson = JSON.stringify(iText);
                SocketIoService.emit('addObject', iTextJson);
                attachListenersToText(iText);
                FabricService.canvas().calcOffset();
                FabricService.canvas().add(iText);
                FabricService.canvas().renderAll();
            }
        }

        function attachListenersToText(iText){
            iText.on('text:changed', function(event){
                SocketIoService.emit('writing', {'objectId':iText.objectId,'text':iText.text});
            });
            attachCommonListeners(iText);
        }

        function attachCommonListeners(fabricObject){
            fabricObject.on('moving', function(event){
                SocketIoService.emit('moving', {'objectId':fabricObject.objectId,'left': fabricObject.left, 'top': fabricObject.top,'originX': fabricObject.originX, 'originY':fabricObject.originY})
            });

            fabricObject.on('rotating', function(event){
                SocketIoService.emit('rotating',{'objectId':fabricObject.objectId, 'angle':fabricObject.angle,'originX': fabricObject.originX, 'originY':fabricObject.originY, 'left': fabricObject.left, 'top': fabricObject.top});
            });

            fabricObject.on('scaling', function(event){
                SocketIoService.emit('scaling', {'objectId': fabricObject.objectId, 'originX': fabricObject.originX, 'originY':fabricObject.originY, 'scaleX': fabricObject.scaleX, 'scaleY':fabricObject.scaleY, 'left': fabricObject.left, 'top': fabricObject.top});
            });
        }

        function connect(){
            SocketIoService.joinRoom(UserService.user());
        }

        function syncClient(message){
            SocketIoService.emit('syncClient', {clientId:message.clientId, canvas:JSON.stringify(FabricService.canvas())});
        }

        function updateCanvas(message){
            FabricService.canvas().loadFromJSON(message.canvas);
            FabricService.canvas().renderAll();
        }

        function writing(message){
            var object = FabricService.findObjectFromCanvasWith(message.objectId);
            object.text = message.text;
            FabricService.canvas().renderAll();
        }

        function moving(message){
            var object = FabricService.findObjectFromCanvasWith(message.objectId);
            object.top = message.top;
            object.left= message.left;
            object.originX = message.originX;
            object.originY = message.originY;
            FabricService.canvas().renderAll();
        }

        function rotating(message){
            var object = FabricService.findObjectFromCanvasWith(message.objectId);
            object.angle = message.angle;
            object.originX = message.originX;
            object.originY = message.originY;
            object.top = message.top;
            object.left= message.left;
            FabricService.canvas().renderAll();
        }

        function scaling(message){
            var object = FabricService.findObjectFromCanvasWith(message.objectId);
            object.scaleX = message.scaleX;
            object.scaleY = message.scaleY;
            object.top = message.top;
            object.left= message.left;
            object.originX = message.originX;
            object.originY = message.originY;
            FabricService.canvas().renderAll();
        }

        function attachFabricObjectListeners(fabricObject) {
            if (fabricObject.type === 'i-text') {
                attachListenersToText(fabricObject);
            } else if (fabricObject.type === 'path') {
                attachCommonListeners(fabricObject);
            } else if (fabricObject.type === 'group') {

                var toRemove = [];
                fabricObject._objects.forEach(function (element) {
                    toRemove.push(FabricService.findObjectFromCanvasWith(element.objectId));
                });
                toRemove.forEach(function (element) {
                    FabricService.canvas().remove(element);
                });
                attachCommonListeners(fabricObject);
            }
        }

        function addObject(message){
            var jsonObject = JSON.parse(message);
            fabric.util.enlivenObjects([jsonObject], function(objects){
                var origRenderOnAddRemove = FabricService.canvas().renderOnAddRemove;
                FabricService.canvas().renderOnAddRemove = false;
                objects.forEach(function(fabricObject) {
                    attachFabricObjectListeners(fabricObject);

                    FabricService.canvas().add(fabricObject);
                });

                FabricService.canvas().renderOnAddRemove = origRenderOnAddRemove;
                FabricService.canvas().renderAll();
                FabricService.canvas().calcOffset();
            });
        }
    }
}])<|endoftext|>


<commit_before>var LOCALHOST = '127.0.0.1';
var dnsCache = {};
var tunnelDnsCache = {};
var count = 1;
var isShowIp = localStorage.showIp;
var hasShowIpMenu;

function checkWhistleVersion() {
	var xhr = new XMLHttpRequest();
	xhr.timeout = 10000;
	xhr.open('GET', 'http://local.whistlejs.com/cgi-bin/server-info?' + Date.now(), true);
	xhr.onreadystatechange = function() {
		if (xhr.readyState != 4) {
			return;
		}
		hasShowIpMenu = false;
		try {
			var version = JSON.parse(xhr.responseText).server.version.split('.');
			hasShowIpMenu = version[0] >= 0 && version[1] >= 7;
		} catch(e) {}
	};
	xhr.send();
}
checkWhistleVersion();

var util = (function() {

	return {
		parseJSON: function(str) {
			try {
				return JSON.parse(str) || {};
			} catch(e) {}

			return {};
		}
	};
})();

var proxy = (function() {
	var proxyConfig = util.parseJSON(localStorage.proxyConfig);
	var proxies = {};
	var list;

	if (!$.isArray(proxyConfig.list)) {
		list = proxyConfig.list = [];
	}

	if (!localStorage.init) {
		list.push({
			name: 'whistle',
			host: LOCALHOST,
			port: 8899,
			active: true
		}, {
			name: 'aeproxy',
			host: LOCALHOST,
			port: 9527
		});
		localStorage.init = true;
		store();
	}

	list = proxyConfig.list = proxyConfig.list.filter(function(item) {
		if (!item || !item.name) {
			return false;
		}
		proxies[item.name] = item;
		return true;
	});

	function store() {
		localStorage.proxyConfig = JSON.stringify(proxyConfig);
		showHostIpInResHeaders(isShowIp);
		checkWhistleVersion();
	}

	function cleartSelection() {
		proxyConfig.system = false;
		proxyConfig.direct = false;
		list.forEach(function(item) {
			item.active = false;
		});
	}

	function active(host, port, callback) {
		host = host || LOCALHOST;
		port = parseInt(port) || 8899;
		chrome.proxy.settings.set({value: {
		    mode: 'fixed_servers',
		    rules: {
		        proxyForHttp: {
		            scheme: 'http',
		            host: host,
		            port: port
		        },
		        proxyForHttps: {
		            scheme: 'http',
		            host: host,
		            port: port
		        }
		    }
		}}, callback);

	}

	function enable(name, callback) {
		var item = proxies[name];
		if (!item) {
			return;
		}
		cleartSelection();
		item.active = true;
		active(item.host, item.port, callback);
		chrome.browserAction.setTitle({
			title: name + '(' + (item.host || LOCALHOST) + ':' + (item.port || 8899) + ')'
		});
		store();
	}

	function save(name, host, port) {
		if (!name) {
			return;
		}

		var item = proxies[name];
		if (item) {
			item.host = host;
			item.port = port;
		} else {
			proxies[name] = item = {
					name: name,
					host: host,
					port: port
			};
			list.push(item);
		}
		store();
	}

	function rename(name, newName) {
		if (!name || !newName || !proxies[name] || name == newName) {
			return;
		}
		var item = proxies[name];
		delete proxies[name];
		item.name = newName;
		proxies[newName] = item;
		store();
	}

	return {
		setDirect: function(callback) {
			chrome.proxy.settings.set({value: {mode: 'direct'}}, callback);
			cleartSelection();
			proxyConfig.direct = true;
			store();
			chrome.browserAction.setTitle({
				title: '直接请求'
			});
		},
		setSystem: function(callback) {
			chrome.proxy.settings.set({value: {mode: 'system'}}, callback);
			cleartSelection();
			proxyConfig.system = true;
			store();
			chrome.browserAction.setTitle({
				title: '系统代理'
			});
		},
		removeProxy: function(name) {
			var item = proxies[name];
			if (!item) {
				return false;
			}

			list.splice(list.indexOf(item), 1);
			store();
			return true;
		},
		saveProxy: save,
		setProxy: function(name, host, port) {
			save(name);
			enable(name);
		},
		renameProxy: rename,
		enableProxy: enable,
		getProxy: function(name) {
			return proxies[name];
		},
		getProxyConfig: function() {
			return proxyConfig; //只是内部使用，不用副本
		}
	};
})();

function openWhistlePage(name) {
	openWindow(getWhistlePageUrl(name), true);
}

function getWhistlePageUrl(name) {
	return 'http://local.whistlejs.com/#' + name;
}

function openOptions() {
    openWindow(chrome.extension.getURL('options.html'));
}

function openAbout() {
	openWindow(chrome.extension.getURL('about.html'));
}

function openWindow(url, pinned) {
	chrome.tabs.getAllInWindow(null, function (tabs) {
        for (var i = 0, len = tabs.length; i < len; i++) {
        	var tab = tabs[i];
            if (getUrl(tab.url) == getUrl(url)) {
            	var options = {selected: true};
            	if (tab.url != url) {
            		options.url = url;
            	}
                chrome.tabs.update(tab.id, options);
                return;
            }
        }

        chrome.tabs.query({active: true}, function(tabs){
		    var tab = tabs[0];
		    chrome.tabs.create({
			    index: tab ? tab.index + 1 : 100,
			    url: url,
			    active: true,
			    pinned: !!pinned
			});
		});
    });
}

function hideIp() {
	isShowIp = null;
	localStorage.removeItem('showIp');
	dnsCache = {};
	tunnelDnsCache = {};
	showHostIpInResHeaders();
}

function showIp() {
	isShowIp = true;
	localStorage.showIp = 1;
	showHostIpInResHeaders(true);
}

function showHostIpInResHeaders(show) {
	var xhr = new XMLHttpRequest();
	xhr.open('POST', 'http://local.whistlejs.com/cgi-bin/show-host-ip-in-res-headers', true);
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	xhr.send('showHostIpInResHeaders=' + (show ? 1 : 0));
}

chrome.webRequest.onHeadersReceived.addListener(
	  function(info) {
		  if (!isShowIp) {
			  return;
		  }
		  var headers = info.responseHeaders || [];
		  for (var i = 0, len = headers.length; i < len; i++) {
			  var header = headers[i];
			  if (header.name == 'x-host-ip') {
				  if (header.value) {
					  dnsCache[info.url] = header.value;
				  }
				  return;
			  }
		  }
	  }, {urls: [], types: []}, ['responseHeaders']
);

chrome.runtime.onMessage.addListener(
		function(request, sender, sendResponse) {
			var type = request && request.type;
			if (!isShowIp || type != 'getIp') {
				return;
			}
			var url = sender.url;
			var ip = dnsCache[url];
			if (ip) {
				sendResponse(ip);
			} else if (/^https:\/\//i.test(url)) {
				var index = request.index;
				if (!index) {
					var xhr = new XMLHttpRequest();
					index = count++;
					xhr.timeout = 10000;
					xhr.open('GET', 'http://local.whistlejs.com/cgi-bin/lookup-tunnel-dns?url=' + encodeURIComponent(url.substring(0, 512)), true)+ '&' + Date.now();
					xhr.onreadystatechange = function() {
						if (xhr.readyState != 4) {
							return;
						}
						try {
							if (ip = JSON.parse(xhr.responseText).host) {
								tunnelDnsCache[index] = ip;
								return;
							}
						} catch(e) {}
						tunnelDnsCache[index] = -1;
					};
					xhr.send();
					sendResponse(index);
				} else if (tunnelDnsCache[index]) {
					sendResponse(tunnelDnsCache[index]);
					delete tunnelDnsCache[index];
				} else {
					sendResponse(index);
				}
			}
		}
);

function getUrl(url) {

	return url && url.replace(/#.*$/, '');
}

function init() {
	var config = proxy.getProxyConfig();
	if (config.direct) {
		proxy.setDirect();
	} else if(config.system) {
		proxy.setSystem();
	} else {
		config.list.forEach(function(item) {
			item.active && proxy.enableProxy(item.name);
		});
	}
}

init();
<commit_msg>prevent cache<commit_after>var LOCALHOST = '127.0.0.1';
var dnsCache = {};
var tunnelDnsCache = {};
var count = 1;
var isShowIp = localStorage.showIp;
var hasShowIpMenu;

function checkWhistleVersion() {
	var xhr = new XMLHttpRequest();
	xhr.timeout = 10000;
	xhr.open('GET', 'http://local.whistlejs.com/cgi-bin/server-info?' + Date.now(), true);
	xhr.onreadystatechange = function() {
		if (xhr.readyState != 4) {
			return;
		}
		hasShowIpMenu = false;
		try {
			var version = JSON.parse(xhr.responseText).server.version.split('.');
			hasShowIpMenu = version[0] > 0 || version[1] >= 7;
		} catch(e) {}
	};
	xhr.send();
}
checkWhistleVersion();

var util = (function() {

	return {
		parseJSON: function(str) {
			try {
				return JSON.parse(str) || {};
			} catch(e) {}

			return {};
		}
	};
})();

var proxy = (function() {
	var proxyConfig = util.parseJSON(localStorage.proxyConfig);
	var proxies = {};
	var list;

	if (!$.isArray(proxyConfig.list)) {
		list = proxyConfig.list = [];
	}

	if (!localStorage.init) {
		list.push({
			name: 'whistle',
			host: LOCALHOST,
			port: 8899,
			active: true
		}, {
			name: 'aeproxy',
			host: LOCALHOST,
			port: 9527
		});
		localStorage.init = true;
		store();
	}

	list = proxyConfig.list = proxyConfig.list.filter(function(item) {
		if (!item || !item.name) {
			return false;
		}
		proxies[item.name] = item;
		return true;
	});

	function store() {
		localStorage.proxyConfig = JSON.stringify(proxyConfig);
		showHostIpInResHeaders(isShowIp);
		checkWhistleVersion();
	}

	function cleartSelection() {
		proxyConfig.system = false;
		proxyConfig.direct = false;
		list.forEach(function(item) {
			item.active = false;
		});
	}

	function active(host, port, callback) {
		host = host || LOCALHOST;
		port = parseInt(port) || 8899;
		chrome.proxy.settings.set({value: {
		    mode: 'fixed_servers',
		    rules: {
		        proxyForHttp: {
		            scheme: 'http',
		            host: host,
		            port: port
		        },
		        proxyForHttps: {
		            scheme: 'http',
		            host: host,
		            port: port
		        }
		    }
		}}, callback);

	}

	function enable(name, callback) {
		var item = proxies[name];
		if (!item) {
			return;
		}
		cleartSelection();
		item.active = true;
		active(item.host, item.port, callback);
		chrome.browserAction.setTitle({
			title: name + '(' + (item.host || LOCALHOST) + ':' + (item.port || 8899) + ')'
		});
		store();
	}

	function save(name, host, port) {
		if (!name) {
			return;
		}

		var item = proxies[name];
		if (item) {
			item.host = host;
			item.port = port;
		} else {
			proxies[name] = item = {
					name: name,
					host: host,
					port: port
			};
			list.push(item);
		}
		store();
	}

	function rename(name, newName) {
		if (!name || !newName || !proxies[name] || name == newName) {
			return;
		}
		var item = proxies[name];
		delete proxies[name];
		item.name = newName;
		proxies[newName] = item;
		store();
	}

	return {
		setDirect: function(callback) {
			chrome.proxy.settings.set({value: {mode: 'direct'}}, callback);
			cleartSelection();
			proxyConfig.direct = true;
			store();
			chrome.browserAction.setTitle({
				title: '直接请求'
			});
		},
		setSystem: function(callback) {
			chrome.proxy.settings.set({value: {mode: 'system'}}, callback);
			cleartSelection();
			proxyConfig.system = true;
			store();
			chrome.browserAction.setTitle({
				title: '系统代理'
			});
		},
		removeProxy: function(name) {
			var item = proxies[name];
			if (!item) {
				return false;
			}

			list.splice(list.indexOf(item), 1);
			store();
			return true;
		},
		saveProxy: save,
		setProxy: function(name, host, port) {
			save(name);
			enable(name);
		},
		renameProxy: rename,
		enableProxy: enable,
		getProxy: function(name) {
			return proxies[name];
		},
		getProxyConfig: function() {
			return proxyConfig; //只是内部使用，不用副本
		}
	};
})();

function openWhistlePage(name) {
	openWindow(getWhistlePageUrl(name), true);
}

function getWhistlePageUrl(name) {
	return 'http://local.whistlejs.com/#' + name;
}

function openOptions() {
    openWindow(chrome.extension.getURL('options.html'));
}

function openAbout() {
	openWindow(chrome.extension.getURL('about.html'));
}

function openWindow(url, pinned) {
	chrome.tabs.getAllInWindow(null, function (tabs) {
        for (var i = 0, len = tabs.length; i < len; i++) {
        	var tab = tabs[i];
            if (getUrl(tab.url) == getUrl(url)) {
            	var options = {selected: true};
            	if (tab.url != url) {
            		options.url = url;
            	}
                chrome.tabs.update(tab.id, options);
                return;
            }
        }

        chrome.tabs.query({active: true}, function(tabs){
		    var tab = tabs[0];
		    chrome.tabs.create({
			    index: tab ? tab.index + 1 : 100,
			    url: url,
			    active: true,
			    pinned: !!pinned
			});
		});
    });
}

function hideIp() {
	isShowIp = null;
	localStorage.removeItem('showIp');
	dnsCache = {};
	tunnelDnsCache = {};
	showHostIpInResHeaders();
}

function showIp() {
	isShowIp = true;
	localStorage.showIp = 1;
	showHostIpInResHeaders(true);
}

function showHostIpInResHeaders(show) {
	var xhr = new XMLHttpRequest();
	xhr.open('POST', 'http://local.whistlejs.com/cgi-bin/show-host-ip-in-res-headers', true);
	xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	xhr.send('showHostIpInResHeaders=' + (show ? 1 : 0));
}

chrome.webRequest.onHeadersReceived.addListener(
	  function(info) {
		  if (!isShowIp) {
			  return;
		  }
		  var headers = info.responseHeaders || [];
		  for (var i = 0, len = headers.length; i < len; i++) {
			  var header = headers[i];
			  if (header.name == 'x-host-ip') {
				  if (header.value) {
					  dnsCache[info.url] = header.value;
				  }
				  return;
			  }
		  }
	  }, {urls: [], types: []}, ['responseHeaders']
);

chrome.runtime.onMessage.addListener(
		function(request, sender, sendResponse) {
			var type = request && request.type;
			if (!isShowIp || type != 'getIp') {
				return;
			}
			var url = sender.url;
			var ip = dnsCache[url];
			if (ip) {
				sendResponse(ip);
			} else if (/^https:\/\//i.test(url)) {
				var index = request.index;
				if (!index) {
					var xhr = new XMLHttpRequest();
					index = count++;
					xhr.timeout = 10000;
					xhr.open('GET', 'http://local.whistlejs.com/cgi-bin/lookup-tunnel-dns?url=' + encodeURIComponent(url.substring(0, 512)), true)+ '&' + Date.now();
					xhr.onreadystatechange = function() {
						if (xhr.readyState != 4) {
							return;
						}
						try {
							if (ip = JSON.parse(xhr.responseText).host) {
								tunnelDnsCache[index] = ip;
								return;
							}
						} catch(e) {}
						tunnelDnsCache[index] = -1;
					};
					xhr.send();
					sendResponse(index);
				} else if (tunnelDnsCache[index]) {
					sendResponse(tunnelDnsCache[index]);
					delete tunnelDnsCache[index];
				} else {
					sendResponse(index);
				}
			}
		}
);

function getUrl(url) {

	return url && url.replace(/#.*$/, '');
}

function init() {
	var config = proxy.getProxyConfig();
	if (config.direct) {
		proxy.setDirect();
	} else if(config.system) {
		proxy.setSystem();
	} else {
		config.list.forEach(function(item) {
			item.active && proxy.enableProxy(item.name);
		});
	}
}

init();
<|endoftext|>


<commit_before>
const fs = require('fs')

const { compile } = require('../moo')
const python = require('./python')

describe('Moo.compile', () => {

  test("warns for /g, /y, /i, /m", () => {
    expect(() => compile([['word', /foo/]])).not.toThrow()
    expect(() => compile([['word', /foo/g]])).toThrow()
    expect(() => compile([['word', /foo/i]])).toThrow()
    expect(() => compile([['word', /foo/y]])).toThrow()
    expect(() => compile([['word', /foo/m]])).toThrow()
  })

  test("tries to warn for leading ^", () => {
    expect(() => compile([['word', /^BOL/]])).toThrow()
    expect(() => compile([['word', /(^BOL)/]])).toThrow()
    expect(() => compile([['word', /[^]/]])).not.toThrow()
  })

})

describe('Lexer', () => {

  test('ducks', () => {
    var factory = compile([
      ['word', /[a-z]+/],
      ['number', /[0-9]+/],
      [null, / +/],
    ])
    let lexer = factory()
    lexer.feed('ducks are 123 bad')
    expect(lexer.lex().toString()).toBe('ducks')
    expect(lexer.lex().toString()).toBe(' ')
    expect(lexer.lex().toString()).toBe('are')
  })

  test('kurt tokens', () => {
    let pythonFactory = compile(python.rules)
    let tokens = pythonFactory(kurtFile).lexAll()
    expect(tokens.length).toBe(14513)
  })

})

describe('python tokenizer', () => {

  test("1 + 2", () => {
    expect(python.outputTokens("1 + 2")).toEqual([
      'NUMBER "1"',
      'OP "+"',
      'NUMBER "2"',
      'ENDMARKER ""',
    ])
  })

  test('example python file', () => {
    expect(python.outputTokens(python.pythonFile)).toEqual([
      // 'ENCODING "utf-8"',
      'COMMENT "#!/usr/local/bin/python3"',
      'NL "\\n"',
      'NAME "import"',
      'NAME "sys"',
      'NEWLINE "\\n"',
      'NAME "from"',
      'NAME "tokenize"',
      'NAME "import"',
      'NAME "tokenize"',
      'OP ","',
      'NAME "tok_name"',
      'NEWLINE "\\n"',
      'NAME "import"',
      'NAME "json"',
      'NEWLINE "\\n"',
      'NAME "from"',
      'NAME "io"',
      'NAME "import"',
      'NAME "BytesIO"',
      'NEWLINE "\\n"',
      'NL "\\n"',
      'NAME "path"',
      'OP "="',
      'NAME "sys"',
      'OP "."',
      'NAME "argv"',
      'OP "["',
      'NUMBER "1"',
      'OP "]"',
      'NEWLINE "\\n"',
      'NAME "for"',
      'NAME "info"',
      'NAME "in"',
      'NAME "tokenize"',
      'OP "("',
      'NAME "open"',
      'OP "("',
      'NAME "path"',
      'OP ","',
      'STRING "rb"',
      'OP ")"',
      'OP "."',
      'NAME "readline"',
      'OP ")"',
      'OP ":"',
      'NEWLINE "\\n"',
      'INDENT "    "',
      'NAME "print"',
      'OP "("',
      'NAME "tok_name"',
      'OP "["',
      'NAME "info"',
      'OP "."',
      'NAME "type"',
      'OP "]"',
      'OP ","',
      'NAME "json"',
      'OP "."',
      'NAME "dumps"',
      'OP "("',
      'NAME "info"',
      'OP "."',
      'NAME "string"',
      'OP ")"',
      'OP ")"',
      'NEWLINE "\\n"',
      // 'NL "\\n"',
      'DEDENT ""',
      'ENDMARKER ""',
    ])
  })

  test("kurt python", () => {
    let tokens = python.outputTokens(fs.readFileSync('test/kurt.py', 'utf-8'))
    expect(tokens[100]).toBe('NAME "def"')
    expect(tokens.pop()).toBe('ENDMARKER ""')
    tokens.pop()
    expect(tokens.pop()).not.toBe('ERRORTOKEN ""')
    expect(tokens.length).toBe(11616)

    // let expected = fs.readFileSync('test/kurt-tokens.txt', 'utf-8').split('\n')
    // expect(tokens).toEqual(expected)
  })

})
<commit_msg>Fix token test<commit_after>
const fs = require('fs')

const { compile } = require('../moo')
const python = require('./python')

describe('Moo.compile', () => {

  test("warns for /g, /y, /i, /m", () => {
    expect(() => compile([['word', /foo/]])).not.toThrow()
    expect(() => compile([['word', /foo/g]])).toThrow()
    expect(() => compile([['word', /foo/i]])).toThrow()
    expect(() => compile([['word', /foo/y]])).toThrow()
    expect(() => compile([['word', /foo/m]])).toThrow()
  })

  test("tries to warn for leading ^", () => {
    expect(() => compile([['word', /^BOL/]])).toThrow()
    expect(() => compile([['word', /(^BOL)/]])).toThrow()
    expect(() => compile([['word', /[^]/]])).not.toThrow()
  })

})

describe('Lexer', () => {

  test('ducks', () => {
    var factory = compile([
      ['word', /[a-z]+/],
      ['number', /[0-9]+/],
      [null, / +/],
    ])
    let lexer = factory()
    lexer.feed('ducks are 123 bad')
    expect(lexer.lex().toString()).toBe('ducks')
    expect(lexer.lex().toString()).toBe(' ')
    expect(lexer.lex().toString()).toBe('are')
  })

  test('kurt tokens', () => {
    let pythonFactory = compile(python.rules)
    let tokens = pythonFactory(fs.readFileSync('test/kurt.py', 'utf-8')).lexAll()
    expect(tokens.length).toBe(14513)
  })

})

describe('python tokenizer', () => {

  test("1 + 2", () => {
    expect(python.outputTokens("1 + 2")).toEqual([
      'NUMBER "1"',
      'OP "+"',
      'NUMBER "2"',
      'ENDMARKER ""',
    ])
  })

  test('example python file', () => {
    expect(python.outputTokens(python.pythonFile)).toEqual([
      // 'ENCODING "utf-8"',
      'COMMENT "#!/usr/local/bin/python3"',
      'NL "\\n"',
      'NAME "import"',
      'NAME "sys"',
      'NEWLINE "\\n"',
      'NAME "from"',
      'NAME "tokenize"',
      'NAME "import"',
      'NAME "tokenize"',
      'OP ","',
      'NAME "tok_name"',
      'NEWLINE "\\n"',
      'NAME "import"',
      'NAME "json"',
      'NEWLINE "\\n"',
      'NAME "from"',
      'NAME "io"',
      'NAME "import"',
      'NAME "BytesIO"',
      'NEWLINE "\\n"',
      'NL "\\n"',
      'NAME "path"',
      'OP "="',
      'NAME "sys"',
      'OP "."',
      'NAME "argv"',
      'OP "["',
      'NUMBER "1"',
      'OP "]"',
      'NEWLINE "\\n"',
      'NAME "for"',
      'NAME "info"',
      'NAME "in"',
      'NAME "tokenize"',
      'OP "("',
      'NAME "open"',
      'OP "("',
      'NAME "path"',
      'OP ","',
      'STRING "rb"',
      'OP ")"',
      'OP "."',
      'NAME "readline"',
      'OP ")"',
      'OP ":"',
      'NEWLINE "\\n"',
      'INDENT "    "',
      'NAME "print"',
      'OP "("',
      'NAME "tok_name"',
      'OP "["',
      'NAME "info"',
      'OP "."',
      'NAME "type"',
      'OP "]"',
      'OP ","',
      'NAME "json"',
      'OP "."',
      'NAME "dumps"',
      'OP "("',
      'NAME "info"',
      'OP "."',
      'NAME "string"',
      'OP ")"',
      'OP ")"',
      'NEWLINE "\\n"',
      // 'NL "\\n"',
      'DEDENT ""',
      'ENDMARKER ""',
    ])
  })

  test("kurt python", () => {
    let tokens = python.outputTokens(fs.readFileSync('test/kurt.py', 'utf-8'))
    expect(tokens[100]).toBe('NAME "def"')
    expect(tokens.pop()).toBe('ENDMARKER ""')
    tokens.pop()
    expect(tokens.pop()).not.toBe('ERRORTOKEN ""')
    expect(tokens.length).toBe(11616)

    // let expected = fs.readFileSync('test/kurt-tokens.txt', 'utf-8').split('\n')
    // expect(tokens).toEqual(expected)
  })

})
<|endoftext|>