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

foam.CLASS({
  package: 'foam.dao',
  name: 'PipelinePMDAO',
  extends: 'foam.dao.ProxyDAO',

  implements: [
    'foam.nanos.auth.EnabledAware',
    'foam.nanos.boot.NSpecAware'
  ],

  requires: [
    'foam.nanos.pm.PM'
  ],

  javaImports: [
    'foam.core.X',
    'foam.mlang.order.Comparator',
    'foam.mlang.predicate.Predicate',
    'foam.nanos.auth.EnabledAware',
    'foam.nanos.pm.PM',
  ],

  constants: [
    {
      name: 'PIPE_PM_START',
      documentation: '',
      type: 'String',
      value: 'pipePmStart'
    }
  ],

  properties: [
    {
      name: 'enabled',
      class: 'Boolean',
      value: false,
      javaPostSet: `
      if ( ! val ) {
        DAO d = getDelegate();
        while ( d != null ) {
          if ( d instanceof PipelinePMDAO ) {
            ((EnabledAware) d).setEnabled(val);
            break;
          }
          if ( d instanceof ProxyDAO ) {
            d = ((ProxyDAO) d).getDelegate();
          } else {
            break;
          }
        }
      }
      `
    },
    {
      name: 'nSpec',
      class: 'FObjectProperty',
      type: 'foam.nanos.boot.NSpec'
    },
    {
      name: 'classType',
      class: 'Class',
      javaFactory: `
        return PMDAO.getOwnClassInfo();
      `,
      hidden: true
    },
    {
      documentation: 'Enable PMs on DAO.find operations',
      name: 'pmFind',
      class: 'Boolean'
    },
    {
      name: 'putName',
      class: 'String',
      javaFactory: 'return getNSpec().getName()+":pipePut";',
      visibility: 'RO'
    },
    {
      name: 'findName',
      class: 'String',
      javaFactory: 'return getNSpec().getName()+":pipeFind";',
      visibility: 'RO'
    },
    {
      name: 'selectName',
      class: 'String',
      javaFactory: 'return getNSpec().getName()+":pipeSelect";',
      visibility: 'RO'
    },
    {
      name: 'removeName',
      class: 'String',
      javaFactory: 'return getNSpec().getName()+":pipeRemove";',
      visibility: 'RO'
    },
    {
      name: 'removeAllName',
      class: 'String',
      javaFactory: 'return getNSpec().getName()+":pipeRemoveAll";',
      visibility: 'RO'
    },
    {
      name: 'cmdName',
      class: 'String',
      javaFactory: 'return getNSpec().getName()+":pipeCmd";',
      visibility: 'RO'
    },
  ],

  axioms: [
    {
      name: 'javaExtras',
      buildJavaClass: function(cls) {
        cls.extras.push(foam.java.Code.create({
          data:
    `public PipelinePMDAO(X x, DAO delegate) {
       super(x, delegate);
     }
          `
        }));
      }
    }
  ],

  methods: [
    {
      name: 'init_',
      javaCode: `
    if ( getEnabled() ) {
      createPipeline();
    }
`
    },
    {
      documentation: `
Creates the PM that will measure the performance of each operation and creates a new context with it as a variable which the EndPipelinePMDAO
   *  will use to access the pm after it is passed onto it through the arguments of the DAO operations
`,
      name: 'createPMX',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'op',
          type: 'String'
        }
      ],
      javaType: 'X',
      javaCode: `
    if ( getEnabled() ) {
      PM pm = new PM();
      pm.setClassType(this.getClassType());
      pm.setName(op);
      pm.init_();
      return x.put(PIPE_PM_START, pm);
    }
    return x;
      `
    },
    {
      documentation: `
Creates the PM pipeline by adding an EndPipelinePMDAO after of this class only if it is a ProxyDAO.
If the delegate of that is also a ProxyDAO, creates a new PipelinedPMDAO in the chain beofre it which repeats this procedure recursively.
`,
      name: 'createPipeline',
      javaCode: `
    DAO delegate = getDelegate();
    DAO secondaryDelegate;
    secondaryDelegate = ((ProxyDAO) delegate).getDelegate();
    ((ProxyDAO) delegate).setDelegate(new EndPipelinePMDAO(getX(), secondaryDelegate));
    delegate = ((ProxyDAO) delegate).getDelegate();
    if ( ( secondaryDelegate instanceof ProxyDAO ) && ! ( secondaryDelegate instanceof PipelinePMDAO ) ) {
      ((ProxyDAO) delegate).setDelegate(new PipelinePMDAO(getX(), secondaryDelegate));
    }
      `
    },
    {
      name: 'isPipelinePMDAO',
      args: [
        {
          name: 'dao',
          type: 'DAO'
        }
      ],
      javaType: 'Boolean',
      javaCode: `
    return dao instanceof PipelinePMDAO || dao instanceof EndPipelinePMDAO;
`
    },
    {
      name: 'log',
      args: [
        {
          name: 'x',
          type: 'X',
        },
        {
          name: 'pm',
          type: 'PM'
        }
      ],
      javaCode: `
    if ( pm != null ) {
      pm.log(x);
    }
      `
    },
    {
      name: 'put_',
      javaCode: `
    return super.put_(createPMX(x, getPutName()), obj);
     `
    },
    {
      name: 'find_',
      javaCode: `
    X y = getPmFind() ? createPMX(x, getFindName()) : x;
    return super.find_(y, id);
     `
    },
    {
      name: 'select_',
      javaCode: `
      return super.select_(createPMX(x, getSelectName()), sink, skip, limit, order, predicate);
     `
    },
    {
      name: 'remove_',
      javaCode: `
    return super.remove_(createPMX(x, getRemoveName()), obj);
     `
    },
    {
      name: 'removeAll_',
      javaCode: `
    super.removeAll_(createPMX(x, getRemoveAllName()), skip, limit, order, predicate);
      `
    },
    {
      name: 'cmd_',
      javaCode: `
    return super.cmd_(createPMX(x, getCmdName()), obj);
     `
    }
  ],

  classes: [
    {
//      package: 'foam.dao',
      name: 'EndPipelinePMDAO',
      extends: 'foam.dao.ProxyDAO',

      requires: [
        'foam.nanos.pm.PM'
      ],

      javaImports: [
        'foam.core.X',
        'foam.mlang.order.Comparator',
        'foam.mlang.predicate.Predicate',
        'foam.nanos.pm.PM',
      ],

      axioms: [
        {
          name: 'javaExtras',
          buildJavaClass: function(cls) {
            cls.extras.push(foam.java.Code.create({
              data:`
     public EndPipelinePMDAO(X x, DAO delegate) {
       super(x, delegate);
     }
          `
            }));
          }
        }
      ],

      methods: [
        {
          name: 'log',
          args: [
            {
              name: 'x',
              type: 'X',
            }
          ],
          javaCode: `
      PM pm = (PM) x.get(PIPE_PM_START);
      if ( pm != null ) {
        pm.log(x);
      }
      `
        },
        {
          name: 'put_',
          javaCode: `
      log(x);
      return super.put_(x, obj);
     `
        },
        {
          name: 'find_',
          javaCode: `
      log(x);
      return super.find_(x, id);
     `
        },
        {
          name: 'select_',
          javaCode: `
      log(x);
      return super.select_(x, sink, skip, limit, order, predicate);
     `
        },
        {
          name: 'remove_',
          javaCode: `
      log(x);
      return super.remove_(x, obj);
     `
        },
        {
          name: 'removeAll_',
          javaCode: `
      log(x);
      super.removeAll_(x, skip, limit, order, predicate);
     `
        },
        {
          name: 'cmd_',
          javaCode: `
      log(x);
      return super.cmd_(x, obj);
     `
        }
      ]
    }
  ]
});
<commit_msg>Handle createPipeline called from either init_ or enabled, and handle dynamic enable/disable.<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.dao',
  name: 'PipelinePMDAO',
  extends: 'foam.dao.ProxyDAO',

  implements: [
    'foam.nanos.auth.EnabledAware',
    'foam.nanos.boot.NSpecAware'
  ],

  requires: [
    'foam.nanos.pm.PM'
  ],

  javaImports: [
    'foam.core.X',
    'foam.mlang.order.Comparator',
    'foam.mlang.predicate.Predicate',
    'foam.nanos.auth.EnabledAware',
    'foam.nanos.pm.PM',
  ],

  constants: [
    {
      name: 'PIPE_PM_START',
      documentation: '',
      type: 'String',
      value: 'pipePmStart'
    }
  ],

  properties: [
    {
      documentation: `true when createPipeline has been called.
both enabled.postSet and Builder.init_() can call createPipeline.`,
      name: 'initialized',
      class: 'Boolean',
      value: false
    },
    {
      name: 'enabled',
      class: 'Boolean',
      value: true,
      javaPostSet: `
      if ( enabled_ != val ) {
        boolean toggled = false;
        DAO d = getDelegate();
        while ( d != null ) {
          if ( d instanceof PipelinePMDAO ) {
            ((EnabledAware) d).setEnabled(val);
            toggled = true;
            break;
          }
          if ( d instanceof ProxyDAO ) {
            d = ((ProxyDAO) d).getDelegate();
          } else {
            break;
          }
        }
        if ( val &&
             ! toggled ) {
          // createPipeline has yet to be called.
          createPipeline();
        }
      }
      `
    },
    {
      name: 'nSpec',
      class: 'FObjectProperty',
      type: 'foam.nanos.boot.NSpec'
    },
    {
      name: 'classType',
      class: 'Class',
      javaFactory: `
        return PMDAO.getOwnClassInfo();
      `,
      hidden: true
    },
    {
      documentation: 'Enable PMs on DAO.find operations',
      name: 'pmFind',
      class: 'Boolean'
    },
    {
      name: 'putName',
      class: 'String',
      javaFactory: 'return getNSpec().getName()+":pipePut";',
      visibility: 'RO'
    },
    {
      name: 'findName',
      class: 'String',
      javaFactory: 'return getNSpec().getName()+":pipeFind";',
      visibility: 'RO'
    },
    {
      name: 'selectName',
      class: 'String',
      javaFactory: 'return getNSpec().getName()+":pipeSelect";',
      visibility: 'RO'
    },
    {
      name: 'removeName',
      class: 'String',
      javaFactory: 'return getNSpec().getName()+":pipeRemove";',
      visibility: 'RO'
    },
    {
      name: 'removeAllName',
      class: 'String',
      javaFactory: 'return getNSpec().getName()+":pipeRemoveAll";',
      visibility: 'RO'
    },
    {
      name: 'cmdName',
      class: 'String',
      javaFactory: 'return getNSpec().getName()+":pipeCmd";',
      visibility: 'RO'
    },
  ],

  axioms: [
    {
      name: 'javaExtras',
      buildJavaClass: function(cls) {
        cls.extras.push(foam.java.Code.create({
          data:
    `public PipelinePMDAO(X x, DAO delegate) {
       super(x, delegate);
     }
          `
        }));
      }
    }
  ],

  methods: [
    {
      name: 'init_',
      javaCode: `
    if ( getEnabled() ) {
      createPipeline();
    }
    `
    },
    {
      documentation: `
Creates the PM that will measure the performance of each operation and creates a new context with it as a variable which the EndPipelinePMDAO
   *  will use to access the pm after it is passed onto it through the arguments of the DAO operations
`,
      name: 'createPMX',
      args: [
        {
          name: 'x',
          type: 'Context'
        },
        {
          name: 'op',
          type: 'String'
        }
      ],
      javaType: 'X',
      javaCode: `
    if ( getEnabled() ) {
      PM pm = new PM();
      pm.setClassType(this.getClassType());
      pm.setName(op);
      pm.init_();
      return x.put(PIPE_PM_START, pm);
    }
    return x;
      `
    },
    {
      documentation: `
Creates the PM pipeline by adding an EndPipelinePMDAO after of this class only if it is a ProxyDAO.
If the delegate of that is also a ProxyDAO, creates a new PipelinedPMDAO in the chain beofre it which repeats this procedure recursively.
`,
      name: 'createPipeline',
      javaCode: `
    synchronized (this) {
      if ( getInitialized() ) {
        return;
      } else {
        setInitialized(true);
      }
    }
((foam.nanos.logger.Logger) getX().get("logger")).debug("PipelinePMDAO createPipeline INITIALIZED");
    DAO delegate = getDelegate();
    DAO secondaryDelegate;
    secondaryDelegate = ((ProxyDAO) delegate).getDelegate();
    ((ProxyDAO) delegate).setDelegate(new EndPipelinePMDAO(getX(), secondaryDelegate));
    delegate = ((ProxyDAO) delegate).getDelegate();
    if ( ( secondaryDelegate instanceof ProxyDAO ) && ! ( secondaryDelegate instanceof PipelinePMDAO ) ) {
      ((ProxyDAO) delegate).setDelegate(new PipelinePMDAO(getX(), secondaryDelegate));
    }
      `
    },
    {
      name: 'isPipelinePMDAO',
      args: [
        {
          name: 'dao',
          type: 'DAO'
        }
      ],
      javaType: 'Boolean',
      javaCode: `
    return dao instanceof PipelinePMDAO || dao instanceof EndPipelinePMDAO;
`
    },
    {
      name: 'log',
      args: [
        {
          name: 'x',
          type: 'X',
        },
        {
          name: 'pm',
          type: 'PM'
        }
      ],
      javaCode: `
    if ( pm != null ) {
      pm.log(x);
    }
      `
    },
    {
      name: 'put_',
      javaCode: `
    return super.put_(createPMX(x, getPutName()), obj);
     `
    },
    {
      name: 'find_',
      javaCode: `
    X y = getPmFind() ? createPMX(x, getFindName()) : x;
    return super.find_(y, id);
     `
    },
    {
      name: 'select_',
      javaCode: `
      return super.select_(createPMX(x, getSelectName()), sink, skip, limit, order, predicate);
     `
    },
    {
      name: 'remove_',
      javaCode: `
    return super.remove_(createPMX(x, getRemoveName()), obj);
     `
    },
    {
      name: 'removeAll_',
      javaCode: `
    super.removeAll_(createPMX(x, getRemoveAllName()), skip, limit, order, predicate);
      `
    },
    {
      name: 'cmd_',
      javaCode: `
    return super.cmd_(createPMX(x, getCmdName()), obj);
     `
    }
  ],

  classes: [
    {
//      package: 'foam.dao',
      name: 'EndPipelinePMDAO',
      extends: 'foam.dao.ProxyDAO',

      requires: [
        'foam.nanos.pm.PM'
      ],

      javaImports: [
        'foam.core.X',
        'foam.mlang.order.Comparator',
        'foam.mlang.predicate.Predicate',
        'foam.nanos.pm.PM',
      ],

      axioms: [
        {
          name: 'javaExtras',
          buildJavaClass: function(cls) {
            cls.extras.push(foam.java.Code.create({
              data:`
     public EndPipelinePMDAO(X x, DAO delegate) {
       super(x, delegate);
     }
          `
            }));
          }
        }
      ],

      methods: [
        {
          name: 'log',
          args: [
            {
              name: 'x',
              type: 'X',
            }
          ],
          javaCode: `
      PM pm = (PM) x.get(PIPE_PM_START);
      if ( pm != null ) {
        pm.log(x);
      }
      `
        },
        {
          name: 'put_',
          javaCode: `
      log(x);
      return super.put_(x, obj);
     `
        },
        {
          name: 'find_',
          javaCode: `
      log(x);
      return super.find_(x, id);
     `
        },
        {
          name: 'select_',
          javaCode: `
      log(x);
      return super.select_(x, sink, skip, limit, order, predicate);
     `
        },
        {
          name: 'remove_',
          javaCode: `
      log(x);
      return super.remove_(x, obj);
     `
        },
        {
          name: 'removeAll_',
          javaCode: `
      log(x);
      super.removeAll_(x, skip, limit, order, predicate);
     `
        },
        {
          name: 'cmd_',
          javaCode: `
      log(x);
      return super.cmd_(x, obj);
     `
        }
      ]
    }
  ]
});
<|endoftext|>


