<commit_before>//scene.js
//Scene Object management and rendering

(function () {
  //Define some locals
  var Vec3 = PhiloGL.Vec3,
      Mat4 = PhiloGL.Mat4,
      //don't ask why, it just works
      generateMipmap = !!(navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox/));

  //Scene class
  var Scene = function(program, camera, opt) {
    opt = $.merge({
      lights: {
        enable: false,
        //ambient light
        ambient: {
          r: 0.2,
          g: 0.2,
          b: 0.2
        },
        //directional light
        directional: {
          direction: {
            x: 1,
            y: 1,
            z: 1
          },
          color: {
            r: 0,
            g: 0,
            b: 0
          }
        }
        //point light
        //points: []
      },
      effects: {
        fog: false
        // { near, far, color }
      }
    }, opt || {});

    this.program = opt.program ? program[opt.program] : program;
    this.camera = camera;
    this.models = [];
    this.config = opt;
  };

  Scene.prototype = {

    add: function() {
      for (var i = 0, models = this.models, l = arguments.length; i < l; i++) {
        var model = arguments[i];
        //Generate unique id for model
        model.id = model.id || $.uid();
        models.push(model);
        //Create and load Buffers
        this.defineBuffers(model);
      }
    },

    remove: function(model) {
      var models = this.models,
          indexOf = models.indexOf(model);

      if (indexOf > -1) {
        models.splice(indexOf, 1);
      }
    },

    getProgram: function(obj) {
      var program = this.program;
      if (program.$$family != 'program' && obj && obj.program) {
        program = program[obj.program];
        program.use();
        return program;
      }
      return program;
    },

    defineBuffers: function(obj) {
      var program = this.getProgram(obj),
          prevDynamic = obj.dynamic;

      obj.dynamic = true;
      obj.setState(program);
      obj.dynamic = prevDynamic;
      obj.unsetState(program);
    },

    beforeRender: function(program) {
      //Setup lighting and scene effects like fog, etc.
      this.setupLighting(program);
      this.setupEffects(program);
      //Set Camera view and projection matrix
      var camera = this.camera,
          pos = camera.position,
          view = camera.view,
          projection = camera.projection,
          viewProjection = view.mulMat4(projection),
          viewProjectionInverse = viewProjection.invert();

      program.setUniforms({
        cameraPosition: [pos.x, pos.y, pos.z],
        projectionMatrix: projection,
        viewMatrix: view,
        viewProjectionMatrix: viewProjection,
        viewInverseMatrix: view.invert(),
        viewProjectionInverseMatrix: viewProjectionInverse
      });
    },

    //Setup the lighting system: ambient, directional, point lights.
    setupLighting: function(program) {
      //Setup Lighting
      var abs = Math.abs,
          camera = this.camera,
          cpos = camera.position,
          light = this.config.lights,
          ambient = light.ambient,
          directional = light.directional,
          dcolor = directional.color,
          dir = directional.direction,
          enable = light.enable,
          points = light.points && $.splat(light.points) || [],
          numberPoints = points.length,
          pointLocations = [],
          pointColors = [],
          enableSpecular = [],
          pointSpecularColors = [];

      //Normalize lighting direction vector
      dir = new Vec3(dir.x, dir.y, dir.z).$unit().$scale(-1);

      //Set light uniforms. Ambient and directional lights.
      program.setUniform('enableLights', enable);

      if (!enable) return;

      program.setUniform('ambientColor', [ambient.r, ambient.g, ambient.b]);
      program.setUniform('directionalColor', [dcolor.r, dcolor.g, dcolor.b]);
      program.setUniform('lightingDirection', [dir.x, dir.y, dir.z]);

      //Set point lights
      program.setUniform('numberPoints', numberPoints);
      for (var i = 0, l = numberPoints; i < l; i++) {
        var point = points[i],
            position = point.position,
            color = point.color || point.diffuse,
            spec = point.specular;

        pointLocations.push(position.x, position.y, position.z);
        pointColors.push(color.r, color.g, color.b);

        //Add specular color
        enableSpecular.push(+!!spec);
        if (spec) {
          pointSpecularColors.push(spec.r, spec.g, spec.b);
        } else {
          pointSpecularColors.push(0, 0, 0);
        }
      }

      program.setUniforms({
        'pointLocation': pointLocations,
        'pointColor': pointColors
      });

      program.setUniforms({
        'enableSpecular': enableSpecular,
        'pointSpecularColor': pointSpecularColors
      });
    },

    //Setup effects like fog, etc.
    setupEffects: function(program) {
      var config = this.config.effects,
          fog = config.fog,
          color = fog.color || { r: 0.5, g: 0.5, b: 0.5 };

      if (fog) {
        program.setUniforms({
          'hasFog': true,
          'fogNear': fog.near,
          'fogFar': fog.far,
          'fogColor': [color.r, color.g, color.b]
        });
      } else {
        program.setUniform('hasFog', false);
      }
    },

    //Renders all objects in the scene.
    render: function(opt) {
      opt = opt || {};
      var camera = this.camera,
          program = this.program,
          renderProgram = opt.renderProgram,
          pType = $.type(program),
          multiplePrograms = !renderProgram && pType == 'object',
          options = $.extend({
            onBeforeRender: $.empty,
            onAfterRender: $.empty
          }, opt || {});

      //If we're just using one program then
      //execute the beforeRender method once.
      !multiplePrograms && this.beforeRender(renderProgram || program);

      //Go through each model and render it.
      for (var i = 0, models = this.models, l = models.length; i < l; ++i) {
        var elem = models[i];
        if (elem.display) {
          var program = renderProgram || this.getProgram(elem);
          //Setup the beforeRender method for each object
          //when there are multiple programs to be used.
          multiplePrograms && this.beforeRender(program);
          elem.onBeforeRender(program, camera);
          options.onBeforeRender(elem, i);
          this.renderObject(elem, program);
          options.onAfterRender(elem, i);
          elem.onAfterRender(program, camera);
        }
      }
    },

    renderToTexture: function(name, opt) {
      opt = opt || {};
      var texture = app.textures[name + '-texture'],
          texMemo = app.textureMemo[name + '-texture'];

      this.render(opt);

      gl.bindTexture(texMemo.textureType, texture);
      gl.generateMipmap(texMemo.textureType);
      gl.bindTexture(texMemo.textureType, null);
    },

    renderObject: function(obj, program) {
      var camera = this.camera,
          view = camera.view,
          projection = camera.projection,
          object = obj.matrix,
          world = view.mulMat4(object),
          worldInverse = world.invert(),
          worldInverseTranspose = worldInverse.transpose();

      obj.setState(program);

      //Now set view and normal matrices
      program.setUniforms({
        objectMatrix: object,
        worldMatrix: world,
        worldInverseMatrix: worldInverse,
        worldInverseTransposeMatrix: worldInverseTranspose
//        worldViewProjection:  view.mulMat4(object).$mulMat4(view.mulMat4(projection))
      });

      //Draw
      //TODO(nico): move this into O3D, but, somehow, abstract the gl.draw* methods inside that object.
      if (obj.render) {
        obj.render(gl, program, camera);
      } else {
        if (obj.$indicesLength) {
          gl.drawElements((obj.drawType !== undefined) ? gl.get(obj.drawType) : gl.TRIANGLES, obj.$indicesLength, gl.UNSIGNED_SHORT, 0);
        } else {
          gl.drawArrays((obj.drawType !== undefined) ? gl.get(obj.drawType) : gl.TRIANGLES, 0, obj.$verticesLength / 3);
        }
      }

      obj.unsetState(program);
    },

    //setup picking framebuffer
    setupPicking: function() {
      //create picking program
      var program = PhiloGL.Program.fromDefaultShaders(),
          floor = Math.floor;
      //create framebuffer
      app.setFrameBuffer('$picking', {
        width: 5,
        height: 1,
        bindToTexture: {
          parameters: [{
            name: 'TEXTURE_MAG_FILTER',
            value: 'LINEAR'
          }, {
            name: 'TEXTURE_MIN_FILTER',
            value: 'LINEAR',
            generateMipmap: generateMipmap
          }]
        },
        bindToRenderBuffer: true
      });
      app.setFrameBuffer('$picking', false);
      this.pickingProgram = program;
    },

    //returns an element at the given position
    pick: function(x, y, lazy) {
      //setup the picking program if this is
      //the first time we enter the method.
      if (!this.pickingProgram) {
        this.setupPicking();
      }

      var o3dHash = {},
          o3dList = [],
          program = app.usedProgram,
          pickingProgram = this.pickingProgram,
          camera = this.camera,
          oldtarget = camera.target,
          oldaspect = camera.aspect,
          config = this.config,
          memoLightEnable = config.lights.enable,
          memoFog = config.effects.fog,
          width = gl.canvas.width,
          height = gl.canvas.height,
          floor = Math.floor,
          pickingRes = Scene.PICKING_RES,
          resWidth = 5,
          resHeight = 1,
          ndcx = x * 2 / width - 1,
          ndcy = 1 - y * 2 / height,
          origin = this.unproject([ndcx, ndcy, -1.0], camera),
          target = this.unproject([ndcx, ndcy,  1.0], camera),
          hash = [],
          pixel = new Uint8Array(1 * 1 * 4),
          index = 0,
          backgroundColor, capture, pindex;

      //setup the scene for picking
      config.lights.enable = false;
      config.effects.fog = false;

      //enable picking and render to texture
      app.setFrameBuffer('$picking', true);
      pickingProgram.use();
      pickingProgram.setUniform('enablePicking', true);

      //render the scene to a texture
      gl.disable(gl.BLEND);
      gl.viewport(0, 0, resWidth, resHeight);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      //read the background color so we don't step on it
      gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
      backgroundColor = pixel[0] + pixel[1] * 256 + pixel[2] * 256 * 256;

      //render to texture
      this.renderToTexture('$picking', {
        renderProgram: pickingProgram,
        onBeforeRender: function(elem, i) {
          if (i == backgroundColor) {
            index = 1;
          }
          var suc = i + index,
              hasPickingColors = !!elem.pickingColors;

          pickingProgram.setUniform('hasPickingColors', hasPickingColors);

          if (!hasPickingColors) {
            hash[0] = suc % 256;
            hash[1] = ((suc / 256) >> 0) % 256;
            hash[2] = ((suc / (256 * 256)) >> 0) % 256;
            pickingProgram.setUniform('pickColor', [hash[0] / 255, hash[1] / 255, hash[2] / 255]);
            o3dHash[hash.join()] = elem;
          } else {
            o3dList.push(elem);
          }
        }
      });

      // the target point is in the center of the screen,
      // so it should be the center point.
      gl.readPixels(2, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);

      var stringColor = [pixel[0], pixel[1], pixel[2]].join(),
          elem = o3dHash[stringColor],
          pick;

      if (!elem) {
        for (var i = 0, l = o3dList.length; i < l; i++) {
          elem = o3dList[i];
          pick = elem.pick(pixel);
          if (pick !== false) {
            elem.$pickingIndex = pick;
          } else {
            elem = false;
          }
        }
      }

      //restore all values and unbind buffers
      app.setFrameBuffer('$picking', false);
      app.setTexture('$picking-texture', false);
      pickingProgram.setUniform('enablePicking', false);
      config.lights.enable = memoLightEnable;
      config.effects.fog = memoFog;

      //restore previous program
      if (program) program.use();
      //restore the viewport size to original size
      gl.viewport(0, 0, width, height);
      //restore camera properties
      camera.target = oldtarget;
      camera.aspect = oldaspect;
      camera.update();

      //store model hash and pixel array
      this.o3dHash = o3dHash;
      this.o3dList = o3dList;
      this.pixel = pixel;
      this.capture = capture;

      return elem && elem.pickable && elem;
    },

    unproject: function(pt, camera) {
      return camera.view.invert().mulMat4(camera.projection.invert()).mulVec3(pt);
    },

    resetPicking: $.empty
  };

  Scene.MAX_TEXTURES = 10;
  Scene.MAX_POINT_LIGHTS = 50;
  Scene.PICKING_RES = 4;

  PhiloGL.Scene = Scene;

})();
<commit_msg>Fix the ray picking issue in philogl's branch<commit_after>//scene.js
//Scene Object management and rendering