<commit_before>var Blocks = function(){
  var scene, cube, mesh, holder;
  var boxAmount = 128;
  var boxSize = 2;
  var boxHeight = boxSize * 2;
  var boxes = [];
  var material = new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0xffffff, shininess: 50 } );

  var x, y;
  var radOffset = -150;
  var radius = (window.innerWidth < window.innerHeight ? window.innerWidth + radOffset : window.innerHeight + radOffset) / 4;
  var h = radius;
  var k = radius;
  var r = radius;

  var lights;
  var colors;

  var step = 2*Math.PI/boxAmount;
  var theta = step * (boxAmount / 4) * -1;

  this.hide = function() {
    holder.position.z = 1000;
  };

  this.show = function() {
    holder.position.z = 0;
  };

  this.update = function(newColors, data) {
    var diff = Math.round(data.spectrum.length / boxes.length);

    if(newColors !== colors) {
      colors = newColors;
      _.each(lights, function(lightObj, index){
        var color = colors[index] || 0xffffff;
        lightObj.light.color.setHex(color);
      });
    }

    _.each(boxes, function(box, index){
      var scale = data.spectrum[diff * index] * 0.1;
      var toScale = scale < 0.01 ? 0.01 : scale;
      box.mesh.scale.y = toScale;
      box.mesh.position.y = boxHeight / 2 * toScale;
    });
  };

  this.init = function(threeScene) {
    scene = threeScene;

    holder = new THREE.Object3D();
    holder.position.z = 0;
    scene.add(holder);

    for (var i = 0; i < boxAmount; i++) {
      addObject();
    }

    addLights();
  };

  function addObject(){
    cube = new THREE.BoxGeometry(boxSize, boxHeight, boxSize);

    var meshHolder = new THREE.Object3D();
    meshHolder.name = "box"+boxes.length;

    mesh = new THREE.Mesh(cube, material);

    x = h + r * Math.cos(theta);
    y = k - r * Math.sin(theta);

    meshHolder.position.x = x -radius;
    meshHolder.position.y = y -radius;
    meshHolder.position.z = -40;

    mesh.position.y = boxSize*3;

    var boxNum = boxes.length;
    var kerroin = boxNum / boxAmount;
    var rotation = 360 * kerroin;

    meshHolder.rotation.z = rotation * (Math.PI/180) * -1;
    //mesh.rotation.x = 45;

    boxes.push({
      mesh: mesh,
      originalPosition: {
        x: meshHolder.position.x,
        y: meshHolder.position.y,
        z: meshHolder.position.z
      },
    });

    meshHolder.add(mesh);

    holder.add(meshHolder);

    theta += step;
  }

  function addLights(){
    var spot = radius;

    lights = [
      {distance: 650, color: 0xff0099, origPos: {x: spot, y: spot, z: 100}},
      {distance: 650, color: 0xff0099, origPos: {x: spot * -1, y: spot, z: 100}},
      {distance: 650, color: 0xff0099, origPos: {x: spot * -1, y: spot * -1, z: 100}},
      {distance: 650, color: 0xff0099, origPos: {x: spot, y: spot * -1, z: 100}}
    ];

    for (var i = 0; i < lights.length; i++) {
      var light = new THREE.PointLight(lights[i].color, 830, lights[i].distance, 30);
      light.position.set(lights[i].origPos.x, lights[i].origPos.y, lights[i].origPos.z);
      scene.add(light);

      lights[i].light = light;
    }
  }
};
<commit_msg>animate hiding / showing blocks<commit_after>var Blocks = function(){
  var scene, cube, mesh, holder;
  var boxAmount = 128;
  var boxSize = 2;
  var boxHeight = boxSize * 2;
  var boxes = [];
  var material = new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0xffffff, shininess: 50 } );

  var x, y;
  var radOffset = -150;
  var radius = (window.innerWidth < window.innerHeight ? window.innerWidth + radOffset : window.innerHeight + radOffset) / 4;
  var h = radius;
  var k = radius;
  var r = radius;

  var lights;
  var colors;

  var step = 2*Math.PI/boxAmount;
  var theta = step * (boxAmount / 4) * -1;

  var hidden = false;
  var hideSpeed = 6;

  this.hide = function() {
    hidden = true;
  };

  this.show = function() {
    hidden = false;
  };

  this.update = function(newColors, data) {
    if(hidden){
      _.each(boxes, function(box, index){
        var toScale = box.mesh.scale.y * 0.9;
        box.mesh.scale.y = toScale;
        box.mesh.position.y = boxHeight / 2 * toScale;
      });

      holder.position.z = holder.position.z < 2000 ? holder.position.z + hideSpeed : holder.position.z;
      return
    } else {
      holder.position.z = holder.position.z > 0 ? holder.position.z - hideSpeed : holder.position.z;
    }

    var diff = Math.round(data.spectrum.length / boxes.length);

    if(newColors !== colors) {
      colors = newColors;
      _.each(lights, function(lightObj, index){
        var color = colors[index] || 0xffffff;
        lightObj.light.color.setHex(color);
      });
    }

    _.each(boxes, function(box, index){
      var scale = data.spectrum[diff * index] * 0.1;
      var toScale = scale < 0.01 ? 0.01 : scale;
      box.mesh.scale.y = toScale;
      box.mesh.position.y = boxHeight / 2 * toScale;
    });
  };

  this.init = function(threeScene) {
    scene = threeScene;

    holder = new THREE.Object3D();
    holder.position.z = 0;
    scene.add(holder);

    for (var i = 0; i < boxAmount; i++) {
      addObject();
    }

    addLights();
  };

  function addObject(){
    cube = new THREE.BoxGeometry(boxSize, boxHeight, boxSize);

    var meshHolder = new THREE.Object3D();
    meshHolder.name = "box"+boxes.length;

    mesh = new THREE.Mesh(cube, material);

    x = h + r * Math.cos(theta);
    y = k - r * Math.sin(theta);

    meshHolder.position.x = x -radius;
    meshHolder.position.y = y -radius;
    meshHolder.position.z = -40;

    mesh.position.y = boxSize*3;

    var boxNum = boxes.length;
    var kerroin = boxNum / boxAmount;
    var rotation = 360 * kerroin;

    meshHolder.rotation.z = rotation * (Math.PI/180) * -1;
    //mesh.rotation.x = 45;

    boxes.push({
      mesh: mesh,
      originalPosition: {
        x: meshHolder.position.x,
        y: meshHolder.position.y,
        z: meshHolder.position.z
      },
    });

    meshHolder.add(mesh);

    holder.add(meshHolder);

    theta += step;
  }

  function addLights(){
    var spot = radius;

    lights = [
      {distance: 650, color: 0xff0099, origPos: {x: spot, y: spot, z: 100}},
      {distance: 650, color: 0xff0099, origPos: {x: spot * -1, y: spot, z: 100}},
      {distance: 650, color: 0xff0099, origPos: {x: spot * -1, y: spot * -1, z: 100}},
      {distance: 650, color: 0xff0099, origPos: {x: spot, y: spot * -1, z: 100}}
    ];

    for (var i = 0; i < lights.length; i++) {
      var light = new THREE.PointLight(lights[i].color, 830, lights[i].distance, 30);
      light.position.set(lights[i].origPos.x, lights[i].origPos.y, lights[i].origPos.z);
      scene.add(light);

      lights[i].light = light;
    }
  }
};
<|endoftext|>


<commit_before>import { Provider } from 'rebass'
import Socket from "./Socket";
import Playing from "./Playing";
import Lobby from "./Lobby";
import theme from "./theme";

if (!Array.prototype.last){
    Array.prototype.last = function(){
        return this[this.length - 1];
    };
};

if (!Array.prototype.clone){
    Array.prototype.clone = function(){
        return JSON.parse(JSON.stringify(this));
    };
};

if (!Array.prototype.remove){
    Array.prototype.remove = function(cb){
        var i = this.findIndex(cb);
        if (i > -1) {
            this.splice(i, 1);
        }
    };
};

function deliver(msg, obj) {
    if (msg.cmd) {
        let o = obj, f;
        let f_name = 'on_' + msg.cmd;
        while (o) {
            f = o[f_name];
            if (typeof f === 'function') {
                f.call(obj, msg);
                return;
            }
            o = Object.getPrototypeOf(o);
        }
    }
}

function on_message(m) {
    let msg = JSON.parse(m.data);
    console.info(msg);
    var q = [this], i=0, ii=1;
    while (i < ii) {
        var o = q[i++];
        if (Array.isArray(o)) {
            ii += o.length;
            Array.prototype.push.apply(q, o);
        }
        else {
            deliver(msg, o);
        }
        if (o.view) {
            ii++;
            q.push(o.view);
        }
    }
}

class Client extends React.Component {
    constructor() {
        super();
        ws = new Socket(on_message.bind(this));
        window.onerror =  function(messageOrEvent, source, lineno, colno, error) {
            ws.send({cmd: 'error',
                message: error.message,
                stack: error.stack
            });
        }
        this.state = { view: () =>
Initializing...
 };
        this.setView = this.setView.bind(this);
        this.back = this.back.bind(this);
        this.stack = [Lobby];
    }

    setView(view) {
        this.stack.push(view);
        this.setState({view: view});
    }

    back() {
        this.stack.pop();
        this.setState({view: this.stack[this.stack.length-1]});
    }

    render() {
        const View = this.state.view;
        return (
      <Provider theme={theme}>
        <View setView={this.setView} back={this.back} ref={(e)=>this.view=e}/>
      </Provider>);
    }

    on_welcome(msg) {
        if ( window.localStorage.token !== undefined ) {
              ws.send({
                  cmd: 'login',
                  name: window.localStorage.name,
                  token: window.localStorage.token,
              });
        }
        else {
              ws.send({ cmd: 'login', name: window.localStorage.name });
        }
    }

    on_login(msg) {
        window.localStorage.token = msg.token;
    }

    on_joined(msg) {
        delete msg.cmd;
        msg.timediff = new Date().getTime() - msg.now;
        gs = msg;
        this.setState({view: msg.game === 'Rally' ? Playing : Lobby});
    }
}

ReactDOM.render(<Client/>, document.getElementById('root'));
<commit_msg>Fix #105, errors during dead/shutdown<commit_after>import { Provider } from 'rebass'
import Socket from "./Socket";
import Playing from "./Playing";
import Lobby from "./Lobby";
import theme from "./theme";

if (!Array.prototype.last){
    Array.prototype.last = function(){
        return this[this.length - 1];
    };
};

if (!Array.prototype.clone){
    Array.prototype.clone = function(){
        return JSON.parse(JSON.stringify(this));
    };
};

if (!Array.prototype.remove){
    Array.prototype.remove = function(cb){
        var i = this.findIndex(cb);
        if (i > -1) {
            this.splice(i, 1);
        }
    };
};

function deliver(msg, obj) {
    if (msg.cmd) {
        let o = obj, f;
        let f_name = 'on_' + msg.cmd;
        while (o) {
            f = o[f_name];
            if (typeof f === 'function') {
                f.call(obj, msg);
                return;
            }
            o = Object.getPrototypeOf(o);
        }
    }
}

function on_message(m) {
    let msg = JSON.parse(m.data);
    console.info(msg);
    var q = [this], i=0, ii=1;
    while (i < ii) {
        var o = q[i++];
        if (Array.isArray(o)) {
            ii += o.length;
            Array.prototype.push.apply(q, o);
        }
        else {
            deliver(msg, o);
        }
        if (o && o.view) {
            ii++;
            q.push(o.view);
        }
    }
}

class Client extends React.Component {
    constructor() {
        super();
        ws = new Socket(on_message.bind(this));
        window.onerror =  function(messageOrEvent, source, lineno, colno, error) {
            ws.send({cmd: 'error',
                message: error.message,
                stack: error.stack
            });
        }
        this.state = { view: () =>
Initializing...
 };
        this.setView = this.setView.bind(this);
        this.back = this.back.bind(this);
        this.stack = [Lobby];
    }

    setView(view) {
        this.stack.push(view);
        this.setState({view: view});
    }

    back() {
        this.stack.pop();
        this.setState({view: this.stack[this.stack.length-1]});
    }

    render() {
        const View = this.state.view;
        return (
      <Provider theme={theme}>
        <View setView={this.setView} back={this.back} ref={(e)=>this.view=e}/>
      </Provider>);
    }

    on_welcome(msg) {
        if ( window.localStorage.token !== undefined ) {
              ws.send({
                  cmd: 'login',
                  name: window.localStorage.name,
                  token: window.localStorage.token,
              });
        }
        else {
              ws.send({ cmd: 'login', name: window.localStorage.name });
        }
    }

    on_login(msg) {
        window.localStorage.token = msg.token;
    }

    on_joined(msg) {
        delete msg.cmd;
        msg.timediff = new Date().getTime() - msg.now;
        gs = msg;
        this.setState({view: msg.game === 'Rally' ? Playing : Lobby});
    }
}

ReactDOM.render(<Client/>, document.getElementById('root'));
<|endoftext|>


<commit_before>/*!
 * OS.js - JavaScript Operating System
 *
 * Copyright (c) 2011-2016, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */
(function(API, Utils, VFS, GUI) {
  'use strict';

  /////////////////////////////////////////////////////////////////////////////
  // HELPERS
  /////////////////////////////////////////////////////////////////////////////

  function getDocument(el, iframe) {
    iframe = iframe || el.querySelector('iframe');
    return iframe.contentDocument || iframe.contentWindow.document;
  }

  function getDocumentData(el) {
    try {
      var doc = getDocument(el);
      return doc.body.innerHTML;
    } catch ( error ) {
      console.error('gui-richtext', 'getDocumentData()', error.stack, error);
    }
    return '';
  }

  function destroyFixInterval(el) {
    el._fixTry = 0;
    el._fixInterval = clearInterval(el._fixInterval);
  }

  function createFixInterval(el, doc, text) {
    if ( el._fixTry > 10 ) {
      el._fixTry = 0;
      return;
    }

    el._fixInterval = setInterval(function() {
      try {
        if ( text ) {
          doc.body.innerHTML = text;
        }
        destroyFixInterval(el);
      } catch ( error ) {
        console.warn('gui-richtext', 'setDocumentData()', error.stack, error, '... trying again');
      }
      el._fixTry++;
    }, 100);
  }

  function setDocumentData(el, text) {
    destroyFixInterval(el);

    text = text || '';

    var wm = OSjs.Core.getWindowManager();
    var theme = (wm ? wm.getSetting('theme') : 'default') || 'default';
    var themeSrc = OSjs.API.getThemeCSS(theme);

    var editable = el.getAttribute('data-editable');
    editable = editable === null || editable === 'true';

    function onMouseDown(ev) {
      function insertTextAtCursor(text) {
        var sel, range, html;
        if (window.getSelection) {
          sel = window.getSelection();
          if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();
            range.insertNode( document.createTextNode(text) );
          }
        } else if (document.selection && document.selection.createRange) {
          document.selection.createRange().text = text;
        }
      }

      if ( ev.keyCode === 9 ) {
        /*
        var t = ev.shiftKey ? "outdent" : "indent";
        document.execCommand("styleWithCSS",true,null);
        document.execCommand(t, false, null)
        */
        insertTextAtCursor('\u00A0');
        ev.preventDefault();
      }
    }

    var script = onMouseDown.toString() + ';window.addEventListener("keydown", onMouseDown)';

    var template = '<!DOCTYPE html><link rel="stylesheet" type="text/css" href="' + themeSrc + '" />';
    if ( !editable ) {
      template = template.replace(' contentEditable="true"', '');
    }

    var doc = getDocument(el);
    doc.open();
    doc.write(template);
    doc.close();
    createFixInterval(el, doc, text);
  }

  /////////////////////////////////////////////////////////////////////////////
  // EXPORTS
  /////////////////////////////////////////////////////////////////////////////

  /**
   * Element: 'gui-richtext'
   *
   * "Richt text" input area.
   *
   * @api OSjs.GUI.Elements.gui-richtext
   * @class
   */
  GUI.Elements['gui-richtext'] = {
    bind: function(el, evName, callback, params) {
      if ( (['selection']).indexOf(evName) !== -1 ) {
        evName = '_' + evName;
      }
      Utils.$bind(el, evName, callback.bind(new GUI.Element(el)), params);
    },
    build: function(el) {
      var text = el.childNodes.length ? el.childNodes[0].nodeValue : '';

      Utils.$empty(el);

      var iframe = document.createElement('iframe');
      iframe.setAttribute('border', 0);
      iframe.onload = function() {
        iframe.contentWindow.addEventListener('selectstart', function() {
          el.dispatchEvent(new CustomEvent('_selection', {detail: {}}));
        });
        iframe.contentWindow.addEventListener('mouseup', function() {
          el.dispatchEvent(new CustomEvent('_selection', {detail: {}}));
        });
      };
      el.appendChild(iframe);

      setTimeout(function() {
        setDocumentData(el, text);
      }, 0);
    },
    call: function(el, method, args) {
      var doc = getDocument(el);
      if ( doc ) {
        if ( method === 'command' ) {
          return doc.execCommand.apply(doc, args);
        } else if ( method === 'query' ) {
          return doc.queryCommandValue.apply(doc, args);
        }
      }
      return null;
    },
    get: function(el, param, value) {
      if ( param === 'value' ) {
        return getDocumentData(el);
      }
      return GUI.Helpers.getProperty(el, param);
    },
    set: function(el, param, value) {
      if ( param === 'value' ) {
        setDocumentData(el, value);
        return true;
      }
      return false;
    }
  };

})(OSjs.API, OSjs.Utils, OSjs.VFS, OSjs.GUI);
<commit_msg>GUI: Richtext prevent error (#214)<commit_after>/*!
 * OS.js - JavaScript Operating System
 *
 * Copyright (c) 2011-2016, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */
(function(API, Utils, VFS, GUI) {
  'use strict';

  /////////////////////////////////////////////////////////////////////////////
  // HELPERS
  /////////////////////////////////////////////////////////////////////////////

  function getDocument(el, iframe) {
    iframe = iframe || el.querySelector('iframe');
    return iframe.contentDocument || iframe.contentWindow.document;
  }

  function getDocumentData(el) {
    try {
      var doc = getDocument(el);
      return doc.body.innerHTML;
    } catch ( error ) {
      console.error('gui-richtext', 'getDocumentData()', error.stack, error);
    }
    return '';
  }

  function destroyFixInterval(el) {
    el._fixTry = 0;
    el._fixInterval = clearInterval(el._fixInterval);
  }

  function createFixInterval(el, doc, text) {
    if ( el._fixTry > 10 ) {
      el._fixTry = 0;
      return;
    }

    el._fixInterval = setInterval(function() {
      try {
        if ( text ) {
          doc.body.innerHTML = text;
        }
        destroyFixInterval(el);
      } catch ( error ) {
        console.warn('gui-richtext', 'setDocumentData()', error.stack, error, '... trying again');
      }
      el._fixTry++;
    }, 100);
  }

  function setDocumentData(el, text) {
    destroyFixInterval(el);

    text = text || '';

    var wm = OSjs.Core.getWindowManager();
    var theme = (wm ? wm.getSetting('theme') : 'default') || 'default';
    var themeSrc = OSjs.API.getThemeCSS(theme);

    var editable = el.getAttribute('data-editable');
    editable = editable === null || editable === 'true';

    function onMouseDown(ev) {
      function insertTextAtCursor(text) {
        var sel, range, html;
        if (window.getSelection) {
          sel = window.getSelection();
          if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();
            range.insertNode( document.createTextNode(text) );
          }
        } else if (document.selection && document.selection.createRange) {
          document.selection.createRange().text = text;
        }
      }

      if ( ev.keyCode === 9 ) {
        /*
        var t = ev.shiftKey ? "outdent" : "indent";
        document.execCommand("styleWithCSS",true,null);
        document.execCommand(t, false, null)
        */
        insertTextAtCursor('\u00A0');
        ev.preventDefault();
      }
    }

    var script = onMouseDown.toString() + ';window.addEventListener("keydown", onMouseDown)';

    var template = '<!DOCTYPE html><link rel="stylesheet" type="text/css" href="' + themeSrc + '" />';
    if ( !editable ) {
      template = template.replace(' contentEditable="true"', '');
    }

    var doc = getDocument(el);
    doc.open();
    doc.write(template);
    doc.close();
    createFixInterval(el, doc, text);
  }

  /////////////////////////////////////////////////////////////////////////////
  // EXPORTS
  /////////////////////////////////////////////////////////////////////////////

  /**
   * Element: 'gui-richtext'
   *
   * "Richt text" input area.
   *
   * @api OSjs.GUI.Elements.gui-richtext
   * @class
   */
  GUI.Elements['gui-richtext'] = {
    bind: function(el, evName, callback, params) {
      if ( (['selection']).indexOf(evName) !== -1 ) {
        evName = '_' + evName;
      }
      Utils.$bind(el, evName, callback.bind(new GUI.Element(el)), params);
    },
    build: function(el) {
      var text = el.childNodes.length ? el.childNodes[0].nodeValue : '';

      Utils.$empty(el);

      var iframe = document.createElement('iframe');
      iframe.setAttribute('border', 0);
      iframe.onload = function() {
        iframe.contentWindow.addEventListener('selectstart', function() {
          el.dispatchEvent(new CustomEvent('_selection', {detail: {}}));
        });
        iframe.contentWindow.addEventListener('mouseup', function() {
          el.dispatchEvent(new CustomEvent('_selection', {detail: {}}));
        });
      };
      el.appendChild(iframe);

      setTimeout(function() {
        setDocumentData(el, text);
      }, 0);
    },
    call: function(el, method, args) {
      var doc = getDocument(el);
      if ( method === 'command' ) {
        if ( doc && doc.execCommand ) {
          return doc.execCommand.apply(doc, args);
        }
      } else if ( method === 'query' ) {
        if ( doc && doc.queryCommandValue ) {
          return doc.queryCommandValue.apply(doc, args);
        }
      }
      return null;
    },
    get: function(el, param, value) {
      if ( param === 'value' ) {
        return getDocumentData(el);
      }
      return GUI.Helpers.getProperty(el, param);
    },
    set: function(el, param, value) {
      if ( param === 'value' ) {
        setDocumentData(el, value);
        return true;
      }
      return false;
    }
  };

})(OSjs.API, OSjs.Utils, OSjs.VFS, OSjs.GUI);
<|endoftext|>


<commit_before>/*!
 * OS.js - JavaScript Cloud/Web Desktop Platform
 *
 * Copyright (c) 2011-2016, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */
(function(Utils, API) {
  'use strict';

  window.OSjs          = window.OSjs          || {};
  OSjs.VFS             = OSjs.VFS             || {};
  OSjs.VFS.Transports  = OSjs.VFS.Transports  || {};

  /////////////////////////////////////////////////////////////////////////////
  // API
  /////////////////////////////////////////////////////////////////////////////

  var internalTransport = {};

  internalTransport.scandir = function(item, callback, options) {
    OSjs.VFS.internalCall('scandir', {path: item.path}, function(error, result) {
      var list = [];
      if ( result ) {
        result = OSjs.VFS.filterScandir(result, options);
        result.forEach(function(iter) {
          list.push(new OSjs.VFS.File(iter));
        });
      }
      callback(error, list);
    });
  };

  internalTransport.write = function(item, data, callback, options) {
    options = options || {};
    options.onprogress = options.onprogress || function() {};

    function _write(dataSource) {
      var wopts = {path: item.path, data: dataSource};

      /*
      if ( API.getConfig('Connection.Type') === 'nw' ) {
        OSjs.Core.getHandler().nw.request(true, 'write', wopt, function(err, res) {
          callback(err, res);
        });
        return;
      }
      */

      OSjs.VFS.internalCall('write', wopts, callback, options);
    }

    if ( typeof data === 'string' && !data.length ) {
      _write(data);
      return;
    }

    OSjs.VFS.abToDataSource(data, item.mime, function(error, dataSource) {
      if ( error ) {
        callback(error);
        return;
      }

      _write(dataSource);
    });
  };

  internalTransport.read = function(item, callback, options) {

    if ( API.getConfig('Connection.Type') === 'nw' ) {
      OSjs.Core.getHandler().nw.request(true, 'read', {
        path: item.path,
        options: {raw: true}
      }, function(err, res) {
        callback(err, res);
      });
      return;
    }

    this.url(item, function(error, url) {
      if ( error ) {
        callback(error);
        return;
      }
      OSjs.VFS.internalCall('xhr', {url: url}, callback, options);
    });
  };

  internalTransport.copy = function(src, dest, callback) {
    OSjs.VFS.internalCall('copy', {src: src.path, dest: dest.path}, callback);
  };

  internalTransport.move = function(src, dest, callback) {
    OSjs.VFS.internalCall('move', {src: src.path, dest: dest.path}, callback);
  };

  internalTransport.unlink = function(item, callback) {
    OSjs.VFS.internalCall('delete', {path: item.path}, callback);
  };

  internalTransport.mkdir = function(item, callback) {
    OSjs.VFS.internalCall('mkdir', {path: item.path}, callback);
  };

  internalTransport.exists = function(item, callback) {
    OSjs.VFS.internalCall('exists', {path: item.path}, callback);
  };

  internalTransport.fileinfo = function(item, callback) {
    OSjs.VFS.internalCall('fileinfo', {path: item.path}, callback);
  };

  internalTransport.url = function(item, callback) {
    callback(false, OSjs.VFS.Transports.Internal.path(item));
  };

  internalTransport.trash = function(item, callback) {
    callback(API._('ERR_VFS_UNAVAILABLE'));
  };

  internalTransport.untrash = function(item, callback) {
    callback(API._('ERR_VFS_UNAVAILABLE'));
  };

  internalTransport.emptyTrash = function(item, callback) {
    callback(API._('ERR_VFS_UNAVAILABLE'));
  };

  /////////////////////////////////////////////////////////////////////////////
  // WRAPPERS
  /////////////////////////////////////////////////////////////////////////////

  function makeRequest(name, args, callback, options) {
    args = args || [];
    callback = callback || {};

    if ( !internalTransport[name] ) {
      throw new Error('Invalid internalTransport API call name');
    }

    var fargs = args;
    fargs.push(callback);
    fargs.push(options);
    internalTransport[name].apply(internalTransport, fargs);
  }

  /////////////////////////////////////////////////////////////////////////////
  // EXPORTS
  /////////////////////////////////////////////////////////////////////////////

  /**
   * This is the WebDAV VFS Module wrapper
   *
   * @api OSjs.VFS.Transports.WebDAV
   */
  OSjs.VFS.Transports.Internal = {
    request: makeRequest,
    path: function(input) {
      var base = API.getConfig('Connection.FSURI', '/');
      if ( input ) {
        var path = typeof input === 'string' ? input : input.path;
        return base + '/get' + encodeURIComponent(path);
      }
      return base + '/upload';
    }
  };

})(OSjs.Utils, OSjs.API);
<commit_msg>VFS: Remove encode URI on URL generator<commit_after>/*!
 * OS.js - JavaScript Cloud/Web Desktop Platform
 *
 * Copyright (c) 2011-2016, Anders Evenrud <andersevenrud@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author  Anders Evenrud <andersevenrud@gmail.com>
 * @licence Simplified BSD License
 */