(function () {
  //Define some locals
  var Vec3 = PhiloGL.Vec3,
      Mat4 = PhiloGL.Mat4,
      //don't ask why, it just works
      generateMipmap = !!(navigator && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox/));

  //Scene class
  var Scene = function(program, camera, opt) {
    opt = $.merge({
      lights: {
        enable: false,
        //ambient light
        ambient: {
          r: 0.2,
          g: 0.2,
          b: 0.2
        },
        //directional light
        directional: {
          direction: {
            x: 1,
            y: 1,
            z: 1
          },
          color: {
            r: 0,
            g: 0,
            b: 0
          }
        }
        //point light
        //points: []
      },
      effects: {
        fog: false
        // { near, far, color }
      }
    }, opt || {});

    this.program = opt.program ? program[opt.program] : program;
    this.camera = camera;
    this.models = [];
    this.config = opt;
  };

  Scene.prototype = {

    add: function() {
      for (var i = 0, models = this.models, l = arguments.length; i < l; i++) {
        var model = arguments[i];
        //Generate unique id for model
        model.id = model.id || $.uid();
        models.push(model);
        //Create and load Buffers
        this.defineBuffers(model);
      }
    },

    remove: function(model) {
      var models = this.models,
          indexOf = models.indexOf(model);

      if (indexOf > -1) {
        models.splice(indexOf, 1);
      }
    },

    getProgram: function(obj) {
      var program = this.program;
      if (program.$$family != 'program' && obj && obj.program) {
        program = program[obj.program];
        program.use();
        return program;
      }
      return program;
    },

    defineBuffers: function(obj) {
      var program = this.getProgram(obj),
          prevDynamic = obj.dynamic;

      obj.dynamic = true;
      obj.setState(program);
      obj.dynamic = prevDynamic;
      obj.unsetState(program);
    },

    beforeRender: function(program) {
      //Setup lighting and scene effects like fog, etc.
      this.setupLighting(program);
      this.setupEffects(program);
      //Set Camera view and projection matrix
      var camera = this.camera,
          pos = camera.position,
          view = camera.view,
          projection = camera.projection,
          viewProjection = view.mulMat4(projection),
          viewProjectionInverse = viewProjection.invert();

      program.setUniforms({
        cameraPosition: [pos.x, pos.y, pos.z],
        projectionMatrix: projection,
        viewMatrix: view,
        viewProjectionMatrix: viewProjection,
        viewInverseMatrix: view.invert(),
        viewProjectionInverseMatrix: viewProjectionInverse
      });
    },

    //Setup the lighting system: ambient, directional, point lights.
    setupLighting: function(program) {
      //Setup Lighting
      var abs = Math.abs,
          camera = this.camera,
          cpos = camera.position,
          light = this.config.lights,
          ambient = light.ambient,
          directional = light.directional,
          dcolor = directional.color,
          dir = directional.direction,
          enable = light.enable,
          points = light.points && $.splat(light.points) || [],
          numberPoints = points.length,
          pointLocations = [],
          pointColors = [],
          enableSpecular = [],
          pointSpecularColors = [];

      //Normalize lighting direction vector
      dir = new Vec3(dir.x, dir.y, dir.z).$unit().$scale(-1);

      //Set light uniforms. Ambient and directional lights.
      program.setUniform('enableLights', enable);

      if (!enable) return;

      program.setUniform('ambientColor', [ambient.r, ambient.g, ambient.b]);
      program.setUniform('directionalColor', [dcolor.r, dcolor.g, dcolor.b]);
      program.setUniform('lightingDirection', [dir.x, dir.y, dir.z]);

      //Set point lights
      program.setUniform('numberPoints', numberPoints);
      for (var i = 0, l = numberPoints; i < l; i++) {
        var point = points[i],
            position = point.position,
            color = point.color || point.diffuse,
            spec = point.specular;

        pointLocations.push(position.x, position.y, position.z);
        pointColors.push(color.r, color.g, color.b);

        //Add specular color
        enableSpecular.push(+!!spec);
        if (spec) {
          pointSpecularColors.push(spec.r, spec.g, spec.b);
        } else {
          pointSpecularColors.push(0, 0, 0);
        }
      }

      program.setUniforms({
        'pointLocation': pointLocations,
        'pointColor': pointColors
      });

      program.setUniforms({
        'enableSpecular': enableSpecular,
        'pointSpecularColor': pointSpecularColors
      });
    },

    //Setup effects like fog, etc.
    setupEffects: function(program) {
      var config = this.config.effects,
          fog = config.fog,
          color = fog.color || { r: 0.5, g: 0.5, b: 0.5 };

      if (fog) {
        program.setUniforms({
          'hasFog': true,
          'fogNear': fog.near,
          'fogFar': fog.far,
          'fogColor': [color.r, color.g, color.b]
        });
      } else {
        program.setUniform('hasFog', false);
      }
    },

    //Renders all objects in the scene.
    render: function(opt) {
      opt = opt || {};
      var camera = this.camera,
          program = this.program,
          renderProgram = opt.renderProgram,
          pType = $.type(program),
          multiplePrograms = !renderProgram && pType == 'object',
          options = $.extend({
            onBeforeRender: $.empty,
            onAfterRender: $.empty
          }, opt || {});

      //If we're just using one program then
      //execute the beforeRender method once.
      !multiplePrograms && this.beforeRender(renderProgram || program);

      //Go through each model and render it.
      for (var i = 0, models = this.models, l = models.length; i < l; ++i) {
        var elem = models[i];
        if (elem.display) {
          var program = renderProgram || this.getProgram(elem);
          //Setup the beforeRender method for each object
          //when there are multiple programs to be used.
          multiplePrograms && this.beforeRender(program);
          elem.onBeforeRender(program, camera);
          options.onBeforeRender(elem, i);
          this.renderObject(elem, program);
          options.onAfterRender(elem, i);
          elem.onAfterRender(program, camera);
        }
      }
    },

    renderToTexture: function(name, opt) {
      opt = opt || {};
      var texture = app.textures[name + '-texture'],
          texMemo = app.textureMemo[name + '-texture'];

      this.render(opt);

      gl.bindTexture(texMemo.textureType, texture);
      gl.generateMipmap(texMemo.textureType);
      gl.bindTexture(texMemo.textureType, null);
    },

    renderObject: function(obj, program) {
      var camera = this.camera,
          view = camera.view,
          projection = camera.projection,
          object = obj.matrix,
          world = view.mulMat4(object),
          worldInverse = world.invert(),
          worldInverseTranspose = worldInverse.transpose();

      obj.setState(program);

      //Now set view and normal matrices
      program.setUniforms({
        objectMatrix: object,
        worldMatrix: world,
        worldInverseMatrix: worldInverse,
        worldInverseTransposeMatrix: worldInverseTranspose
//        worldViewProjection:  view.mulMat4(object).$mulMat4(view.mulMat4(projection))
      });

      //Draw
      //TODO(nico): move this into O3D, but, somehow, abstract the gl.draw* methods inside that object.
      if (obj.render) {
        obj.render(gl, program, camera);
      } else {
        if (obj.$indicesLength) {
          gl.drawElements((obj.drawType !== undefined) ? gl.get(obj.drawType) : gl.TRIANGLES, obj.$indicesLength, gl.UNSIGNED_SHORT, 0);
        } else {
          gl.drawArrays((obj.drawType !== undefined) ? gl.get(obj.drawType) : gl.TRIANGLES, 0, obj.$verticesLength / 3);
        }
      }

      obj.unsetState(program);
    },

    //setup picking framebuffer
    setupPicking: function() {
      //create picking program
      var program = PhiloGL.Program.fromDefaultShaders(),
          floor = Math.floor;
      //create framebuffer
      app.setFrameBuffer('$picking', {
        width: 5,
        height: 1,
        bindToTexture: {
          parameters: [{
            name: 'TEXTURE_MAG_FILTER',
            value: 'LINEAR'
          }, {
            name: 'TEXTURE_MIN_FILTER',
            value: 'LINEAR',
            generateMipmap: generateMipmap
          }]
        },
        bindToRenderBuffer: true
      });
      app.setFrameBuffer('$picking', false);
      this.pickingProgram = program;
    },

    //returns an element at the given position
    pick: function(x, y, lazy) {
      //setup the picking program if this is
      //the first time we enter the method.
      if (!this.pickingProgram) {
        this.setupPicking();
      }

      var o3dHash = {},
          o3dList = [],
          program = app.usedProgram,
          pickingProgram = this.pickingProgram,
          camera = this.camera,
          oldtarget = camera.target,
          oldaspect = camera.aspect,
          config = this.config,
          memoLightEnable = config.lights.enable,
          memoFog = config.effects.fog,
          width = gl.canvas.width,
          height = gl.canvas.height,
          floor = Math.floor,
          pickingRes = Scene.PICKING_RES,
          resWidth = 5,
          resHeight = 1,
          ndcx = x * 2 / width - 1,
          ndcy = 1 - y * 2 / height,
          target = this.unproject([ndcx, ndcy,  1.0], camera),
          hash = [],
          pixel = new Uint8Array(1 * 1 * 4),
          index = 0,
          backgroundColor, capture, pindex;

      this.camera.target = target;
      this.camera.update ();
      //setup the scene for picking
      config.lights.enable = false;
      config.effects.fog = false;

      //enable picking and render to texture
      app.setFrameBuffer('$picking', true);
      pickingProgram.use();
      pickingProgram.setUniform('enablePicking', true);

      //render the scene to a texture
      gl.disable(gl.BLEND);
      gl.viewport(0, 0, resWidth, resHeight);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      //read the background color so we don't step on it
      gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
      backgroundColor = pixel[0] + pixel[1] * 256 + pixel[2] * 256 * 256;

      //render to texture
      this.renderToTexture('$picking', {
        renderProgram: pickingProgram,
        onBeforeRender: function(elem, i) {
          if (i == backgroundColor) {
            index = 1;
          }
          var suc = i + index,
              hasPickingColors = !!elem.pickingColors;

          pickingProgram.setUniform('hasPickingColors', hasPickingColors);

          if (!hasPickingColors) {
            hash[0] = suc % 256;
            hash[1] = ((suc / 256) >> 0) % 256;
            hash[2] = ((suc / (256 * 256)) >> 0) % 256;
            pickingProgram.setUniform('pickColor', [hash[0] / 255, hash[1] / 255, hash[2] / 255]);
            o3dHash[hash.join()] = elem;
          } else {
            o3dList.push(elem);
          }
        }
      });

      // the target point is in the center of the screen,
      // so it should be the center point.
      gl.readPixels(2, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);

      var stringColor = [pixel[0], pixel[1], pixel[2]].join(),
          elem = o3dHash[stringColor],
          pick;

      if (!elem) {
        for (var i = 0, l = o3dList.length; i < l; i++) {
          elem = o3dList[i];
          pick = elem.pick(pixel);
          if (pick !== false) {
            elem.$pickingIndex = pick;
          } else {
            elem = false;
          }
        }
      }

      //restore all values and unbind buffers
      app.setFrameBuffer('$picking', false);
      app.setTexture('$picking-texture', false);
      pickingProgram.setUniform('enablePicking', false);
      config.lights.enable = memoLightEnable;
      config.effects.fog = memoFog;

      //restore previous program
      if (program) program.use();
      //restore the viewport size to original size
      gl.viewport(0, 0, width, height);
      //restore camera properties
      camera.target = oldtarget;
      camera.aspect = oldaspect;
      camera.update();

      //store model hash and pixel array
      this.o3dHash = o3dHash;
      this.o3dList = o3dList;
      this.pixel = pixel;
      this.capture = capture;

      return elem && elem.pickable && elem;
    },

    unproject: function(pt, camera) {
      return camera.view.invert().mulMat4(camera.projection.invert()).mulVec3(pt);
    },

    resetPicking: $.empty
  };

  Scene.MAX_TEXTURES = 10;
  Scene.MAX_POINT_LIGHTS = 50;
  Scene.PICKING_RES = 4;

  PhiloGL.Scene = Scene;

})();
<|endoftext|>