(function(Utils, API) {
  'use strict';

  window.OSjs          = window.OSjs          || {};
  OSjs.VFS             = OSjs.VFS             || {};
  OSjs.VFS.Transports  = OSjs.VFS.Transports  || {};

  /////////////////////////////////////////////////////////////////////////////
  // API
  /////////////////////////////////////////////////////////////////////////////

  var internalTransport = {};

  internalTransport.scandir = function(item, callback, options) {
    OSjs.VFS.internalCall('scandir', {path: item.path}, function(error, result) {
      var list = [];
      if ( result ) {
        result = OSjs.VFS.filterScandir(result, options);
        result.forEach(function(iter) {
          list.push(new OSjs.VFS.File(iter));
        });
      }
      callback(error, list);
    });
  };

  internalTransport.write = function(item, data, callback, options) {
    options = options || {};
    options.onprogress = options.onprogress || function() {};

    function _write(dataSource) {
      var wopts = {path: item.path, data: dataSource};

      /*
      if ( API.getConfig('Connection.Type') === 'nw' ) {
        OSjs.Core.getHandler().nw.request(true, 'write', wopt, function(err, res) {
          callback(err, res);
        });
        return;
      }
      */

      OSjs.VFS.internalCall('write', wopts, callback, options);
    }

    if ( typeof data === 'string' && !data.length ) {
      _write(data);
      return;
    }

    OSjs.VFS.abToDataSource(data, item.mime, function(error, dataSource) {
      if ( error ) {
        callback(error);
        return;
      }

      _write(dataSource);
    });
  };

  internalTransport.read = function(item, callback, options) {

    if ( API.getConfig('Connection.Type') === 'nw' ) {
      OSjs.Core.getHandler().nw.request(true, 'read', {
        path: item.path,
        options: {raw: true}
      }, function(err, res) {
        callback(err, res);
      });
      return;
    }

    this.url(item, function(error, url) {
      if ( error ) {
        callback(error);
        return;
      }
      OSjs.VFS.internalCall('xhr', {url: url}, callback, options);
    });
  };

  internalTransport.copy = function(src, dest, callback) {
    OSjs.VFS.internalCall('copy', {src: src.path, dest: dest.path}, callback);
  };

  internalTransport.move = function(src, dest, callback) {
    OSjs.VFS.internalCall('move', {src: src.path, dest: dest.path}, callback);
  };

  internalTransport.unlink = function(item, callback) {
    OSjs.VFS.internalCall('delete', {path: item.path}, callback);
  };

  internalTransport.mkdir = function(item, callback) {
    OSjs.VFS.internalCall('mkdir', {path: item.path}, callback);
  };

  internalTransport.exists = function(item, callback) {
    OSjs.VFS.internalCall('exists', {path: item.path}, callback);
  };

  internalTransport.fileinfo = function(item, callback) {
    OSjs.VFS.internalCall('fileinfo', {path: item.path}, callback);
  };

  internalTransport.url = function(item, callback) {
    callback(false, OSjs.VFS.Transports.Internal.path(item));
  };

  internalTransport.trash = function(item, callback) {
    callback(API._('ERR_VFS_UNAVAILABLE'));
  };

  internalTransport.untrash = function(item, callback) {
    callback(API._('ERR_VFS_UNAVAILABLE'));
  };

  internalTransport.emptyTrash = function(item, callback) {
    callback(API._('ERR_VFS_UNAVAILABLE'));
  };

  /////////////////////////////////////////////////////////////////////////////
  // WRAPPERS
  /////////////////////////////////////////////////////////////////////////////

  function makeRequest(name, args, callback, options) {
    args = args || [];
    callback = callback || {};

    if ( !internalTransport[name] ) {
      throw new Error('Invalid internalTransport API call name');
    }

    var fargs = args;
    fargs.push(callback);
    fargs.push(options);
    internalTransport[name].apply(internalTransport, fargs);
  }

  /////////////////////////////////////////////////////////////////////////////
  // EXPORTS
  /////////////////////////////////////////////////////////////////////////////

  /**
   * This is the WebDAV VFS Module wrapper
   *
   * @api OSjs.VFS.Transports.WebDAV
   */
  OSjs.VFS.Transports.Internal = {
    request: makeRequest,
    path: function(input) {
      var base = API.getConfig('Connection.FSURI', '/');
      if ( input ) {
        var path = typeof input === 'string' ? input : input.path;
        return base + '/get' + path;
      }
      return base + '/upload';
    }
  };

})(OSjs.Utils, OSjs.API);
<|endoftext|>


<commit_before>// Copyright 2018-2022, University of Colorado Boulder

/**
 * General dialog type. Migrated from Joist on 4/10/2018
 *
 * @author Jonathan Olson (PhET Interactive Simulations)
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Andrea Lin (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */

import Property from '../../axon/js/Property.js';
import ScreenView from '../../joist/js/ScreenView.js';
import getGlobal from '../../phet-core/js/getGlobal.js';
import merge from '../../phet-core/js/merge.js';
import CloseButton from '../../scenery-phet/js/buttons/CloseButton.js';
import { AlignBox, FocusManager, FullScreen, HBox, KeyboardUtils, Node, PDOMPeer, PDOMUtils, VBox } from '../../scenery/js/imports.js';
import generalCloseSoundPlayer from '../../tambo/js/shared-sound-players/generalCloseSoundPlayer.js';
import generalOpenSoundPlayer from '../../tambo/js/shared-sound-players/generalOpenSoundPlayer.js';
import nullSoundPlayer from '../../tambo/js/shared-sound-players/nullSoundPlayer.js';
import PhetioObject from '../../tandem/js/PhetioObject.js';
import Tandem from '../../tandem/js/Tandem.js';
import DynamicMarkerIO from '../../tandem/js/types/DynamicMarkerIO.js';
import IOType from '../../tandem/js/types/IOType.js';
import ButtonNode from './buttons/ButtonNode.js';
import Panel from './Panel.js';
import Popupable from './Popupable.js';
import sun from './sun.js';
import sunStrings from './sunStrings.js';

class Dialog extends Popupable( Panel ) {

  /**
   * @param {Node} content - The content to display inside the dialog (not including the title)
   * @param {Object} [options]
   */
  constructor( content, options ) {

    options = merge( {

      /* Margins and spacing:

                                    maxHeightMargin
       ____________________________________________________________________________
      |                                     |                          |           |
      |                                     |                          closeButton |
      |                                     topMargin                  TopMargin   |
      |                                     |                         _|___        |
      |                  ___________________|____________________    |     |       |
    m |--------l--------|                                        |-x-|  X  |---c---| m
    a |        e        |   Title                                | S |_____|   l   | a
    x |        f        |________________________________________| P           o   | x
    W |        t        |   |                                    | a           s   | W
    i |        M        |   ySpacing                             | c           e   | i
    d |        a        |___|____________________________________| i           B   | d
    t |        r        |                                        | n           u   | t
    h |        g        |   Content                              | g           t   | h
    M |        i        |                                        |             t   | M
    a |        n        |                                        |             o   | a
    r |                 |                                        |             n   | r
    g |                 |                                        |             R   | g
    i |                 |                                        |             i   | i
    n |                 |                                        |             g   | n
      |                 |                                        |             h   |
      |                 |                                        |             M   |
      |                 |________________________________________|             a   |
      |                                     |                                  r   |
      |                                     |                                  g   |
      |                                     bottomMargin                       i   |
      |                                     |                                  n   |
      |_____________________________________|______________________________________|

                                        maxHeightMargin
       */

      xSpacing: 10, // {number} how far the title and content is placed to the left of the close button
      ySpacing: 10, // {number} vertical space between title and content
      topMargin: 15, // {number} margin above content, or above title if provided
      bottomMargin: 15, // {number} margin below content
      leftMargin: null, // {number|null} margin to the left of the content.  If null, this is computed so that we have
      // the same margins on the left and right of the content.
      maxWidthMargin: 12, // {number} the margin between the left/right of the layoutBounds and the dialog, ignored if maxWidth is specified
      maxHeightMargin: 12, // {number} the margin between the top/bottom of the layoutBounds and the dialog, ignored if maxHeight is specified
      closeButtonLength: 18.2, // {number} width of the close button
      closeButtonTopMargin: 10, // {number} margin above the close button
      closeButtonRightMargin: 10, // {number} margin to the right of the close button

      // {Bounds2|null}
      layoutBounds: ScreenView.DEFAULT_LAYOUT_BOUNDS,

      // more Dialog-specific options
      // {Node|null} Title to be displayed at top. For a11y, make sure that its primary sibling has an accessible name
      title: null,
      titleAlign: 'center', // horizontal alignment of the title: {string} left, right or center

      // {function(Dialog,simBounds:Bounds2,screenBounds:Bounds2,scale:number)} which sets the dialog's position in
      // global coordinates. By default it will center the dialog in the middle of the current screen (not including nav bar).
      layoutStrategy: defaultLayoutStrategy,

      // close button options
      closeButtonListener: () => this.hide(),
      closeButtonColor: 'black', // {Color|null} color for the close button 'X'
      closeButtonTouchAreaXDilation: 0,
      closeButtonTouchAreaYDilation: 0,
      closeButtonMouseAreaXDilation: 0,
      closeButtonMouseAreaYDilation: 0,

      // {function|null} called after the dialog is shown, see https://github.com/phetsims/joist/issues/478
      showCallback: null,

      // {function|null} called after the dialog is hidden, see https://github.com/phetsims/joist/issues/478
      hideCallback: null,

      // {Node|null} - The Node that receives focus when the Dialog is shown. If null then Dialog will focus
      // the CloseButton.
      focusOnShowNode: null,

      // pass through to Panel options
      cornerRadius: 10, // {number} radius of the dialog's corners
      resize: true, // {boolean} whether to resize if content's size changes
      fill: 'white', // {string|Color}
      stroke: 'black', // {string|Color}
      backgroundPickable: true,
      maxHeight: null, // if not provided, then dynamically calculate based on the layoutBounds of the current screen, see updateLayoutMultilink
      maxWidth: null, // if not provided, then dynamically calculate based on the layoutBounds of the current screen, see updateLayoutMultilink

      // phet-io
      tandem: Tandem.OPTIONAL,
      phetioType: Dialog.DialogIO,
      phetioReadOnly: PhetioObject.DEFAULT_OPTIONS.phetioReadOnly, // default to false so it can pass it through to the close button
      phetioState: PhetioObject.DEFAULT_OPTIONS.phetioState,
      visiblePropertyOptions: {
        phetioReadOnly: false // Dialog's visibility is controlled by Popupable.isShowingProperty
      },

      // {ISoundPlayer} - sound generation
      openedSoundPlayer: generalOpenSoundPlayer,
      closedSoundPlayer: generalCloseSoundPlayer,

      sim: getGlobal( 'phet.joist.sim' ),

      // pdom options
      tagName: 'div',
      ariaRole: 'dialog',

      // {boolean} - By default, the close button is placed first in the PDOMOrder (and thus the focus order), set this if you want
      // the close button to be the last element in the focus order for the Dialog.
      closeButtonLastInPDOM: false,

      // By default set the accessible name of this dialog to be the content of the title. Some dialogs want to opt out
      // of providing the default accessible name for the dialog, opting to instead manage the accessible name
      // themselves, for example see KeyboardHelpDialog and https://github.com/phetsims/scenery-phet/issues/494
      addAriaLabelledByFromTitle: true
    }, options );

    assert && assert( options.sim, 'sim must be provided, as Dialog needs a Sim instance' );

    assert && assert( options.xMargin === undefined, 'Dialog sets xMargin' );
    options.xMargin = 0;
    assert && assert( options.yMargin === undefined, 'Dialog sets yMargin' );
    options.yMargin = 0;

    // if left margin is specified in options, use it. otherwise, set it to make the left right gutters symmetrical
    if ( options.leftMargin === null ) {
      options.leftMargin = options.xSpacing + options.closeButtonLength + options.closeButtonRightMargin;
    }

    assert && assert( options.maxHeight === null || typeof options.maxHeight === 'number' );
    assert && assert( options.maxWidth === null || typeof options.maxWidth === 'number' );

    // Apply maxWidth/maxHeight depending on the margins and layoutBounds
    if ( !options.maxWidth && options.layoutBounds ) {
      options.maxWidth = applyDoubleMargin( options.layoutBounds.width, options.maxWidthMargin );
    }
    if ( !options.maxHeight && options.layoutBounds ) {
      options.maxHeight = applyDoubleMargin( options.layoutBounds.height, options.maxHeightMargin );
    }

    // create close button - a flat "X"
    const closeButton = new CloseButton( {
      iconLength: options.closeButtonLength,
      baseColor: 'transparent',
      buttonAppearanceStrategy: ButtonNode.FlatAppearanceStrategy,

      // no margins since the flat X takes up all the space
      xMargin: 0,
      yMargin: 0,

      listener: () => {
        options.closeButtonListener();
      },

      pathOptions: {
        stroke: options.closeButtonColor
      },

      // phet-io
      tandem: options.tandem.createTandem( 'closeButton' ),
      phetioReadOnly: options.phetioReadOnly, // match the readOnly of the Dialog

      phetioState: false, // close button should not be in state

      // dialog close buttons by default do not have a featured visibleProperty
      visiblePropertyOptions: {
        phetioFeatured: false,
        phetioState: false
      },

      // dialog close buttons by default do not have a featured enabledProperty
      enabledPropertyOptions: { phetioFeatured: false },

      // turn off default sound generation, Dialog will create its own sounds
      soundPlayer: nullSoundPlayer,

      // pdom
      tagName: 'button',
      innerContent: sunStrings.a11y.close
    } );

    // touch/mouse areas for the close button
    closeButton.touchArea = closeButton.bounds.dilatedXY(
      options.closeButtonTouchAreaXDilation,
      options.closeButtonTouchAreaYDilation
    );
    closeButton.mouseArea = closeButton.bounds.dilatedXY(
      options.closeButtonMouseAreaXDilation,
      options.closeButtonMouseAreaYDilation
    );

    // pdom - set the order of content, close button first so remaining content can be read from top to bottom
    // with virtual cursor
    let pdomOrder = [ options.title, content ];
    options.closeButtonLastInPDOM ? pdomOrder.push( closeButton ) : pdomOrder.unshift( closeButton );
    pdomOrder = pdomOrder.filter( node => node !== undefined && node !== null );

    // pdom - fall back to focusing the closeButton by default if there is no focusOnShowNode or the
    // content is not focusable
    options.focusOnShowNode = options.focusOnShowNode ? options.focusOnShowNode :
                              pdomOrder[ 0 ].focusable ? pdomOrder[ 0 ] :
                              closeButton;

    assert && assert( options.focusOnShowNode instanceof Node, 'should be non-null and defined' );
    assert && assert( options.focusOnShowNode.focusable, 'focusOnShowNode must be focusable.' );

    // Align content, title, and close button using spacing and margin options

    // align content and title (if provided) vertically
    const contentAndTitle = new VBox( {
      children: options.title ? [ options.title, content ] : [ content ],
      spacing: options.ySpacing,
      align: options.titleAlign
    } );

    // add topMargin, bottomMargin, and leftMargin
    const contentAndTitleWithMargins = new AlignBox( contentAndTitle, {
      topMargin: options.topMargin,
      bottomMargin: options.bottomMargin,
      leftMargin: options.leftMargin
    } );

    // add closeButtonTopMargin and closeButtonRightMargin
    const closeButtonWithMargins = new AlignBox( closeButton, {
      topMargin: options.closeButtonTopMargin,
      rightMargin: options.closeButtonRightMargin
    } );

    // create content for Panel
    const dialogContent = new HBox( {
      children: [ contentAndTitleWithMargins, closeButtonWithMargins ],
      spacing: options.xSpacing,
      align: 'top'
    } );

    super( dialogContent, options );

    // The Dialog's display runs on this Property, so add the listener that controls show/hide.
    this.isShowingProperty.lazyLink( isShowing => {
      if ( isShowing ) {
        // sound generation
        options.openedSoundPlayer.play();

        // pdom - modal dialogs should be the only readable content in the sim
        // TODO: https://github.com/phetsims/joist/issues/293 non-modal dialogs shouldn't hide other accessible content,
        // and this should be dependant on other things in the sim modalNodeStack
        this.sim.setAccessibleViewsVisible( false );

        // Do this last
        options.showCallback && options.showCallback();
      }
      else {
        // sound generation
        options.closedSoundPlayer.play();

        // pdom - when the dialog is hidden, make all ScreenView content visible to assistive technology
        this.sim.setAccessibleViewsVisible( true );

        // Do this last
        options.hideCallback && options.hideCallback();
      }
    } );

    // @private {Bounds2|null}
    this.layoutBounds = options.layoutBounds;

    // @private (a11y)
    this.closeButton = closeButton;

    // @private {Sim}
    this.sim = options.sim;

    this.updateLayoutMultilink = Property.multilink( [
      this.sim.boundsProperty,
      this.sim.screenBoundsProperty,
      this.sim.scaleProperty,
      this.sim.screenProperty,
      this.isShowingProperty
    ], ( bounds, screenBounds, scale, screen ) => {
      if ( bounds && screenBounds && scale ) {
        options.layoutStrategy( this, bounds, screenBounds, scale );
      }
    } );

    // Setter after the super call
    this.pdomOrder = pdomOrder;

    // pdom - set the aria-labelledby relation so that whenever focus enters the dialog the title is read
    if ( options.title && options.title.tagName && options.addAriaLabelledByFromTitle ) {
      this.addAriaLabelledbyAssociation( {
        thisElementName: PDOMPeer.PRIMARY_SIBLING,
        otherNode: options.title,
        otherElementName: PDOMPeer.PRIMARY_SIBLING
      } );
    }

    // pdom - close the dialog when pressing "escape"
    const escapeListener = {
      keydown: event => {
        const domEvent = event.domEvent;

        if ( KeyboardUtils.isKeyEvent( event.domEvent, KeyboardUtils.KEY_ESCAPE ) ) {
          domEvent.preventDefault();
          this.hide();
        }
        else if ( KeyboardUtils.isKeyEvent( event.domEvent, KeyboardUtils.KEY_TAB ) && FullScreen.isFullScreen() ) {

          // prevent a particular bug in Windows 7/8.1 Firefox where focus gets trapped in the document
          // when the navigation bar is hidden and there is only one focusable element in the DOM
          // see https://bugzilla.mozilla.org/show_bug.cgi?id=910136
          const activeId = FocusManager.pdomFocus.trail.getUniqueId();
          const noNextFocusable = PDOMUtils.getNextFocusable().id === activeId;
          const noPreviousFocusable = PDOMUtils.getPreviousFocusable().id === activeId;

          if ( noNextFocusable && noPreviousFocusable ) {
            domEvent.preventDefault();
          }
        }
      }
    };
    this.addInputListener( escapeListener );

    // @private - to be called on dispose()
    this.disposeDialog = () => {
      this.updateLayoutMultilink.dispose();
      this.removeInputListener( escapeListener );

      closeButton.dispose();

      // remove dialog content from scene graph, but don't dispose because Panel
      // needs to remove listeners on the content in its dispose()
      dialogContent.removeAllChildren();
      dialogContent.detach();
    };
  }

  /**
   * @public
   * @override
   */
  dispose() {
    this.disposeDialog();
    super.dispose();
  }
}

// Default value for options.layoutStrategy, centers the Dialog in the layoutBounds
function defaultLayoutStrategy( dialog, simBounds, screenBounds, scale ) {
  if ( dialog.layoutBounds ) {
    dialog.center = dialog.layoutBounds.center;
  }
}

function applyDoubleMargin( dimension, margin ) {
  return ( dimension > margin * 2 ) ? ( dimension - margin * 2 ) : dimension;
}

Dialog.DialogIO = new IOType( 'DialogIO', {
  valueType: Dialog,

  // Since many Dialogs are dynamic elements, these need to be in the state. The value of the state object doesn't
  // matter, but it instead just serves as a marker to tell the state engine to recreate the Dialog (if dynamic) when
  // setting state.
  supertype: DynamicMarkerIO
} );

sun.register( 'Dialog', Dialog );
export default Dialog;<commit_msg>Dialog visibleProperty is readonly, https://github.com/phetsims/joist/issues/799<commit_after>// Copyright 2018-2022, University of Colorado Boulder

/**
 * General dialog type. Migrated from Joist on 4/10/2018
 *
 * @author Jonathan Olson (PhET Interactive Simulations)
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Andrea Lin (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */

import Property from '../../axon/js/Property.js';
import ScreenView from '../../joist/js/ScreenView.js';
import getGlobal from '../../phet-core/js/getGlobal.js';
import merge from '../../phet-core/js/merge.js';
import CloseButton from '../../scenery-phet/js/buttons/CloseButton.js';
import { AlignBox, FocusManager, FullScreen, HBox, KeyboardUtils, Node, PDOMPeer, PDOMUtils, VBox } from '../../scenery/js/imports.js';
import generalCloseSoundPlayer from '../../tambo/js/shared-sound-players/generalCloseSoundPlayer.js';
import generalOpenSoundPlayer from '../../tambo/js/shared-sound-players/generalOpenSoundPlayer.js';
import nullSoundPlayer from '../../tambo/js/shared-sound-players/nullSoundPlayer.js';
import PhetioObject from '../../tandem/js/PhetioObject.js';
import Tandem from '../../tandem/js/Tandem.js';
import DynamicMarkerIO from '../../tandem/js/types/DynamicMarkerIO.js';
import IOType from '../../tandem/js/types/IOType.js';
import ButtonNode from './buttons/ButtonNode.js';
import Panel from './Panel.js';
import Popupable from './Popupable.js';
import sun from './sun.js';
import sunStrings from './sunStrings.js';

class Dialog extends Popupable( Panel ) {