<commit_before>import { h, Component } from 'preact';
import { exec, pathRankSort } from './util';

const ROUTERS = [];

const EMPTY = {};

function route(url, replace=false) {
	if (typeof url!=='string' && url.url) {
		replace = url.replace;
		url = url.url;
	}
	if (typeof history!=='undefined' && history.pushState) {
		if (replace===true) {
			history.replaceState(null, null, url);
		}
		else {
			history.pushState(null, null, url);
		}
	}
	return routeTo(url);
}

function routeTo(url) {
	let didRoute = false;
	ROUTERS.forEach( router => {
		if (router.routeTo(url)===true) {
			didRoute = true;
		}
	});
	return didRoute;
}

function getCurrentUrl() {
	let url = typeof location!=='undefined' ? location : EMPTY;
	return `${url.pathname || ''}${url.search || ''}`;
}

function routeFromLink(node) {
	// only valid elements
	if (!node || !node.getAttribute) return;

	let href = node.getAttribute('href'),
		target = node.getAttribute('target');

	// ignore links with targets and non-path URLs
	if (!href || !href.match(/^\//g) || (target && !target.match(/^_?self$/i))) return;

	// attempt to route, if no match simply cede control to browser
	return route(href);
}

function handleLinkClick(e) {
	routeFromLink(e.currentTarget || e.target || this);
	return prevent(e);
}

function prevent(e) {
	if (e) {
		if (e.stopImmediatePropagation) e.stopImmediatePropagation();
		if (e.stopPropagation) e.stopPropagation();
		e.preventDefault();
	}
	return false;
}

function delegateLinkHandler(e) {
	// ignore events the browser takes care of already:
	if (e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) return;

	let t = e.target;
	do {
		if (String(t.nodeName).toUpperCase()==='A' && t.getAttribute('href')) {
			// if link is handled by the router, prevent browser defaults
			if (routeFromLink(t)) {
				return prevent(e);
			}
		}
	} while ((t=t.parentNode));
}

if (typeof addEventListener==='function') {
	addEventListener('popstate', () => routeTo(getCurrentUrl()));
	addEventListener('click', delegateLinkHandler);
}

const Link = ({ children, ...props }) => (
	{ children }
);

class Router extends Component {
	state = {
		url: this.props.url || getCurrentUrl()
	};

	routeTo(url) {
		this._didRoute = false;
		if (url!==this.state.url) {
			this.setState({ url });
			if (!this._didRoute) {
				this.forceUpdate();
			}
		}
		return this._didRoute;
	}

	componentWillMount() {
		ROUTERS.push(this);
	}

	componentWillUnmount() {
		ROUTERS.splice(ROUTERS.indexOf(this), 1);
	}

	render({ children, onChange }, { url }) {
		let active = children.slice().sort(pathRankSort).filter( ({ attributes }) => {
			let path = attributes.path,
				matches = exec(url, path, attributes);
			if (matches) {
				attributes.url = url;
				attributes.matches = matches;
				// copy matches onto props
				for (let i in matches) {
					if (matches.hasOwnProperty(i)) {
						attributes[i] = matches[i];
					}
				}
				return true;
			}
		});

		let current = active[0] || null;
		this._didRoute = !!current;

		let previous = this.previousUrl;
		if (url!==previous) {
			this.previousUrl = url;
			if (typeof onChange==='function') {
				onChange({
					router: this,
					url,
					previous,
					active,
					current
				});
			}
		}

		return current;
	}
}

const Route = ({ component:RoutedComponent, url, matches }) => (
	<RoutedComponent {...{url, matches}} />
);

Router.route = route;
Router.Router = Router;
Router.Route = Route;
Router.Link = Link;

export { route, Router, Route, Link };
export default Router;
<commit_msg>Add Boolean `static` prop that enables `shouldComponentUpdate():false` optimization.<commit_after>import { h, Component } from 'preact';
import { exec, pathRankSort } from './util';

const ROUTERS = [];

const EMPTY = {};

function route(url, replace=false) {
	if (typeof url!=='string' && url.url) {
		replace = url.replace;
		url = url.url;
	}
	if (typeof history!=='undefined' && history.pushState) {
		if (replace===true) {
			history.replaceState(null, null, url);
		}
		else {
			history.pushState(null, null, url);
		}
	}
	return routeTo(url);
}

function routeTo(url) {
	let didRoute = false;
	ROUTERS.forEach( router => {
		if (router.routeTo(url)===true) {
			didRoute = true;
		}
	});
	return didRoute;
}

function getCurrentUrl() {
	let url = typeof location!=='undefined' ? location : EMPTY;
	return `${url.pathname || ''}${url.search || ''}`;
}

function routeFromLink(node) {
	// only valid elements
	if (!node || !node.getAttribute) return;

	let href = node.getAttribute('href'),
		target = node.getAttribute('target');

	// ignore links with targets and non-path URLs
	if (!href || !href.match(/^\//g) || (target && !target.match(/^_?self$/i))) return;

	// attempt to route, if no match simply cede control to browser
	return route(href);
}

function handleLinkClick(e) {
	routeFromLink(e.currentTarget || e.target || this);
	return prevent(e);
}

function prevent(e) {
	if (e) {
		if (e.stopImmediatePropagation) e.stopImmediatePropagation();
		if (e.stopPropagation) e.stopPropagation();
		e.preventDefault();
	}
	return false;
}

function delegateLinkHandler(e) {
	// ignore events the browser takes care of already:
	if (e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) return;

	let t = e.target;
	do {
		if (String(t.nodeName).toUpperCase()==='A' && t.getAttribute('href')) {
			// if link is handled by the router, prevent browser defaults
			if (routeFromLink(t)) {
				return prevent(e);
			}
		}
	} while ((t=t.parentNode));
}

if (typeof addEventListener==='function') {
	addEventListener('popstate', () => routeTo(getCurrentUrl()));
	addEventListener('click', delegateLinkHandler);
}

const Link = ({ children, ...props }) => (
	{ children }
);

class Router extends Component {
	state = {
		url: this.props.url || getCurrentUrl()
	};

	shouldComponentUpdate(props) {
		if (props.static!==true) return true;
		return props.url!==this.props.url || props.onChange!==this.props.onChange;
	}

	routeTo(url) {
		this._didRoute = false;
		if (url!==this.state.url) {
			this.setState({ url });
			if (!this._didRoute) {
				this.forceUpdate();
			}
		}
		return this._didRoute;
	}

	componentWillMount() {
		ROUTERS.push(this);
	}

	componentWillUnmount() {
		ROUTERS.splice(ROUTERS.indexOf(this), 1);
	}

	render({ children, onChange }, { url }) {
		let active = children.slice().sort(pathRankSort).filter( ({ attributes }) => {
			let path = attributes.path,
				matches = exec(url, path, attributes);
			if (matches) {
				attributes.url = url;
				attributes.matches = matches;
				// copy matches onto props
				for (let i in matches) {
					if (matches.hasOwnProperty(i)) {
						attributes[i] = matches[i];
					}
				}
				return true;
			}
		});

		let current = active[0] || null;
		this._didRoute = !!current;

		let previous = this.previousUrl;
		if (url!==previous) {
			this.previousUrl = url;
			if (typeof onChange==='function') {
				onChange({
					router: this,
					url,
					previous,
					active,
					current
				});
			}
		}

		return current;
	}
}

const Route = ({ component:RoutedComponent, url, matches }) => (
	<RoutedComponent {...{url, matches}} />
);

Router.route = route;
Router.Router = Router;
Router.Route = Route;
Router.Link = Link;

export { route, Router, Route, Link };
export default Router;
<|endoftext|>


<commit_before>define([
    'src/collection',
    'src/util'
], function (
    Collection,
    util
) {
    'use strict';

    /**
     * A Scene controls the flow of game logic by defining a beginning state and one or more ending states. Once it's
     * initialized, the scene will iterate frame-by-frame invoking evaluation logic on each of its actors and stages.
     * The last step of each frame is to test each ending condition to see if the scene should end and how control
     * should be passed to the next segment of the game.
     * @param config {scenConfig} Configuration options for initializing this scene
     * @param scope {Proscenium} A reference to the Proscenium object used to define the Scene
     * @returns {Scene}
     * @constructor
     *
     * {Object} sceneConfig
     * {Function} sceneConfig.always will run at the beginning of every frame
     * {Function} sceneConfig.init will run immediately upon instantiation
     */
    var Scene = function(config, scope) {
        var that = this;

        config = config || {};
        this.id = config.id;

        this._frame = 0;
        this._framerate = 0;
        this._lastFrame = 0;
        this._pausedAt = 0;
        this._pausedFor = 0;

        this.actors = [];
        this.conditions = [];
        this.curtains = [];
        this.paused = false;
	this.running = false;
        this.stages = [];

        this.always = config.always;
        this.throttle = config.throttle || 60;

        if (config.curtains instanceof Array) {
            this.curtains = config.curtains.map(function (name) {
                return scope.curtains[name];
            });
        } else if ('string' === typeof config.curtains) {
            this.curtains.push(scope.curtains[name]);
        }

        if (config.stages instanceof Array) {
            this.stages = config.stages.map(function (name) {
                return scope.stages[name];
            });
        } else if ('string' === typeof config.curtains) {
            this.stages.push(scope.stages[name]);
        }

        if ('function' === typeof config.prep) {
            this.prep = config.prep.bind(this);
        }

        if ('function' === typeof config.init) {
            config.init.call(this);
        }

        return this;
    };

    Scene.prototype.warmup = function (config) {
        if ('function' === typeof this.prep) {
            this.prep(config);
        }
        this.curtains.forEach(function (curtain) {
            curtain.clear();
        });
        this.stages.forEach(function (stage) {
            if ('function' === typeof stage.prep) {
                stage.prep(config);
            }
        });
        this.actors.forEach(function (actor) {
            if ('function' === typeof actor.prep) {
                actor.prep(config);
            }
        });
        return this;
    };

    Scene.prototype.evaluate = function (interval) {
        var evaluations, frame;

        evaluations = [];
        this._frame += 1;
        frame = this._frame;

        if ('function' === typeof this.always) {
            this.always(interval);
        }

        this.actors.forEach(function (actor) {
            var result;
            if ('function' === typeof actor.evaluate) {
                result = actor.evaluate(interval);
                if ('function' === typeof result) {
                    evaluations.push(result);
                } else if (result instanceof Array) {
                    evaluations = evaluations.concat(result);
                }
            }
        });

        evaluations.forEach(function (execute) {
            execute();
        });

        this.stages.forEach(function (stage) {
            if ('function' === typeof stage.evaluate) {
                if ('function' !== typeof stage.phase || stage.phase(frame)) {
                    stage.evaluate(interval);
                }
            }
        });

        this.conditions.forEach(function (condition) {
            if (condition.test()) {
                condition.run();
            }
        });
    };

    Scene.prototype.pause = function () {
        if (!this.paused) {
            this.paused = true;
            this._pausedAt = new Date().getTime();
        }
        return this;
    };

    Scene.prototype.unpause = function () {
        if (this.paused) {
            this.paused = false;
            this._pausedFor += new Date().getTime() - this._pausedAt;
        }
        return this;
    };

    Scene.prototype.run = function () {
        var interval, now, timeout;

        now = new Date().getTime();
        timeout = 1000 / this.throttle;
        interval = now - this._lastFrame;
        this._lastFrame = now;

        if (!this.paused) {
            this.evaluate(interval);
            now = new Date().getTime();
            interval = now - this._lastFrame;
            if (interval < timeout) {
                timeout -= interval;
                this._framerate = this.throttle;
            } else {
                timeout = interval % timeout;
                this._framerate = Math.floor(1000 / Math.max(interval, 1));
            }
        }

        if (this.running) {
            this._timeout = setTimeout(
                this.run.bind(this),
                timeout
            );
        }

        return this;
    };

    Scene.prototype.cleanup = function () {
        this.curtains.forEach(function (curtain) {
            curtain.clear(this);
        });
        this.stages.forEach(function (stage) {
            if ('function' === typeof stage.clear) {
                stage.clear(this);
            }
        });
        return this;
    };

    Scene.prototype.begin = function (config) {
        this.running = true;
        this._lastFrame = new Date().getTime();
        this.warmup(config);
        this.run();
    };

    Scene.prototype.end = function () {
        this.running = false;
        clearTimeout(this._timeout);
	this.cleanup();
    };

    return Scene;
});

<commit_msg>Fix scope issue during scene.cleanup<commit_after>define([
    'src/collection',
    'src/util'
], function (
    Collection,
    util
) {
    'use strict';

    /**
     * A Scene controls the flow of game logic by defining a beginning state and one or more ending states. Once it's
     * initialized, the scene will iterate frame-by-frame invoking evaluation logic on each of its actors and stages.
     * The last step of each frame is to test each ending condition to see if the scene should end and how control
     * should be passed to the next segment of the game.
     * @param config {scenConfig} Configuration options for initializing this scene
     * @param scope {Proscenium} A reference to the Proscenium object used to define the Scene
     * @returns {Scene}
     * @constructor
     *
     * {Object} sceneConfig
     * {Function} sceneConfig.always will run at the beginning of every frame
     * {Function} sceneConfig.init will run immediately upon instantiation
     */
    var Scene = function(config, scope) {
        var that = this;

        config = config || {};
        this.id = config.id;

        this._frame = 0;
        this._framerate = 0;
        this._lastFrame = 0;
        this._pausedAt = 0;
        this._pausedFor = 0;

        this.actors = [];
        this.conditions = [];
        this.curtains = [];
        this.paused = false;
	this.running = false;
        this.stages = [];

        this.always = config.always;
        this.throttle = config.throttle || 60;

        if (config.curtains instanceof Array) {
            this.curtains = config.curtains.map(function (name) {
                return scope.curtains[name];
            });
        } else if ('string' === typeof config.curtains) {
            this.curtains.push(scope.curtains[name]);
        }

        if (config.stages instanceof Array) {
            this.stages = config.stages.map(function (name) {
                return scope.stages[name];
            });
        } else if ('string' === typeof config.curtains) {
            this.stages.push(scope.stages[name]);
        }

        if ('function' === typeof config.prep) {
            this.prep = config.prep.bind(this);
        }

        if ('function' === typeof config.init) {
            config.init.call(this);
        }

        return this;
    };

    Scene.prototype.warmup = function (config) {
        if ('function' === typeof this.prep) {
            this.prep(config);
        }
        this.curtains.forEach(function (curtain) {
            curtain.clear();
        });
        this.stages.forEach(function (stage) {
            if ('function' === typeof stage.prep) {
                stage.prep(config);
            }
        });
        this.actors.forEach(function (actor) {
            if ('function' === typeof actor.prep) {
                actor.prep(config);
            }
        });
        return this;
    };

    Scene.prototype.evaluate = function (interval) {
        var evaluations, frame;

        evaluations = [];
        this._frame += 1;
        frame = this._frame;

        if ('function' === typeof this.always) {
            this.always(interval);
        }

        this.actors.forEach(function (actor) {
            var result;
            if ('function' === typeof actor.evaluate) {
                result = actor.evaluate(interval);
                if ('function' === typeof result) {
                    evaluations.push(result);
                } else if (result instanceof Array) {
                    evaluations = evaluations.concat(result);
                }
            }
        });

        evaluations.forEach(function (execute) {
            execute();
        });

        this.stages.forEach(function (stage) {
            if ('function' === typeof stage.evaluate) {
                if ('function' !== typeof stage.phase || stage.phase(frame)) {
                    stage.evaluate(interval);
                }
            }
        });

        this.conditions.forEach(function (condition) {
            if (condition.test()) {
                condition.run();
            }
        });
    };

    Scene.prototype.pause = function () {
        if (!this.paused) {
            this.paused = true;
            this._pausedAt = new Date().getTime();
        }
        return this;
    };

    Scene.prototype.unpause = function () {
        if (this.paused) {
            this.paused = false;
            this._pausedFor += new Date().getTime() - this._pausedAt;
        }
        return this;
    };

    Scene.prototype.run = function () {
        var interval, now, timeout;

        now = new Date().getTime();
        timeout = 1000 / this.throttle;
        interval = now - this._lastFrame;
        this._lastFrame = now;

        if (!this.paused) {
            this.evaluate(interval);
            now = new Date().getTime();
            interval = now - this._lastFrame;
            if (interval < timeout) {
                timeout -= interval;
                this._framerate = this.throttle;
            } else {
                timeout = interval % timeout;
                this._framerate = Math.floor(1000 / Math.max(interval, 1));
            }
        }

        if (this.running) {
            this._timeout = setTimeout(
                this.run.bind(this),
                timeout
            );
        }

        return this;
    };

    Scene.prototype.cleanup = function () {
    	var scene = this;
        this.curtains.forEach(function (curtain) {
            curtain.clear(scene);
        });
        this.stages.forEach(function (stage) {
            if ('function' === typeof stage.clear) {
                stage.clear(scene);
            }
        });
        return this;
    };

    Scene.prototype.begin = function (config) {
        this.running = true;
        this._lastFrame = new Date().getTime();
        this.warmup(config);
        this.run();
    };

    Scene.prototype.end = function () {
        this.running = false;
        clearTimeout(this._timeout);
	this.cleanup();
    };

    return Scene;
});

<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import Resizable from 'react-resizable-and-movable';
import Draggable from 'react-draggable';

export default class HelloWorld extends Component {
  static propTypes = {
    start: PropTypes.object.isRequired,
    backgroundColor: PropTypes.string,
  };

  static defaultProps = {
    start: {
      box: {
        x: 200,
        y: 200,
        width: 100,
        height: 100,
      },
      pointer: {
        x: 400,
        y: 400,
      },
    },
    backgroundColor: '#ccc',
  };

  constructor(props) {
    super(props);
    const { box, pointer } = this.props.start;
    const base = [
      { x: box.x + box.width, y: box.y + box.height * 0.25 },
      { x: box.x + box.width, y: box.y + box.height * 0.75 },
    ];

    const control = { x: box.x + box.width, y: (box.y + box.height * 0.5) };

    this.state = {
      pointer: {
        base,
        control,
        destination: pointer,
      },
      box: {
        x: box.x,
        y: box.y,
        width: box.width,
        height: box.height,
      },
    };
  }

  onBoxDrag(e, { position }) {
    console.log(`${position.left}, ${position.top}`);
    const { width, height } = this.state.box;
    const x = position.left;
    const y = position.top;
    const base = [
      { x: x + width, y: y + height * 0.25 },
      { x: x + width, y: y + height * 0.75 },
    ];
    const control = { x: x + width, y: y + height * 0.5 };
    const { destination } = this.state.pointer;
    this.setState({
      pointer: {
        base,
        control,
        destination,
      },
      box: {
        width,
        height,
        x,
        y,
      },
    });
  }

  onPointerDrag(e) {
    console.dir(e);
    const { base, control } = this.state.pointer;
    this.setState({
      pointer: {
        base,
        control,
        destination: {
          x: e.clientX,
          y: e.clientY,
        },
      },
    });
  }

  render() {
    const { start, backgroundColor } = this.props;
    const { base, destination, control } = this.state.pointer;

    return (

        <Resizable
          start={ start.box }
          customStyle={{ backgroundColor }}
          onDrag={ ::this.onBoxDrag }
        >
          Hello, world
        </Resizable>
        <svg width="2000" height="2000" style={{/* TODO: add absolute */}}>
          <path
            d={ `M ${ base[0].x } ${ base[0].y }
                 Q ${ control.x } ${ control.y } ${ destination.x } ${ destination.y }
                 Q ${ control.x } ${ control.y } ${ base[1].x } ${ base[1].y }` }
            fill={ backgroundColor }
          />
        </svg>
        <Draggable
          start={ start.pointer }
          onDrag={ ::this.onPointerDrag }
        >

        </Draggable>

    );
  }
}
<commit_msg>Implement pointer type calculator<commit_after>import React, { Component, PropTypes } from 'react';
import Resizable from 'react-resizable-and-movable';
import Draggable from 'react-draggable';

export default class HelloWorld extends Component {
  static propTypes = {
    start: PropTypes.object.isRequired,
    backgroundColor: PropTypes.string,
  };

  static defaultProps = {
    start: {
      box: {
        x: 200,
        y: 200,
        width: 100,
        height: 100,
      },
      pointer: {
        x: 250,
        y: 0,
      },
    },
    backgroundColor: '#ccc',
  };

  constructor(props) {
    super(props);
    const { box, pointer } = this.props.start;
    const boxCenter = {
      x: box.x + box.width / 2,
      y: box.y + box.height / 2,
    };

    const type = this.getPointerType(boxCenter, pointer);

    const base = [
      { x: box.x + box.width, y: box.y + box.height * 0.25 },
      { x: box.x + box.width, y: box.y + box.height * 0.75 },
    ];

    const control = { x: box.x + box.width, y: (box.y + box.height * 0.5) };

    this.state = {
      type,
      pointer: {
        base,
        control,
        destination: pointer,
      },
      box: {
        x: box.x,
        y: box.y,
        width: box.width,
        height: box.height,
      },
    };
  }

  getDegree(origin, destination) {
    const x = destination.x - origin.x;
    const y = origin.y - destination.y;
    const rad = Math.atan2(y, x);
    if (isNaN(rad)) return null; // TODO:
    return rad * 360 / (2 * Math.PI);
  }

  getPointerType(origin, destination) {
    const degree = this.getDegree(origin, destination);
    console.log(degree);
    if (-45 <= degree && degree < 45) return 'right';
    if (45 <= degree && degree < 135) return 'top';
    if ((135 <= degree && degree <= 180) || (-180 <= degree && degree < -135)) return 'left';
    if (-135 <= degree && degree < -45) return 'bottom';
  }

  onBoxDrag(e, { position }) {
    console.log(`${position.left}, ${position.top}`);
    const { width, height } = this.state.box;
    const x = position.left;
    const y = position.top;
    const base = [
      { x: x + width, y: y + height * 0.25 },
      { x: x + width, y: y + height * 0.75 },
    ];
    const control = { x: x + width, y: y + height * 0.5 };
    const { destination } = this.state.pointer;
    this.setState({
      pointer: {
        base,
        control,
        destination,
      },
      box: {
        width,
        height,
        x,
        y,
      },
    });
  }

  onPointerDrag(e) {
    console.dir(e);
    const { base, control } = this.state.pointer;
    this.setState({
      pointer: {
        base,
        control,
        destination: {
          x: e.clientX,
          y: e.clientY,
        },
      },
    });
  }

  render() {
    const { start, backgroundColor } = this.props;
    const { base, destination, control } = this.state.pointer;

    return (

        <Resizable
          start={ start.box }
          customStyle={{ backgroundColor }}
          onDrag={ ::this.onBoxDrag }
        >
          Hello, world
        </Resizable>
        <svg width="2000" height="2000" style={{/* TODO: add absolute */}}>
          <path
            d={ `M ${ base[0].x } ${ base[0].y }
                 Q ${ control.x } ${ control.y } ${ destination.x } ${ destination.y }
                 Q ${ control.x } ${ control.y } ${ base[1].x } ${ base[1].y }` }
            fill={ backgroundColor }
          />
        </svg>
        <Draggable
          start={ start.pointer }
          onDrag={ ::this.onPointerDrag }
        >

        </Draggable>

    );
  }
}
<|endoftext|>


<commit_before>import Connector from './connector';
import {bind} from 'angular2/di';

export default function createRedux(store) {
  const _connector = new Connector(store);

  return bind('ngRedux').toFactory(() => {
    return {connect: _connector.connect, ...store};
  });
}

<commit_msg>Draft applyInjectableMiddleware<commit_after>import Connector from './connector';
import {bind, Injector} from 'angular2/di';
let redux = import('redux');

export function createRedux(store) {
  const _connector = new Connector(store);

  return bind('ngRedux').toFactory(() => {
    return {connect: _connector.connect, ...store};
  });
}

/*
 const createStoreWithMiddleware = applyInjectableMiddleware(thunk, 'promise')(createStore);
*/

export function applyInjectableMiddleware(middlewares) {
    const injector = new Injector();
    let resolvedMiddlewares = [];
    _.forEach(middlewares, middleware => {
        _.isString(middleware)
            ? resolvedMiddlewares.push(Injector.resolve(middleware))
            : resolvedMiddlewares.push(middleware)
    });

    return redux.applyMiddleware(...resolvedMiddlewares);
}

<|endoftext|>


<commit_before>'use strict';

import fs from 'fs';
import path from 'path';

//rewite promise, bluebird is more faster
require('babel-runtime/core-js/promise').default = require('bluebird');
global.Promise = require('bluebird');

import AutoReload from './util/auto_reload.js';
import WatchCompile from './util/watch_compile.js';
import Checker from './util/checker.js';

import './core/think.js';

export default class {
  /**
   * init
   * @param  {Object} options [project options]
   * @return {}         []
   */
  constructor(options = {}){
    //extend options to think
    think.extend(think, this.getPath(), options);

    //normalize path
    think.APP_PATH = path.normalize(think.APP_PATH);
    think.ROOT_PATH = path.normalize(think.ROOT_PATH);
    think.RESOURCE_PATH = path.normalize(think.RESOURCE_PATH);
    think.RUNTIME_PATH = path.normalize(think.RUNTIME_PATH);

    //parse data from process arguments
    let i = 2;
    let argv = process.argv[i];
    //get app mode from argv
    if (argv === 'production' || argv === 'development' || argv === 'testing') {
      think.env = argv;
      i++;
    }
    argv = process.argv[i];
    //get port or cli url from argv
    if (argv) {
      if (/^\d+$/.test(argv)) {
        think.port = argv;
      }else{
        think.cli = argv;
      }
    }
    //get app mode
    think.mode = this.getMode();
  }
  /**
   * get app mode
   * @return {Number} [app mode]
   */
  getMode(){
    let filepath = `${think.APP_PATH}/${think.dirname.controller}`;
    if (think.isDir(filepath)) {
      return think.mode_normal;
    }
    return think.mode_module;
  }
  /**
   * get app path
   * @return {Object} []
   */
  getPath(){
    let filepath = process.argv[1];
    let RESOURCE_PATH = path.dirname(filepath);
    let ROOT_PATH = path.dirname(RESOURCE_PATH);
    let APP_PATH = `${ROOT_PATH}${think.sep}app`;
    let RUNTIME_PATH = ROOT_PATH + think.sep + think.dirname.runtime;
    return {
      APP_PATH,
      RESOURCE_PATH,
      ROOT_PATH,
      RUNTIME_PATH
    };
  }
  /**
   * check node env
   * @return {Boolean} []
   */
  checkEnv(){
    this.checkNodeVersion();
  }

  /**
   * get app module list
   * @return {} []
   */
  getModule(){
    //only have default module in mini mode
    if (think.mode === think.mode_normal) {
      think.module = [think.config('default_module')];
      return think.module;
    }
    let modulePath = think.APP_PATH;
    if(!think.isDir(modulePath)){
      return [];
    }
    let modules = fs.readdirSync(modulePath);
    let denyModuleList = think.config('deny_module_list') || [];
    if (denyModuleList.length > 0) {
      modules = modules.filter(module => {
        if(module[0] === '.'){
          return;
        }
        if (denyModuleList.indexOf(module) === -1) {
          return module;
        }
      });
    }
    think.module = modules;
    return modules;
  }
  /**
   * load alias
   * @return {} []
   */
  loadAlias(){
    let aliasPath = `${think.THINK_LIB_PATH}/config/sys/alias.js`;
    thinkData.alias = think.safeRequire(aliasPath);
  }
  /**
   * load config
   * @return {} []
   */
  loadConfig(){
    think.getModuleConfig();
    //load modules config
    this.getModule().forEach(module => {
      think.getModuleConfig(module);
    });
  }

  /**
   * load route
   * @return {} []
   */
  loadRoute(){
    think.route();
  }
  /**
   * load adapter
   * @return {} []
   */
  loadAdapter(){
    think.adapter.load();
  }
  /**
   * load middleware
   * @return {} []
   */
  loadMiddleware(){
    let paths = [
      `${think.THINK_LIB_PATH}${think.sep}middleware`,
      `${think.getPath(undefined, think.dirname.middleware)}`
    ];
    think.alias('middleware', paths);
    //middleware base class
    think.middleware.base = think.require('middleware_base');
  }
  /**
   * load hook
   * @return {} []
   */
  loadHook(){
    let hookPath = `${think.THINK_LIB_PATH}/config/hook.js`;
    thinkData.hook = think.extend({}, think.safeRequire(hookPath));

    let file = `${think.getPath(undefined, think.dirname.config)}/hook.js`;
    let data = think.extend({}, think.safeRequire(file));
    for(let key in data){
      think.hook.set(key, data[key]);
    }
  }
  /**
   * load controller, model, logic, service files
   * @return {} []
   */
  loadMVC(){
    let types = {
      model: ['base', 'relation', 'mongo', 'adv'],
      controller: ['base', 'rest'],
      logic: ['base'],
      service: ['base']
    };
    for(let itemType in types){
      think.alias(itemType, `${think.THINK_LIB_PATH}${think.sep}${itemType}`);
      types[itemType].forEach(item => {
        think[itemType][item] = think.require(`${itemType}_${item}`);
      });
      think.module.forEach(module => {
        let moduleType = `${module}/${itemType}`; //can not use think.sep
        let filepath = think.getPath(module, think.dirname[itemType]);
        think.alias(moduleType, filepath, true);
      });
    }
  }
  /**
   * load sub controller
   * @return {} []
   */
  loadSubController(){
    think.module.forEach(module => {
      let filepath = think.getPath(module, think.dirname.controller);
      let subControllers = think.getFiles(filepath).filter(item => {
        if(item.indexOf(think.sep) === -1){
          return;
        }
        if(path.extname(item) !== '.js'){
          return;
        }
        return true;
      }).map(item => {
        return item.slice(0, -3).replace(/\\/g, '/');
      }).sort((a, b) => {
        let al = a.split('/').length;
        let bl = b.split('/').length;
        if(al === bl){
          return a < b ? 1 : -1;
        }
        return al < bl ? 1 : -1;
      });
      if(subControllers.length){
        thinkData.subController[module] = subControllers;
      }
    });
  }
  /**
   * load bootstrap
   * @return {} []
   */
  loadBootstrap(){
    let paths = [
      `${think.THINK_LIB_PATH}${think.sep}bootstrap`,
      think.getPath(think.dirname.common, think.dirname.bootstrap)
    ];
    paths.forEach(item => {
      if (!think.isDir(item)) {
        return;
      }
      let files = fs.readdirSync(item);

      //must reload all bootstrap files.
      if (think.config('auto_reload')) {
        AutoReload.rewriteSysModuleLoad();
        var instance = new AutoReload(item, ()=>{});
        instance.clearFilesCache(files.map(file => item + think.sep + file));
      }

      files.forEach(file => {
        let extname = path.extname(file);
        if(extname !== '.js'){
          return;
        }
        think.safeRequire(`${item}${think.sep}${file}`);
      });
    });
  }
  /**
   * load template file
   * add template files to cache
   * @return {} []
   */
  loadTemplate(){
    let data = {};

    let add = filepath => {
      if (!think.isDir(filepath)) {
        return;
      }
      let files = think.getFiles(filepath, true);
      files.forEach(file => {
        let key = `${filepath}${think.sep}${file}`;
        data[key] = true;
      });
    };
    let {root_path} = think.config('view');
    if(root_path){
      add(path.normalize(root_path));
    }else{
      think.module.forEach(module => {
        add(think.getPath(module, think.dirname.view));
      });
    }
    thinkData.template = data;
  }
  /**
   * load system error message
   * @return {} []
   */
  loadError(){
    thinkData.error = think.safeRequire(think.THINK_LIB_PATH + `/config/sys/error.js`);
  }
  /**
   * clear all cache for reload
   * @return {void} []
   */
  clearData(){
    thinkData.alias = {};
    thinkData.export = {};
    thinkData.config = {};
    thinkData.hook = {};
    thinkData.template = {};
    thinkData.middleware = {};
    thinkData.subController = {};
    thinkData.route = null;
  }
  /**
   * load all config or modules
   * @return {} []
   */
  load(){

    this.loadConfig();
    this.loadRoute();
    this.loadAlias();
    this.loadAdapter();
    this.loadMiddleware();
    this.loadMVC();
    this.loadSubController();
    this.loadHook();
    this.loadTemplate();
    this.loadError();
    this.loadBootstrap();

    Checker.checkModuleConfig();

    think.toFastProperties(thinkData.alias);
    think.toFastProperties(thinkData.config);
    think.toFastProperties(thinkData.hook);
    think.toFastProperties(thinkData.middleware);
    think.toFastProperties(thinkData.error);
    think.toFastProperties(thinkData.template);
    think.toFastProperties(thinkData.subController);

    //console.log(thinkData.alias)
    //console.log(eval('%HasFastProperties(thinkData.template)'))
  }
  /**
   * capture error
   * @return {} []
   */
  captureError(){
    process.on('uncaughtException', err => {
      let msg = err.message;
      err = think.error(err, 'port:' + think.config('port'));
      think.log(err);
      if(msg.indexOf(' EADDRINUSE ') > -1){
        process.exit();
      }
    });
    process.on('unhandledRejection', err => {
      think.log(err);
    });
  }
  /**
   * start
   * @return {} []
   */
  start(){
    Checker.checkNodeVersion();
    Checker.checkFileName();
    Checker.checkDependencies();

    this.load();
    this.captureError();
    if (think.config('auto_reload')) {
      this.autoReload();
    }
  }
  /**
   * auto reload user modified files
   * @return {} []
   */
  autoReload(){
    //it auto reload by watch compile
    if(this.compileCallback){
      return;
    }
    let instance = this.getReloadInstance();
    instance.run();
  }
  /**
   * get auto reload class instance
   * @param  {String} srcPath []
   * @return {Object}         []
   */
  getReloadInstance(srcPath){
    srcPath = srcPath || think.APP_PATH;
    AutoReload.rewriteSysModuleLoad();
    let instance = new AutoReload(srcPath, () => {
      this.clearData();
      this.load();
    });
    return instance;
  }
  /**
   * use babel compile code
   * @return {} []
   */
  compile(srcPath, outPath, options = {}){
    if(think.isObject(srcPath)){
      options = srcPath;
      srcPath = '';
    }else if(srcPath === true){
      options = {log: true};
      srcPath = '';
    }
    srcPath = srcPath || `${think.ROOT_PATH}${think.sep}src`;
    outPath = outPath || think.APP_PATH;

    if(!think.isDir(srcPath)){
      return;
    }
    let reloadInstance = this.getReloadInstance(outPath);
    this.compileCallback = changedFiles => {
      process.nextTick(() => reloadInstance.clearFilesCache(changedFiles));
    };

    let instance = new WatchCompile(srcPath, outPath, options, this.compileCallback);
    instance.run();

    think.autoCompile = true;
    //get app mode
    think.mode = this.getMode();
  }
  /**
   * run
   * @return {} []
   */
  run(){
    this.start();
    return think.require('app').run();
  }
  /**
   * load, convenient for plugins
   * @return {} []
   */
  static load(){
    let instance = new this();
    instance.load();
  }
}

module.exports = exports.default;<commit_msg>Fix: detect app mode error when use mode_normal<commit_after>'use strict';

import fs from 'fs';
import path from 'path';

//rewite promise, bluebird is more faster
require('babel-runtime/core-js/promise').default = require('bluebird');
global.Promise = require('bluebird');

import AutoReload from './util/auto_reload.js';
import WatchCompile from './util/watch_compile.js';
import Checker from './util/checker.js';

import './core/think.js';

export default class {
  /**
   * init
   * @param  {Object} options [project options]
   * @return {}         []
   */
  constructor(options = {}){
    //extend options to think
    think.extend(think, this.getPath(), options);

    //normalize path
    think.APP_PATH = path.normalize(think.APP_PATH);
    think.ROOT_PATH = path.normalize(think.ROOT_PATH);
    think.RESOURCE_PATH = path.normalize(think.RESOURCE_PATH);
    think.RUNTIME_PATH = path.normalize(think.RUNTIME_PATH);

    //parse data from process arguments
    let i = 2;
    let argv = process.argv[i];
    //get app mode from argv
    if (argv === 'production' || argv === 'development' || argv === 'testing') {
      think.env = argv;
      i++;
    }
    argv = process.argv[i];
    //get port or cli url from argv
    if (argv) {
      if (/^\d+$/.test(argv)) {
        think.port = argv;
      }else{
        think.cli = argv;
      }
    }
    //get app mode
    think.mode = this.getMode();
  }
  /**
   * get app mode
   * @return {Number} [app mode]
   */
  getMode(){
    let filepath = `${think.APP_PATH}/${think.dirname.controller}`;
    if (think.isDir(filepath)) {
      return think.mode_normal;
    }
    return think.mode_module;
  }
  /**
   * get app path
   * @return {Object} []
   */
  getPath(){
    let filepath = process.argv[1];
    let RESOURCE_PATH = path.dirname(filepath);
    let ROOT_PATH = path.dirname(RESOURCE_PATH);
    let APP_PATH = `${ROOT_PATH}${think.sep}app`;
    let RUNTIME_PATH = ROOT_PATH + think.sep + think.dirname.runtime;
    return {
      APP_PATH,
      RESOURCE_PATH,
      ROOT_PATH,
      RUNTIME_PATH
    };
  }
  /**
   * check node env
   * @return {Boolean} []
   */
  checkEnv(){
    this.checkNodeVersion();
  }

  /**
   * get app module list
   * @return {} []
   */
  getModule(){
    //only have default module in mini mode
    if (think.mode === think.mode_normal) {
      think.module = [think.config('default_module')];
      return think.module;
    }
    let modulePath = think.APP_PATH;
    if(!think.isDir(modulePath)){
      return [];
    }
    let modules = fs.readdirSync(modulePath);
    let denyModuleList = think.config('deny_module_list') || [];
    if (denyModuleList.length > 0) {
      modules = modules.filter(module => {
        if(module[0] === '.'){
          return;
        }
        if (denyModuleList.indexOf(module) === -1) {
          return module;
        }
      });
    }
    think.module = modules;
    return modules;
  }
  /**
   * load alias
   * @return {} []
   */
  loadAlias(){
    let aliasPath = `${think.THINK_LIB_PATH}/config/sys/alias.js`;
    thinkData.alias = think.safeRequire(aliasPath);
  }
  /**
   * load config
   * @return {} []
   */
  loadConfig(){
    think.getModuleConfig();
    //load modules config
    this.getModule().forEach(module => {
      think.getModuleConfig(module);
    });
  }

  /**
   * load route
   * @return {} []
   */
  loadRoute(){
    think.route();
  }
  /**
   * load adapter
   * @return {} []
   */
  loadAdapter(){
    think.adapter.load();
  }
  /**
   * load middleware
   * @return {} []
   */
  loadMiddleware(){
    let paths = [
      `${think.THINK_LIB_PATH}${think.sep}middleware`,
      `${think.getPath(undefined, think.dirname.middleware)}`
    ];
    think.alias('middleware', paths);
    //middleware base class
    think.middleware.base = think.require('middleware_base');
  }
  /**
   * load hook
   * @return {} []
   */
  loadHook(){
    let hookPath = `${think.THINK_LIB_PATH}/config/hook.js`;
    thinkData.hook = think.extend({}, think.safeRequire(hookPath));

    let file = `${think.getPath(undefined, think.dirname.config)}/hook.js`;
    let data = think.extend({}, think.safeRequire(file));
    for(let key in data){
      think.hook.set(key, data[key]);
    }
  }
  /**
   * load controller, model, logic, service files
   * @return {} []
   */
  loadMVC(){
    let types = {
      model: ['base', 'relation', 'mongo', 'adv'],
      controller: ['base', 'rest'],
      logic: ['base'],
      service: ['base']
    };
    for(let itemType in types){
      think.alias(itemType, `${think.THINK_LIB_PATH}${think.sep}${itemType}`);
      types[itemType].forEach(item => {
        think[itemType][item] = think.require(`${itemType}_${item}`);
      });
      think.module.forEach(module => {
        let moduleType = `${module}/${itemType}`; //can not use think.sep
        let filepath = think.getPath(module, think.dirname[itemType]);
        think.alias(moduleType, filepath, true);
      });
    }
  }
  /**
   * load sub controller
   * @return {} []
   */
  loadSubController(){
    think.module.forEach(module => {
      let filepath = think.getPath(module, think.dirname.controller);
      let subControllers = think.getFiles(filepath).filter(item => {
        if(item.indexOf(think.sep) === -1){
          return;
        }
        if(path.extname(item) !== '.js'){
          return;
        }
        return true;
      }).map(item => {
        return item.slice(0, -3).replace(/\\/g, '/');
      }).sort((a, b) => {
        let al = a.split('/').length;
        let bl = b.split('/').length;
        if(al === bl){
          return a < b ? 1 : -1;
        }
        return al < bl ? 1 : -1;
      });
      if(subControllers.length){
        thinkData.subController[module] = subControllers;
      }
    });
  }
  /**
   * load bootstrap
   * @return {} []
   */
  loadBootstrap(){
    let paths = [
      `${think.THINK_LIB_PATH}${think.sep}bootstrap`,
      think.getPath(think.dirname.common, think.dirname.bootstrap)
    ];
    paths.forEach(item => {
      if (!think.isDir(item)) {
        return;
      }
      let files = fs.readdirSync(item);

      //must reload all bootstrap files.
      if (think.config('auto_reload')) {
        AutoReload.rewriteSysModuleLoad();
        var instance = new AutoReload(item, ()=>{});
        instance.clearFilesCache(files.map(file => item + think.sep + file));
      }

      files.forEach(file => {
        let extname = path.extname(file);
        if(extname !== '.js'){
          return;
        }
        think.safeRequire(`${item}${think.sep}${file}`);
      });
    });
  }
  /**
   * load template file
   * add template files to cache
   * @return {} []
   */
  loadTemplate(){
    let data = {};

    let add = filepath => {
      if (!think.isDir(filepath)) {
        return;
      }
      let files = think.getFiles(filepath, true);
      files.forEach(file => {
        let key = `${filepath}${think.sep}${file}`;
        data[key] = true;
      });
    };
    let {root_path} = think.config('view');
    if(root_path){
      add(path.normalize(root_path));
    }else{
      think.module.forEach(module => {
        add(think.getPath(module, think.dirname.view));
      });
    }
    thinkData.template = data;
  }
  /**
   * load system error message
   * @return {} []
   */
  loadError(){
    thinkData.error = think.safeRequire(think.THINK_LIB_PATH + `/config/sys/error.js`);
  }
  /**
   * clear all cache for reload
   * @return {void} []
   */
  clearData(){
    thinkData.alias = {};
    thinkData.export = {};
    thinkData.config = {};
    thinkData.hook = {};
    thinkData.template = {};
    thinkData.middleware = {};
    thinkData.subController = {};
    thinkData.route = null;
  }
  /**
   * load all config or modules
   * @return {} []
   */
  load(){

    this.loadConfig();
    this.loadRoute();
    this.loadAlias();
    this.loadAdapter();
    this.loadMiddleware();
    this.loadMVC();
    this.loadSubController();
    this.loadHook();
    this.loadTemplate();
    this.loadError();
    this.loadBootstrap();

    Checker.checkModuleConfig();

    think.toFastProperties(thinkData.alias);
    think.toFastProperties(thinkData.config);
    think.toFastProperties(thinkData.hook);
    think.toFastProperties(thinkData.middleware);
    think.toFastProperties(thinkData.error);
    think.toFastProperties(thinkData.template);
    think.toFastProperties(thinkData.subController);

    //console.log(thinkData.alias)
    //console.log(eval('%HasFastProperties(thinkData.template)'))
  }
  /**
   * capture error
   * @return {} []
   */
  captureError(){
    process.on('uncaughtException', err => {
      let msg = err.message;
      err = think.error(err, 'port:' + think.config('port'));
      think.log(err);
      if(msg.indexOf(' EADDRINUSE ') > -1){
        process.exit();
      }
    });
    process.on('unhandledRejection', err => {
      think.log(err);
    });
  }
  /**
   * start
   * @return {} []
   */
  start(){
    Checker.checkNodeVersion();
    Checker.checkFileName();
    Checker.checkDependencies();

    this.load();
    this.captureError();
    if (think.config('auto_reload')) {
      this.autoReload();
    }
  }
  /**
   * auto reload user modified files
   * @return {} []
   */
  autoReload(){
    //it auto reload by watch compile
    if(this.compileCallback){
      return;
    }
    let instance = this.getReloadInstance();
    instance.run();
  }
  /**
   * get auto reload class instance
   * @param  {String} srcPath []
   * @return {Object}         []
   */
  getReloadInstance(srcPath){
    srcPath = srcPath || think.APP_PATH;
    AutoReload.rewriteSysModuleLoad();
    let instance = new AutoReload(srcPath, () => {
      this.clearData();
      this.load();
    });
    return instance;
  }
  /**
   * use babel compile code
   * @return {} []
   */
  compile(srcPath, outPath, options = {}){
    if(think.isObject(srcPath)){
      options = srcPath;
      srcPath = '';
    }else if(srcPath === true){
      options = {log: true};
      srcPath = '';
    }
    srcPath = srcPath || `${think.ROOT_PATH}${think.sep}src`;
    outPath = outPath || think.APP_PATH;

    if(!think.isDir(srcPath)){
      return;
    }
    let reloadInstance = this.getReloadInstance(outPath);
    let _getMode = false;
    this.compileCallback = changedFiles => {
      if(!_getMode){
        _getMode = true;
        //get app mode
        think.mode = this.getMode();
      }

      reloadInstance.clearFilesCache(changedFiles);
    };

    let instance = new WatchCompile(srcPath, outPath, options, this.compileCallback);
    instance.run();

    think.autoCompile = true;
  }
  /**
   * run
   * @return {} []
   */
  run(){
    this.start();
    return think.require('app').run();
  }
  /**
   * load, convenient for plugins
   * @return {} []
   */
  static load(){
    let instance = new this();
    instance.load();
  }
}

module.exports = exports.default;<|endoftext|>


<commit_before>/* jshint globalstrict: true */

'use strict';

function Scope () {
  this.$$watchers = [];
  this.$$lastDirtyWatch = null;
  this.$$asyncQueue = [];
  this.$$applyAsyncQueue = [];
  this.$$applyAsyncId = null;
  this.$$postDigestQueue = [];
  this.$$phase = null;
}

/* use for initialize the last attribute to be unique
  -- javascript function is reference value.
 */
function initWatchVal() {}

Scope.prototype.$areEqual = function(newValue, oldValue, valueEq){
  if (valueEq) {
    return _.isEqual(newValue, oldValue);
  } else {
    return newValue === oldValue ||
      (typeof newValue === "number" && typeof oldValue === "number" &&
        isNaN(newValue) && isNaN(oldValue));
  }
};

Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) {
  var self = this;
  var watcher = {
    watchFn: watchFn,
    listenerFn: listenerFn || function(){},
    valueEq: !!valueEq,
    last: initWatchVal
  };
  this.$$watchers.unshift(watcher);
  this.$$lastDirtyWatch = null;
  return function () {
    var index = self.$$watchers.indexOf(watcher);
    if (index >= 0) {
      self.$$watchers.splice(index, 1);
      self.$$lastDirtyWatch = null;
    }
  };
};

Scope.prototype.$$digestOnce = function() {
	var self = this;
  var newValue,oldValue,dirty;

  _.forEachRight(this.$$watchers, function(watcher) {
    try {
      if (watcher) {
        newValue = watcher.watchFn(self);
        oldValue = watcher.last;
        if (!self.$areEqual(newValue, oldValue, watcher.valueEq)) {
          self.$$lastDirtyWatch = watcher;
          watcher.last = (watcher.valueEq ? _.cloneDeep(newValue) : newValue);
          watcher.listenerFn(newValue,
            (oldValue === initWatchVal ? newValue : oldValue),
            self);
          dirty = true;
        } else if (self.$$lastDirtyWatch === watcher){
          return false;
        }
      }
    } catch (e) {
      console.error(e);
    }
  });
  return dirty;
};

Scope.prototype.$digest = function(){
  var ttl = 10;
  var dirty;
  this.$$lastDirtyWatch = null;
  this.$beginPhase("$digest");

  if (this.$$applyAsyncId) {
    clearTimeout(this.$$applyAsyncId);
    this.$$flushApplyAsync();
  }

  do{
    while (this.$$asyncQueue.length) {
      try {
        var asyncTask = this.$$asyncQueue.shift();
        asyncTask.scope.$eval(asyncTask.expression);
      } catch (e) {
        console.error(e);
      }
    }
    dirty = this.$$digestOnce();
    if ((dirty || this.$$asyncQueue.length) && !(ttl--)) {
      this.$clearPhase();
      throw "10 digest iterations reached";
    }
  } while (dirty || this.$$asyncQueue.length);
  this.$clearPhase();

  while (this.$$postDigestQueue.length) {
    try {
      this.$$postDigestQueue.shift()();
    } catch (e) {
      console.error(e);
    }
  }
};

Scope.prototype.$eval = function(expr, locals){
  return expr(this, locals);
};

Scope.prototype.$apply = function(expr, locals){
  try {
    this.$beginPhase("$apply");
    return this.$eval(expr, locals);
  } finally {
    this.$clearPhase();
    this.$digest();
  }
};

Scope.prototype.$evalAsync = function(expr) {
  var self = this;
  if (!self.$$phase && !self.$$asyncQueue.length) {
    setTimeout(function () {
      if (self.$$asyncQueue.length) {
        self.$digest();
      }
    }, 0);
  }
  this.$$asyncQueue.push({scope: this, expression: expr});
};

Scope.prototype.$applyAsync = function(expr) {
  var self = this;
  self.$$applyAsyncQueue.push(function(){
    self.$eval(expr);
  });
  if (self.$$applyAsyncId === null){
    self.$$applyAsyncId = setTimeout(function(){
      self.$apply(_.bind(self.$$flushApplyAsync, self));
    }, 0);
  }
};
Scope.prototype.$$flushApplyAsync = function () {
  while (this.$$applyAsyncQueue.length) {
    try {
      this.$$applyAsyncQueue.shift()();
    } catch (e) {
      console.error(e);
    }
  }
  this.$$applyAsyncId = null;
};

Scope.prototype.$$postDigest = function(fn) {
  this.$$postDigestQueue.push(fn);
};

Scope.prototype.$beginPhase = function(phase) {
  if (this.$$phase) {
    throw this.$$phase + " already in progress.";
  }
  this.$$phase = phase;
};
Scope.prototype.$clearPhase = function() {
  this.$$phase = null;
};
<commit_msg>feat(scope): add $watchGroup method for observing a set of expressions<commit_after>/* jshint globalstrict: true */

'use strict';

function Scope () {
  this.$$watchers = [];
  this.$$lastDirtyWatch = null;
  this.$$asyncQueue = [];
  this.$$applyAsyncQueue = [];
  this.$$applyAsyncId = null;
  this.$$postDigestQueue = [];
  this.$$phase = null;
}

/* use for initialize the last attribute to be unique
  -- javascript function is reference value.
 */
function initWatchVal() {}

Scope.prototype.$areEqual = function(newValue, oldValue, valueEq){
  if (valueEq) {
    return _.isEqual(newValue, oldValue);
  } else {
    return newValue === oldValue ||
      (typeof newValue === "number" && typeof oldValue === "number" &&
        isNaN(newValue) && isNaN(oldValue));
  }
};

Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) {
  var self = this;
  var watcher = {
    watchFn: watchFn,
    listenerFn: listenerFn || function(){},
    valueEq: !!valueEq,
    last: initWatchVal
  };
  this.$$watchers.unshift(watcher);
  this.$$lastDirtyWatch = null;
  return function () {
    var index = self.$$watchers.indexOf(watcher);
    if (index >= 0) {
      self.$$watchers.splice(index, 1);
      self.$$lastDirtyWatch = null;
    }
  };
};

Scope.prototype.$watchGroup = function(watchFns, listenerFn){
  var self = this;
  var newValues = new Array(watchFns.length);
  var oldValues = new Array(watchFns.length);
  _.forEach(watchFns, function(watchFn, i){
    self.$watch(watchFn, function(newValue, oldValue){
      newValues[i] = newValue;
      oldValues[i] = oldValue;
      listenerFn(newValues, oldValues, self);
    });
  });
};

Scope.prototype.$$digestOnce = function() {
	var self = this;
  var newValue,oldValue,dirty;

  _.forEachRight(this.$$watchers, function(watcher) {
    try {
      if (watcher) {
        newValue = watcher.watchFn(self);
        oldValue = watcher.last;
        if (!self.$areEqual(newValue, oldValue, watcher.valueEq)) {
          self.$$lastDirtyWatch = watcher;
          watcher.last = (watcher.valueEq ? _.cloneDeep(newValue) : newValue);
          watcher.listenerFn(newValue,
            (oldValue === initWatchVal ? newValue : oldValue),
            self);
          dirty = true;
        } else if (self.$$lastDirtyWatch === watcher){
          return false;
        }
      }
    } catch (e) {
      console.error(e);
    }
  });
  return dirty;
};

Scope.prototype.$digest = function(){
  var ttl = 10;
  var dirty;
  this.$$lastDirtyWatch = null;
  this.$beginPhase("$digest");

  if (this.$$applyAsyncId) {
    clearTimeout(this.$$applyAsyncId);
    this.$$flushApplyAsync();
  }

  do{
    while (this.$$asyncQueue.length) {
      try {
        var asyncTask = this.$$asyncQueue.shift();
        asyncTask.scope.$eval(asyncTask.expression);
      } catch (e) {
        console.error(e);
      }
    }
    dirty = this.$$digestOnce();
    if ((dirty || this.$$asyncQueue.length) && !(ttl--)) {
      this.$clearPhase();
      throw "10 digest iterations reached";
    }
  } while (dirty || this.$$asyncQueue.length);
  this.$clearPhase();

  while (this.$$postDigestQueue.length) {
    try {
      this.$$postDigestQueue.shift()();
    } catch (e) {
      console.error(e);
    }
  }
};

Scope.prototype.$eval = function(expr, locals){
  return expr(this, locals);
};

Scope.prototype.$apply = function(expr, locals){
  try {
    this.$beginPhase("$apply");
    return this.$eval(expr, locals);
  } finally {
    this.$clearPhase();
    this.$digest();
  }
};

Scope.prototype.$evalAsync = function(expr) {
  var self = this;
  if (!self.$$phase && !self.$$asyncQueue.length) {
    setTimeout(function () {
      if (self.$$asyncQueue.length) {
        self.$digest();
      }
    }, 0);
  }
  this.$$asyncQueue.push({scope: this, expression: expr});
};

Scope.prototype.$applyAsync = function(expr) {
  var self = this;
  self.$$applyAsyncQueue.push(function(){
    self.$eval(expr);
  });
  if (self.$$applyAsyncId === null){
    self.$$applyAsyncId = setTimeout(function(){
      self.$apply(_.bind(self.$$flushApplyAsync, self));
    }, 0);
  }
};
Scope.prototype.$$flushApplyAsync = function () {
  while (this.$$applyAsyncQueue.length) {
    try {
      this.$$applyAsyncQueue.shift()();
    } catch (e) {
      console.error(e);
    }
  }
  this.$$applyAsyncId = null;
};

Scope.prototype.$$postDigest = function(fn) {
  this.$$postDigestQueue.push(fn);
};

Scope.prototype.$beginPhase = function(phase) {
  if (this.$$phase) {
    throw this.$$phase + " already in progress.";
  }
  this.$$phase = phase;
};
Scope.prototype.$clearPhase = function() {
  this.$$phase = null;
};
<|endoftext|>


<commit_before>import Rx, {Observable as O} from "rx"

const noop = () => undefined

const objKeys = x =>
  x ? Object.keys(x) : []

const extend = Object.assign

const isFun = x => typeof x === "function"
const isObj = x => typeof x === "object" && x.constructor === Object

const keyNotIn = keys => {
  const hash = keys.reduce((acc, k) => (acc[k] = true) && acc, {})
  return s => !hash[s.key]
}

const from = (in$, key) =>
  in$.filter(s => s.key === key).map(s => s.val)

const to = (out$, key) =>
  out$.map(val => ({key, val}))

const mapValuesWhen = (obj, fn) =>
  objKeys(obj).reduce((o, k) => {
    const a = fn(obj[k]);
    return a ? ((o[k] = a) && o) : o
  }, {})

export default function TSERS(main, interpreters) {
  if (!isObj(interpreters) || objKeys(interpreters).length === 0)
    throw new Error("Interpreters must be an object of initializer functions (at least one interpreter required)")

  const mapListById = mapListBy.bind(null, x => x.id)
  const CommonTransformers = {mux, demux, loop, mapListById, mapListBy, demuxCombined}
  const InterpreterImports = CommonTransformers
  const signalsAndExecutors = mapValuesWhen(interpreters, d => d(InterpreterImports))

  const input = {
    ...CommonTransformers,
    ...mapValuesWhen(signalsAndExecutors, d => d[0] || (!isFun(d) && d))
  }
  const executors =
    mapValuesWhen(signalsAndExecutors, d => d[1] || (isFun(d) && d))

  if (objKeys(executors).length === 0)
    throw new Error("At least one executor is required")

  const noopd = {dispose: noop}
  const out$ = main(input)
  if (!O.isObservable(out$))
    throw new Error("Main must return an Observable")
  const [out] = demux(out$, ...objKeys(interpreters))

  return new Rx.CompositeDisposable(objKeys(out).map(key =>
    executors[key] ? executors[key](out[key]) || noopd : noopd
  ))
}

export function mux(input, rest$) {
  const muxed$ = O.merge(...objKeys(input).map(k => to(input[k], k)))
  return rest$ ? muxed$.merge(rest$) : muxed$
}

export function demux(out$, ...keys) {
  const demuxed = keys.reduce((o, k) => (o[k] = from(out$, k)) && o, {})
  const rest$ = out$.filter(keyNotIn(keys))
  return [demuxed, rest$]
}

export function demuxCombined(list$$, ...keys) {
  const combine = k => list => list.length === 0 ? O.just([])
    : O.combineLatest(...list.map(out$ => demux(out$, k)[0][k]))
  const demuxed = keys.reduce((o, k) => (o[k] = list$$.flatMapLatest(combine(k))) && o, {})
  const rest$ = list$$.flatMapLatest(list => O.merge(list.map(out$ => out$.filter(keyNotIn(keys)))))
  return [demuxed, rest$]
}

export function loop(input$, main) {
  let lo = null
  const in$ = input$
    .doOnCompleted(() => lo && lo.onCompleted() && (lo = null))
    .merge(O.create(o => (lo = o) && (() => lo = null)))
    .share()
  const [out$, loop$] = main(in$)
  return out$.merge(loop$.filter(val => lo && lo.onNext(val) && false)).share()
}

export function mapListBy(identity, list$, it, replay = ["DOM"]) {
  return O.using(() => new Cache(replay), cache => {
    return list$
      .distinctUntilChanged(items => items.map(item => identity(item)), (a, b) => {
        if (a.length !== b.length) return false
        for (var i = 0; i < a.length; i++) {
          if (a[i] !== b[i]) return false
        }
        return true
      })
      .map(items => {
        const itemByKey = {}
        items.forEach((item, idx) => itemByKey[identity(item)] = {item, idx})
        items.forEach((item, idx) => {
          const key = identity(item)
          if (!cache.contains(key)) {
            const proxy = makeProxy()
            cache.put(key, it(key, proxy.asObservable()), idx, proxy)
          } else {
            cache.update(key, idx, item)
          }
        })
        cache.keys().forEach(key => {
          if (!(key in itemByKey)) {
            cache.del(key)
          }
        })
        return cache.list()
      })
  }).shareReplay(1)

  function makeProxy(initial) {
    return new Rx.BehaviorSubject(initial)
  }
}

function Cache(toReplay) {
  this.cache = {}
  this.toReplay = toReplay.reduce((acc, k) => (acc[k] = true) && acc, {})
}

extend(Cache.prototype, {
  contains(key) {
    return key in this.cache
  },
  put(key, sinks, idx, proxy) {
    const streams = {}
    const disposable = new Rx.CompositeDisposable()
    objKeys(sinks).map(name => {
      const sink$ = this.toReplay[name] ? sinks[name].replay(null, 1) : sinks[name].publish()
      disposable.add(sink$.connect())
      streams[name] = sink$
    })
    this.cache[key] = {
      key,
      streams,
      disposable,
      idx,
      proxy
    }
  },
  get(key) {
    return this.cache[key].item
  },
  update(key, idx, item) {
    this.cache[key].idx = idx
    this.cache[key].proxy.onNext(item)
  },
  del(key) {
    const cached = this.cache[key]
    if (cached) {
      delete this.cache[key]
      cached.proxy.dispose()
      cached.disposable.dispose()
    }
  },
  keys() {
    return objKeys(this.cache)
  },
  list() {
    const list = objKeys(this.cache).map(k => this.cache[k])
    list.sort((a, b) => a.idx - b.idx)
    return list.map(x => x.streams)
  },
  dispose() {
    this.toReplay = []
    objKeys(this.cache).forEach(key => {
      this.del(key)
    })
  }
})
<commit_msg>Remove proxy object from mapListBy <commit_after>import Rx, {Observable as O} from "rx"

const noop = () => undefined

const objKeys = x =>
  x ? Object.keys(x) : []

const extend = Object.assign

const isFun = x => typeof x === "function"
const isObj = x => typeof x === "object" && x.constructor === Object

const keyNotIn = keys => {
  const hash = keys.reduce((acc, k) => (acc[k] = true) && acc, {})
  return s => !hash[s.key]
}

const from = (in$, key) =>
  in$.filter(s => s.key === key).map(s => s.val)

const to = (out$, key) =>
  out$.map(val => ({key, val}))

const mapValuesWhen = (obj, fn) =>
  objKeys(obj).reduce((o, k) => {
    const a = fn(obj[k]);
    return a ? ((o[k] = a) && o) : o
  }, {})

export default function TSERS(main, interpreters) {
  if (!isObj(interpreters) || objKeys(interpreters).length === 0)
    throw new Error("Interpreters must be an object of initializer functions (at least one interpreter required)")

  const mapListById = mapListBy.bind(null, x => x.id)
  const CommonTransformers = {mux, demux, loop, mapListById, mapListBy, demuxCombined}
  const InterpreterImports = CommonTransformers
  const signalsAndExecutors = mapValuesWhen(interpreters, d => d(InterpreterImports))

  const input = {
    ...CommonTransformers,
    ...mapValuesWhen(signalsAndExecutors, d => d[0] || (!isFun(d) && d))
  }
  const executors =
    mapValuesWhen(signalsAndExecutors, d => d[1] || (isFun(d) && d))

  if (objKeys(executors).length === 0)
    throw new Error("At least one executor is required")

  const noopd = {dispose: noop}
  const out$ = main(input)
  if (!O.isObservable(out$))
    throw new Error("Main must return an Observable")
  const [out] = demux(out$, ...objKeys(interpreters))

  return new Rx.CompositeDisposable(objKeys(out).map(key =>
    executors[key] ? executors[key](out[key]) || noopd : noopd
  ))
}

export function mux(input, rest$) {
  const muxed$ = O.merge(...objKeys(input).map(k => to(input[k], k)))
  return rest$ ? muxed$.merge(rest$) : muxed$
}

export function demux(out$, ...keys) {
  const demuxed = keys.reduce((o, k) => (o[k] = from(out$, k)) && o, {})
  const rest$ = out$.filter(keyNotIn(keys))
  return [demuxed, rest$]
}

export function demuxCombined(list$$, ...keys) {
  const combine = k => list => list.length === 0 ? O.just([])
    : O.combineLatest(...list.map(out$ => demux(out$, k)[0][k]))
  const demuxed = keys.reduce((o, k) => (o[k] = list$$.flatMapLatest(combine(k))) && o, {})
  const rest$ = list$$.flatMapLatest(list => O.merge(list.map(out$ => out$.filter(keyNotIn(keys)))))
  return [demuxed, rest$]
}

export function loop(input$, main) {
  let lo = null
  const in$ = input$
    .doOnCompleted(() => lo && lo.onCompleted() && (lo = null))
    .merge(O.create(o => (lo = o) && (() => lo = null)))
    .share()
  const [out$, loop$] = main(in$)
  return out$.merge(loop$.filter(val => lo && lo.onNext(val) && false)).share()
}

export function mapListBy(identity, list$, it, replay = ["DOM"]) {
  return O.using(() => new Cache(replay), cache => {
    return list$
      .distinctUntilChanged(items => items.map(item => identity(item)), (a, b) => {
        if (a.length !== b.length) return false
        for (var i = 0; i < a.length; i++) {
          if (a[i] !== b[i]) return false
        }
        return true
      })
      .map(items => {
        const itemByKey = {}
        items.forEach((item, idx) => itemByKey[identity(item)] = {item, idx})
        items.forEach((item, idx) => {
          const key = identity(item)
          if (!cache.contains(key)) {
            const item$ = list$.map(findByIdentityEq(identity, key)).share()
            cache.put(key, it(key, item$), idx)
          } else {
            cache.update(key, idx, item)
          }
        })
        cache.keys().forEach(key => {
          if (!(key in itemByKey)) {
            cache.del(key)
          }
        })
        return cache.list()
      })
  }).shareReplay(1)

  function findByIdentityEq(identity, key) {
    return items => {
      for (var i = 0; i < items.length; i++) {
        if (identity(items[i]) === key) return items[i]
      }
    }
  }
}

function Cache(toReplay) {
  this.cache = {}
  this.toReplay = toReplay.reduce((acc, k) => (acc[k] = true) && acc, {})
}

extend(Cache.prototype, {
  contains(key) {
    return key in this.cache
  },
  put(key, sinks, idx) {
    const streams = {}
    const disposable = new Rx.CompositeDisposable()
    objKeys(sinks).map(name => {
      const sink$ = this.toReplay[name] ? sinks[name].replay(null, 1) : sinks[name].publish()
      disposable.add(sink$.connect())
      streams[name] = sink$
    })
    this.cache[key] = {
      key,
      streams,
      disposable,
      idx
    }
  },
  update(key, idx) {
    this.cache[key].idx = idx
  },
  del(key) {
    const cached = this.cache[key]
    if (cached) {
      delete this.cache[key]
      cached.disposable.dispose()
    }
  },
  keys() {
    return objKeys(this.cache)
  },
  list() {
    const list = objKeys(this.cache).map(k => this.cache[k])
    list.sort((a, b) => a.idx - b.idx)
    return list.map(x => x.streams)
  },
  dispose() {
    this.toReplay = []
    objKeys(this.cache).forEach(key => {
      this.del(key)
    })
  }
})
<|endoftext|>


<commit_before>export default function({ types: t }) {
    return {
        visitor: {
            Program(path, state) {
                path.addComment('leading', ' eslint-disable ');
                path.unshiftContainer("directives", t.noop());
            }
        }
    }
};<commit_msg>fix comment node generation to correctly work with other directives plugins<commit_after>export default function({ types: t }) {
    return {
        visitor: {
            Program: {
                exit(path, state) {
                    path.addComment('leading', ' eslint-disable ');
                    path.unshiftContainer("directives", t.noop());
                }
            }
        }
    }
};<|endoftext|>


<commit_before>import h from 'snabbdom/h';
import VNode from 'snabbdom/vnode';

export default function snabbdomVirtualize(element) {
    // If our node is a text node, then we only want to set the `text` part of
    // the VNode.
    if (element.nodeType === Node.TEXT_NODE) {
        return VNode(undefined, undefined, undefined, element.textContent);
    }

    // If not a text node, then build up a VNode based on the element's tag
    // name, class and style attributes, and remaining attributes.

    // Special values: style, class. We don't include these in the attrs hash
    // of the VNode.
    const data = {};
    const classes = getClasses(element);
    if (Object.keys(classes).length !== 0) {
        data.class = classes;
    }
    const style = getStyle(element);
    if (Object.keys(style).length !== 0) {
        data.style = style;
    }

    // Build up set of attributes on the element.
    const attributes = element.attributes;
    for (let i = 0; i < attributes.length; i++) {
        const attr = attributes.item(i);
        const name = attr.name;
        if (name !== 'style' && name !== 'class') {
            if (!data.attrs) {
                data.attrs = {};
            }
            data.attrs[name] = attr.value;
        }
    }

    // Build up set of children.
    const childNodes = [];
    const children = element.childNodes;
    for (var i = 0; i < children.length; i++) {
        childNodes.push(snabbdomVirtualize(children.item(i)));
    }
    return h(element.tagName.toLowerCase(), data, childNodes);
}

// Builds the class object for the VNode.
function getClasses(element) {
    const className = element.className;
    const classes = {};
    if (className !== null && className.length > 0) {
        className.split(' ').forEach((className) => {
            classes[className] = true;
        });
    }
    return classes;
}

// Builds the style object for the VNode.
function getStyle(element) {
    const style = element.style;
    const styles = {};
    for (let i = 0; i < style.length; i++) {
        const name = style.item(i);
        styles[name] = style.getPropertyValue(name);
    }
    return styles;
}

<commit_msg>Only include child array if there are children to add.<commit_after>import h from 'snabbdom/h';
import VNode from 'snabbdom/vnode';

export default function snabbdomVirtualize(element) {
    // If our node is a text node, then we only want to set the `text` part of
    // the VNode.
    if (element.nodeType === Node.TEXT_NODE) {
        return VNode(undefined, undefined, undefined, element.textContent);
    }

    // If not a text node, then build up a VNode based on the element's tag
    // name, class and style attributes, and remaining attributes.

    // Special values: style, class. We don't include these in the attrs hash
    // of the VNode.
    const data = {};
    const classes = getClasses(element);
    if (Object.keys(classes).length !== 0) {
        data.class = classes;
    }
    const style = getStyle(element);
    if (Object.keys(style).length !== 0) {
        data.style = style;
    }

    // Build up set of attributes on the element.
    const attributes = element.attributes;
    for (let i = 0; i < attributes.length; i++) {
        const attr = attributes.item(i);
        const name = attr.name;
        if (name !== 'style' && name !== 'class') {
            if (!data.attrs) {
                data.attrs = {};
            }
            data.attrs[name] = attr.value;
        }
    }

    // Build up set of children.
    let childNodes = null;
    const children = element.childNodes;
    if (children.length > 0) {
        childNodes = [];
        for (var i = 0; i < children.length; i++) {
            childNodes.push(snabbdomVirtualize(children.item(i)));
        }
    }
    return h(element.tagName.toLowerCase(), data, childNodes);
}

// Builds the class object for the VNode.
function getClasses(element) {
    const className = element.className;
    const classes = {};
    if (className !== null && className.length > 0) {
        className.split(' ').forEach((className) => {
            classes[className] = true;
        });
    }
    return classes;
}

// Builds the style object for the VNode.
function getStyle(element) {
    const style = element.style;
    const styles = {};
    for (let i = 0; i < style.length; i++) {
        const name = style.item(i);
        styles[name] = style.getPropertyValue(name);
    }
    return styles;
}

<|endoftext|>