  /**
   * @param {Node} content - The content to display inside the dialog (not including the title)
   * @param {Object} [options]
   */
  constructor( content, options ) {

    options = merge( {

      /* Margins and spacing:

                                    maxHeightMargin
       ____________________________________________________________________________
      |                                     |                          |           |
      |                                     |                          closeButton |
      |                                     topMargin                  TopMargin   |
      |                                     |                         _|___        |
      |                  ___________________|____________________    |     |       |
    m |--------l--------|                                        |-x-|  X  |---c---| m
    a |        e        |   Title                                | S |_____|   l   | a
    x |        f        |________________________________________| P           o   | x
    W |        t        |   |                                    | a           s   | W
    i |        M        |   ySpacing                             | c           e   | i
    d |        a        |___|____________________________________| i           B   | d
    t |        r        |                                        | n           u   | t
    h |        g        |   Content                              | g           t   | h
    M |        i        |                                        |             t   | M
    a |        n        |                                        |             o   | a
    r |                 |                                        |             n   | r
    g |                 |                                        |             R   | g
    i |                 |                                        |             i   | i
    n |                 |                                        |             g   | n
      |                 |                                        |             h   |
      |                 |                                        |             M   |
      |                 |________________________________________|             a   |
      |                                     |                                  r   |
      |                                     |                                  g   |
      |                                     bottomMargin                       i   |
      |                                     |                                  n   |
      |_____________________________________|______________________________________|

                                        maxHeightMargin
       */

      xSpacing: 10, // {number} how far the title and content is placed to the left of the close button
      ySpacing: 10, // {number} vertical space between title and content
      topMargin: 15, // {number} margin above content, or above title if provided
      bottomMargin: 15, // {number} margin below content
      leftMargin: null, // {number|null} margin to the left of the content.  If null, this is computed so that we have
      // the same margins on the left and right of the content.
      maxWidthMargin: 12, // {number} the margin between the left/right of the layoutBounds and the dialog, ignored if maxWidth is specified
      maxHeightMargin: 12, // {number} the margin between the top/bottom of the layoutBounds and the dialog, ignored if maxHeight is specified
      closeButtonLength: 18.2, // {number} width of the close button
      closeButtonTopMargin: 10, // {number} margin above the close button
      closeButtonRightMargin: 10, // {number} margin to the right of the close button

      // {Bounds2|null}
      layoutBounds: ScreenView.DEFAULT_LAYOUT_BOUNDS,

      // more Dialog-specific options
      // {Node|null} Title to be displayed at top. For a11y, make sure that its primary sibling has an accessible name
      title: null,
      titleAlign: 'center', // horizontal alignment of the title: {string} left, right or center

      // {function(Dialog,simBounds:Bounds2,screenBounds:Bounds2,scale:number)} which sets the dialog's position in
      // global coordinates. By default it will center the dialog in the middle of the current screen (not including nav bar).
      layoutStrategy: defaultLayoutStrategy,

      // close button options
      closeButtonListener: () => this.hide(),
      closeButtonColor: 'black', // {Color|null} color for the close button 'X'
      closeButtonTouchAreaXDilation: 0,
      closeButtonTouchAreaYDilation: 0,
      closeButtonMouseAreaXDilation: 0,
      closeButtonMouseAreaYDilation: 0,

      // {function|null} called after the dialog is shown, see https://github.com/phetsims/joist/issues/478
      showCallback: null,

      // {function|null} called after the dialog is hidden, see https://github.com/phetsims/joist/issues/478
      hideCallback: null,

      // {Node|null} - The Node that receives focus when the Dialog is shown. If null then Dialog will focus
      // the CloseButton.
      focusOnShowNode: null,

      // pass through to Panel options
      cornerRadius: 10, // {number} radius of the dialog's corners
      resize: true, // {boolean} whether to resize if content's size changes
      fill: 'white', // {string|Color}
      stroke: 'black', // {string|Color}
      backgroundPickable: true,
      maxHeight: null, // if not provided, then dynamically calculate based on the layoutBounds of the current screen, see updateLayoutMultilink
      maxWidth: null, // if not provided, then dynamically calculate based on the layoutBounds of the current screen, see updateLayoutMultilink

      // phet-io
      tandem: Tandem.OPTIONAL,
      phetioType: Dialog.DialogIO,
      phetioReadOnly: PhetioObject.DEFAULT_OPTIONS.phetioReadOnly, // default to false so it can pass it through to the close button
      phetioState: PhetioObject.DEFAULT_OPTIONS.phetioState,
      visiblePropertyOptions: {
        phetioReadOnly: true // Dialog's visibility is controlled by Popupable.isShowingProperty
      },

      // {ISoundPlayer} - sound generation
      openedSoundPlayer: generalOpenSoundPlayer,
      closedSoundPlayer: generalCloseSoundPlayer,

      sim: getGlobal( 'phet.joist.sim' ),

      // pdom options
      tagName: 'div',
      ariaRole: 'dialog',

      // {boolean} - By default, the close button is placed first in the PDOMOrder (and thus the focus order), set this if you want
      // the close button to be the last element in the focus order for the Dialog.
      closeButtonLastInPDOM: false,

      // By default set the accessible name of this dialog to be the content of the title. Some dialogs want to opt out
      // of providing the default accessible name for the dialog, opting to instead manage the accessible name
      // themselves, for example see KeyboardHelpDialog and https://github.com/phetsims/scenery-phet/issues/494
      addAriaLabelledByFromTitle: true
    }, options );

    assert && assert( options.sim, 'sim must be provided, as Dialog needs a Sim instance' );

    assert && assert( options.xMargin === undefined, 'Dialog sets xMargin' );
    options.xMargin = 0;
    assert && assert( options.yMargin === undefined, 'Dialog sets yMargin' );
    options.yMargin = 0;

    // if left margin is specified in options, use it. otherwise, set it to make the left right gutters symmetrical
    if ( options.leftMargin === null ) {
      options.leftMargin = options.xSpacing + options.closeButtonLength + options.closeButtonRightMargin;
    }

    assert && assert( options.maxHeight === null || typeof options.maxHeight === 'number' );
    assert && assert( options.maxWidth === null || typeof options.maxWidth === 'number' );

    // Apply maxWidth/maxHeight depending on the margins and layoutBounds
    if ( !options.maxWidth && options.layoutBounds ) {
      options.maxWidth = applyDoubleMargin( options.layoutBounds.width, options.maxWidthMargin );
    }
    if ( !options.maxHeight && options.layoutBounds ) {
      options.maxHeight = applyDoubleMargin( options.layoutBounds.height, options.maxHeightMargin );
    }

    // create close button - a flat "X"
    const closeButton = new CloseButton( {
      iconLength: options.closeButtonLength,
      baseColor: 'transparent',
      buttonAppearanceStrategy: ButtonNode.FlatAppearanceStrategy,

      // no margins since the flat X takes up all the space
      xMargin: 0,
      yMargin: 0,

      listener: () => {
        options.closeButtonListener();
      },

      pathOptions: {
        stroke: options.closeButtonColor
      },

      // phet-io
      tandem: options.tandem.createTandem( 'closeButton' ),
      phetioReadOnly: options.phetioReadOnly, // match the readOnly of the Dialog

      phetioState: false, // close button should not be in state

      // dialog close buttons by default do not have a featured visibleProperty
      visiblePropertyOptions: {
        phetioFeatured: false,
        phetioState: false
      },

      // dialog close buttons by default do not have a featured enabledProperty
      enabledPropertyOptions: { phetioFeatured: false },

      // turn off default sound generation, Dialog will create its own sounds
      soundPlayer: nullSoundPlayer,

      // pdom
      tagName: 'button',
      innerContent: sunStrings.a11y.close
    } );

    // touch/mouse areas for the close button
    closeButton.touchArea = closeButton.bounds.dilatedXY(
      options.closeButtonTouchAreaXDilation,
      options.closeButtonTouchAreaYDilation
    );
    closeButton.mouseArea = closeButton.bounds.dilatedXY(
      options.closeButtonMouseAreaXDilation,
      options.closeButtonMouseAreaYDilation
    );

    // pdom - set the order of content, close button first so remaining content can be read from top to bottom
    // with virtual cursor
    let pdomOrder = [ options.title, content ];
    options.closeButtonLastInPDOM ? pdomOrder.push( closeButton ) : pdomOrder.unshift( closeButton );
    pdomOrder = pdomOrder.filter( node => node !== undefined && node !== null );

    // pdom - fall back to focusing the closeButton by default if there is no focusOnShowNode or the
    // content is not focusable
    options.focusOnShowNode = options.focusOnShowNode ? options.focusOnShowNode :
                              pdomOrder[ 0 ].focusable ? pdomOrder[ 0 ] :
                              closeButton;

    assert && assert( options.focusOnShowNode instanceof Node, 'should be non-null and defined' );
    assert && assert( options.focusOnShowNode.focusable, 'focusOnShowNode must be focusable.' );

    // Align content, title, and close button using spacing and margin options

    // align content and title (if provided) vertically
    const contentAndTitle = new VBox( {
      children: options.title ? [ options.title, content ] : [ content ],
      spacing: options.ySpacing,
      align: options.titleAlign
    } );

    // add topMargin, bottomMargin, and leftMargin
    const contentAndTitleWithMargins = new AlignBox( contentAndTitle, {
      topMargin: options.topMargin,
      bottomMargin: options.bottomMargin,
      leftMargin: options.leftMargin
    } );

    // add closeButtonTopMargin and closeButtonRightMargin
    const closeButtonWithMargins = new AlignBox( closeButton, {
      topMargin: options.closeButtonTopMargin,
      rightMargin: options.closeButtonRightMargin
    } );

    // create content for Panel
    const dialogContent = new HBox( {
      children: [ contentAndTitleWithMargins, closeButtonWithMargins ],
      spacing: options.xSpacing,
      align: 'top'
    } );

    super( dialogContent, options );

    // The Dialog's display runs on this Property, so add the listener that controls show/hide.
    this.isShowingProperty.lazyLink( isShowing => {
      if ( isShowing ) {
        // sound generation
        options.openedSoundPlayer.play();

        // pdom - modal dialogs should be the only readable content in the sim
        // TODO: https://github.com/phetsims/joist/issues/293 non-modal dialogs shouldn't hide other accessible content,
        // and this should be dependant on other things in the sim modalNodeStack
        this.sim.setAccessibleViewsVisible( false );

        // Do this last
        options.showCallback && options.showCallback();
      }
      else {
        // sound generation
        options.closedSoundPlayer.play();

        // pdom - when the dialog is hidden, make all ScreenView content visible to assistive technology
        this.sim.setAccessibleViewsVisible( true );

        // Do this last
        options.hideCallback && options.hideCallback();
      }
    } );

    // @private {Bounds2|null}
    this.layoutBounds = options.layoutBounds;

    // @private (a11y)
    this.closeButton = closeButton;

    // @private {Sim}
    this.sim = options.sim;

    this.updateLayoutMultilink = Property.multilink( [
      this.sim.boundsProperty,
      this.sim.screenBoundsProperty,
      this.sim.scaleProperty,
      this.sim.screenProperty,
      this.isShowingProperty
    ], ( bounds, screenBounds, scale, screen ) => {
      if ( bounds && screenBounds && scale ) {
        options.layoutStrategy( this, bounds, screenBounds, scale );
      }
    } );

    // Setter after the super call
    this.pdomOrder = pdomOrder;

    // pdom - set the aria-labelledby relation so that whenever focus enters the dialog the title is read
    if ( options.title && options.title.tagName && options.addAriaLabelledByFromTitle ) {
      this.addAriaLabelledbyAssociation( {
        thisElementName: PDOMPeer.PRIMARY_SIBLING,
        otherNode: options.title,
        otherElementName: PDOMPeer.PRIMARY_SIBLING
      } );
    }

    // pdom - close the dialog when pressing "escape"
    const escapeListener = {
      keydown: event => {
        const domEvent = event.domEvent;

        if ( KeyboardUtils.isKeyEvent( event.domEvent, KeyboardUtils.KEY_ESCAPE ) ) {
          domEvent.preventDefault();
          this.hide();
        }
        else if ( KeyboardUtils.isKeyEvent( event.domEvent, KeyboardUtils.KEY_TAB ) && FullScreen.isFullScreen() ) {

          // prevent a particular bug in Windows 7/8.1 Firefox where focus gets trapped in the document
          // when the navigation bar is hidden and there is only one focusable element in the DOM
          // see https://bugzilla.mozilla.org/show_bug.cgi?id=910136
          const activeId = FocusManager.pdomFocus.trail.getUniqueId();
          const noNextFocusable = PDOMUtils.getNextFocusable().id === activeId;
          const noPreviousFocusable = PDOMUtils.getPreviousFocusable().id === activeId;

          if ( noNextFocusable && noPreviousFocusable ) {
            domEvent.preventDefault();
          }
        }
      }
    };
    this.addInputListener( escapeListener );

    // @private - to be called on dispose()
    this.disposeDialog = () => {
      this.updateLayoutMultilink.dispose();
      this.removeInputListener( escapeListener );

      closeButton.dispose();

      // remove dialog content from scene graph, but don't dispose because Panel
      // needs to remove listeners on the content in its dispose()
      dialogContent.removeAllChildren();
      dialogContent.detach();
    };
  }

  /**
   * @public
   * @override
   */
  dispose() {
    this.disposeDialog();
    super.dispose();
  }
}

// Default value for options.layoutStrategy, centers the Dialog in the layoutBounds
function defaultLayoutStrategy( dialog, simBounds, screenBounds, scale ) {
  if ( dialog.layoutBounds ) {
    dialog.center = dialog.layoutBounds.center;
  }
}

function applyDoubleMargin( dimension, margin ) {
  return ( dimension > margin * 2 ) ? ( dimension - margin * 2 ) : dimension;
}

Dialog.DialogIO = new IOType( 'DialogIO', {
  valueType: Dialog,

  // Since many Dialogs are dynamic elements, these need to be in the state. The value of the state object doesn't
  // matter, but it instead just serves as a marker to tell the state engine to recreate the Dialog (if dynamic) when
  // setting state.
  supertype: DynamicMarkerIO
} );

sun.register( 'Dialog', Dialog );
export default Dialog;<|endoftext|>


<commit_before>/**
 * icu-converter
 * https://github.com/alex-dow/icu-converter
 *
 * Copyright (c) 2015 Alex Dowgailenko
 * Licensed under the MIT License
 * https://github.com/alex-dow/icu-converter/blob/master/LICENSE
 */

var _ = require('lodash');

function stringify(obj, argOptions) {

  var defaultOptions = {
    spaces: null,
    replacer: null
  };

  var options = _.defaults({}, defaultOptions, argOptions);

  return JSON.stringify(obj, options.replacer, options.spaces);

}
<commit_msg>Fixed issue with creating options object<commit_after>/**
 * icu-converter
 * https://github.com/alex-dow/icu-converter
 *
 * Copyright (c) 2015 Alex Dowgailenko
 * Licensed under the MIT License
 * https://github.com/alex-dow/icu-converter/blob/master/LICENSE
 */

var _ = require('lodash');

function stringify(obj, argOptions) {

  var defaultOptions = {
    spaces: null,
    replacer: null
  };

  var options = _.defaults({}, argOptions, defaultOptions);

  return JSON.stringify(obj, options.replacer, options.spaces);

}

module.exports = stringify;
<|endoftext|>


<commit_before>function Hammer(pixiStage,board)
{
    var self = this;
    this.board = board;

    this.pixiHammer  = new PIXI.Sprite.fromImage(Config.textures.hammer);
    this.pixiTarget = new PIXI.Sprite.fromImage(Config.textures.target);

    var ratio = this.pixiHammer.height / this.pixiHammer.width;
    this.hit = false;

    this.pixiHammer.alpha = 0.5;
    this.pixiHammer.position ={
        x: 370,
        y: 490
    };
    this.pixiHammer.anchor = {
        x: 0.3,
        y: 0.75
    }

    this.pixiTarget.position = { x : 0.5 , y:0.5};
    this.pixiTarget.anchor = { x : 0.5 , y:0.5};

    this.position ={
       x : 0.5,
       y : 0.5
    };

    this.scoreSinceLastHit = 0;
    this.speed = this.originalSpeed = 0.2;

    pixiStage.addChild(this.pixiHammer);
    pixiStage.addChild(this.pixiTarget);

    setInterval(function(){self.activate()}, 4000);
}

Hammer.prototype.update = function(time, dt, score) {
    var mole = this.getTargetedMole()
    if( mole )
      this.moveToClosestMole(dt, mole)

    var projection = projectVirtualPosition(this.position, Math.sin(time * 2) * 0.04 + 0.04)
    var targetProj = projectVirtualPosition(this.position, 0);

    this.pixiHammer.position = projection.position;
    this.pixiHammer.scale = projection.scale;

    this.pixiTarget.position = targetProj.position;
    this.pixiTarget.scale    = targetProj.scale;

    this.scoreSinceLastHit += score;
    this.speed += this.scoreSinceLastHit / 100000 / 3;
};

function distance(a,b){
    var dx = b.x - a.x
    var dy = b.y - a.y
    return  Math.sqrt(dx * dx + dy * dy);
}

function vec2subtract(a, b)
{
    return  {x: a.x - b.x, y: a.y - b.y}
}

Hammer.prototype.getTargetedMole = function(){
    var self = this;
    var closestMole;
    var hittableMoles = this.board.moles.filter(function(m){
        return m.isHittable();
    });
    if(hittableMoles.length === 0 ) return null;

    initialMole = hittableMoles.pop();

    closestMole = hittableMoles.reduce(function(m,c){
        var dist = distance(self.position,c.molePositions);
        if (m[1]< dist){
           return m;
        }
        else{
            return [c,dist];
        }
    },[initialMole,distance(this.position,initialMole.molePositions)])[0];

    return closestMole;
};

Hammer.prototype.moveToClosestMole = function(dt,nextMole){
    var direction = vec2subtract(nextMole.molePositions, this.position)
    var length = distance(nextMole.molePositions, this.position)

    var normalizedDirection = {
        x: direction.x / length || 0,
        y: direction.y / length || 0
    }

    var modspeed = function(v){
        return v*Math.random()*3
    };

    this.position.x += normalizedDirection.x * dt * (this.speed + modspeed(this.speed));
    this.position.y += normalizedDirection.y * dt * (this.speed + modspeed(this.speed));

}

Hammer.prototype.activate = function(){
    var self = this;
    this.hit = true;
    this.pixiHammer.alpha = 1;
    var hasHit = this.board.hit( this.position );
    if (hasHit) {
      this.scoreSinceLastHit = 0;
      this.speed = this.originalSpeed;
    }

    setTimeout(
        function(){
          self.hit = false;
          self.pixiHammer.alpha = 0.5;
        },200);
};
<commit_msg>Random hammer move when iddle<commit_after>function Hammer(pixiStage,board)
{
    var self = this;
    this.board = board;

    this.pixiHammer  = new PIXI.Sprite.fromImage(Config.textures.hammer);
    this.pixiTarget = new PIXI.Sprite.fromImage(Config.textures.target);

    var ratio = this.pixiHammer.height / this.pixiHammer.width;
    this.hit = false;

    this.pixiHammer.alpha = 0.5;
    this.pixiHammer.position ={
        x: 370,
        y: 490
    };
    this.pixiHammer.anchor = {
        x: 0.3,
        y: 0.75
    }

    this.pixiTarget.position = { x : 0.5 , y:0.5};
    this.pixiTarget.anchor = { x : 0.5 , y:0.5};

    this.position ={
       x : 0.5,
       y : 0.5
    };

    this.scoreSinceLastHit = 0;
    this.speed = this.originalSpeed = 0.2;

    pixiStage.addChild(this.pixiHammer);
    pixiStage.addChild(this.pixiTarget);

    setInterval(function(){self.activate()}, 4000);
}

Hammer.prototype.update = function(time, dt, score) {
    var mole = this.getTargetedMole()
    if( mole )
      this.moveToClosestMole(dt, mole)
    else
      this.moveRandomly( dt, time );

    var projection = projectVirtualPosition(this.position, Math.sin(time * 2) * 0.04 + 0.04)
    var targetProj = projectVirtualPosition(this.position, 0);

    this.pixiHammer.position = projection.position;
    this.pixiHammer.scale = projection.scale;

    this.pixiTarget.position = targetProj.position;
    this.pixiTarget.scale    = targetProj.scale;

    this.scoreSinceLastHit += score;
    this.speed += this.scoreSinceLastHit / 100000 / 3;
};

function distance(a,b){
    var dx = b.x - a.x
    var dy = b.y - a.y
    return  Math.sqrt(dx * dx + dy * dy);
}

function vec2subtract(a, b)
{
    return  {x: a.x - b.x, y: a.y - b.y}
}

Hammer.prototype.getTargetedMole = function(){
    var self = this;
    var closestMole;
    var hittableMoles = this.board.moles.filter(function(m){
        return m.isHittable();
    });
    if(hittableMoles.length === 0 ) return null;

    initialMole = hittableMoles.pop();

    closestMole = hittableMoles.reduce(function(m,c){
        var dist = distance(self.position,c.molePositions);
        if (m[1]< dist){
           return m;
        }
        else{
            return [c,dist];
        }
    },[initialMole,distance(this.position,initialMole.molePositions)])[0];

    return closestMole;
};

Hammer.prototype.moveToClosestMole = function(dt,nextMole){
    var direction = vec2subtract(nextMole.molePositions, this.position)
    var length = distance(nextMole.molePositions, this.position)

    var normalizedDirection = {
        x: direction.x / length || 0,
        y: direction.y / length || 0
    }

    var modspeed = function(v){
        return v*Math.random()*3
    };

    this.position.x += normalizedDirection.x * dt * (this.speed + modspeed(this.speed));
    this.position.y += normalizedDirection.y * dt * (this.speed + modspeed(this.speed));
};

Hammer.prototype.moveRandomly = function( dt, time ){
    var target = {
        x : (Math.sin( time * 2 ) / 2 + 0.5) + (Math.cos(time * 10) + 1 ) / 10 ,
        y : (Math.cos( time * 2 ) / 2 + 0.5) + (Math.cos(time * 10) + 1 ) / 10
    }

    var direction = vec2subtract(target, this.position)
    var length = distance(target, this.position)

    var normalizedDirection = {
        x: direction.x / length || 0,
        y: direction.y / length || 0
    }

    this.position.x += normalizedDirection.x * dt * (this.speed);
    this.position.y += normalizedDirection.y * dt * (this.speed);
};

Hammer.prototype.activate = function(){
    var self = this;
    this.hit = true;
    this.pixiHammer.alpha = 1;
    var hasHit = this.board.hit( this.position );
    if (hasHit) {
      this.scoreSinceLastHit = 0;
      this.speed = this.originalSpeed;
    }

    setTimeout(
        function(){
          self.hit = false;
          self.pixiHammer.alpha = 0.5;
        },200);
};
<|endoftext|>