Example #1
0
export const createLeaf = ({
  initialState,
  pauser,
  createNode,
  hookMap,
  provisionalNode
}) => {
  if (
    provisionalNode &&
    Object.keys(provisionalNode.getChildrenSubject.getValue()).length
  ) {
    throw new Error('Tried to create leaf node when provisional has children');
  }
  if (!pauser) {
    pauser = new Rx.BehaviorSubject(true);
  }
  if (!hookMap) {
    hookMap = new WeakMap();
  }
  const subject = new Rx.BehaviorSubject(initialState);
  const observable = subject
    .distinctUntilChanged();
  const setNextState = (newState) => {
    subject.onNext(newState);
  }

  return createNode({
    observable,
    hookMap,
    pauser,
    setNextState,
    provisional: false,
    provisionalNode
  });
};
Example #2
0
test('localStorageDriver', t => {

  const possibleValues = new Map([
    ['an empty Observable', Rx.Observable.just()],
    ['an observable with empty object', Rx.Observable.just({})],
    ['an observable object with multiple keys/values', Rx.Observable.just(createData())],
    ['an empty observable array', Rx.Observable.just([])],
    ['an observable array of object', Rx.Observable.just(createArray())],
  ]);

  possibleValues.forEach((value, key) => t.ok(
    localStorageDriver(value), `should accept ${key} as input`
  ));

  const impossibleValues = new Map([
    ['an observable string', Rx.Observable.just('hello') ],
    ['an observable number', Rx.Observable.just(1)],
    ['an observable interval', Rx.Observable.interval(1000)],
    ['an observable array of arrays', Rx.Observable.just([ ['key1', 'value1'], ['key2', 'value2'] ]) ],
  ]);

  impossibleValues.forEach((value, key) => t.throws(localStorageDriver(value), Error, `should NOT accept ${key} as input`));

  t.ok(localStorageDriver(Rx.Observable.just(createData())) instanceof Rx.Observable, 'should return a Rx.Observable');

  localStorageDriver(Rx.Observable.just(createData()))
    .get('key1')
    .subscribe(value => {
      t.equal(value, 'value1', 'giving .get() a single key should return a single value');
    });

  localStorageDriver(Rx.Observable.just(createData()))
    .get(['key1', 'key2', 'key3', 'key4'])
    .subscribe(value => {
      t.ok(Array.isArray(value), `giving .get() an array of keys should return an array of values`);
    });

  let data$ = new Rx.BehaviorSubject(createData());
  let storage$ = localStorageDriver(data$);
  storage$
    .get('key5')
    .take(0)
    .defaultIfEmpty('Sorry')
    .subscribe(value => {
      t.equal(value, 'Sorry', 'should be able to use defualtIfEmpty()')
    })

  storage$
    .get('key5')
    .defaultIfEmpty('Sorry')
    .take(2)
    .subscribe(value => {
      t.equal(value, 'value5', 'should receive updates to values set')
    });

  data$.onNext({"key4": "newValue4"});
  data$.onNext({"key5": "value5"});

  setTimeout(() => t.end(), 1000);
});
Example #3
0
 t.test('behavior', function (t) {
   var stateStream = new Rx.BehaviorSubject({ foo: 'bar'});
   var getStateSteamSpy = sinon.spy(function () {
     return stateStream;
   });
   var Component = React.createClass({
     displayName: 'Component',
     mixins: [StateStreamMixin],
     getStateStream: getStateSteamSpy,
     render: function () {
       return null;
     }
   });
   
   var component = testUtils.render(React.createElement(Component));
   
   t.ok(getStateSteamSpy.called, 'it should have called getStateStreamSpy');
   
   t.deepEquals(component.state, { foo: 'bar'}, 'state should have been merged with the value of stateStream');
   
   stateStream.onNext({hello: 'world'});
   
   t.deepEquals(component.state, { foo: 'bar', hello: 'world'}, 'state should have been merged with the new value of stateStream');
   
   testUtils.unmount();
   
   t.notOk(stateStream.hasObservers(), 'the subscrition to stateStream should have been cleaned after that the component has been unmounted ');
   
   t.end();
 });
Example #4
0
function getDiagramPromise(stream, scheduler) {
  var diagram = [];
  var subject = new Rx.BehaviorSubject([]);
  stream
    .observeOn(scheduler)
    .timestamp(scheduler)
    .map(function(x) {
      if (typeof x.value !== "object") {
        x.value = {content: x.value, id: Utils.calculateNotificationContentHash(x.value)};
      }
      return {
        // converts timestamp to % of MAX_VT_TIME
        time: (x.timestamp / MAX_VT_TIME) * 100,
        content: x.value.content,
        id: x.value.id
      };
    })
    .reduce(function(acc, x) {
      acc.push(x);
      return acc;
    },[])
    .subscribe(function onNext(x) {
      diagram = x;
      subject.onNext(diagram);
    }, function onError(e) {
      console.warn("Error in the diagram promise stream: "+e);
    }, function onComplete() {
      diagram.end = scheduler.now();
    });
  return subject.asObservable();
}
Example #5
0
function main(sources) {
    const storedData = storage.get('svg-data');
    const selection$ = new BehaviorSubject(storedData ? storedData.selection : initialSelection);
    const svgs$ = new BehaviorSubject(storedData ? storedData.svgs : initialSVGs);
    const names$ = svgs$.map(svgs => svgs.map(({ name }) => name));
    const state$ = Observable.combineLatest(svgs$, selection$, (svgs, selection) => ({ svgs, selection }));
    const validSelection$ = state$.map(({ svgs, selection }) => (0 <= selection && selection < svgs.length));
    const currentSVG$ = state$.withLatestFrom(validSelection$, ({ svgs, selection }, validSelection) => validSelection ? svgs[selection] : null);
    const currentName$ = currentSVG$.map(svg => svg ? svg.name: '');
    const currentData$ = currentSVG$.map(svg => svg ? svg.data: '');

    state$.subscribe(state => {
        storage.set('svg-data', state);
    });

    const DOM = sources.DOM;
    const tabs = Tabs({ DOM, names$, selection$ });

    const addTabButton = Button({ DOM, props$: Observable.of({ label: '+' }) });
    addTabButton.click$.subscribe(AddTab({ tabs$: svgs$, selection$, name$: Observable.of('svg') }));

    const removeTabButton = Button({ DOM, props$: Observable.of({ label: '-' }) });
    removeTabButton.click$.subscribe(RemoveTab({ tabs$: svgs$, selection$ }));

    const renameTabButton = Button({ DOM, props$: Observable.of({ label: '_' }) });
    const showRenameTabDialog$ = renameTabButton.click$.map(event => true);
    const renameTabDialog = InputDialog({ DOM, valueOnShow$: currentName$, visible$: showRenameTabDialog$ });
    renameTabDialog.confirm$.subscribe(RenameTab({ tabs$: svgs$, selection$ }));

    const svgInput = Input({ DOM, data$: currentData$, enabled$: validSelection$, type$: Observable.of('textarea') });
    const svgRenderer = SVGRenderer({ value$: svgInput.value$ });

    const newSVGs$ = svgInput.input$.withLatestFrom(svgs$, selection$, (value, svgs, selection) => {
        const newSVGs = svgs.slice();
        newSVGs[selection] = { name: newSVGs[selection].name, data: value };
        return newSVGs;
    });
    newSVGs$.subscribe(svgs$);

    const vleftControls$ = Observable.combineLatest(tabs.DOM, addTabButton.DOM, (vtabs, vaddTabButton) => div('.controls.left-controls', [vtabs, vaddTabButton]));
    const vrightControls$ = Observable.combineLatest(renameTabButton.DOM, removeTabButton.DOM, (vrenameTabButton, vremoveTabButton) => div('.controls.right-controls', [vrenameTabButton, vremoveTabButton]));
    const vcontrols$ = Observable.combineLatest(vleftControls$, vrightControls$, (vleftControls, vrightControls) => div('.controls', [vleftControls, vrightControls]));
    const veditor$ = Observable.combineLatest(svgInput.DOM, svgRenderer.DOM, (vinput, vrenderer) => div('.svg-editor', [vinput, vrenderer]));
    const vapp$ = Observable.combineLatest(vcontrols$, veditor$, renameTabDialog.DOM, (vcontrols, veditor, vrenameTabDialog) => div('.app', [vcontrols, veditor, vrenameTabDialog]));

    return {
        DOM: vapp$
    };
}
Example #6
0
 const setInitialState = (initialState) => {
   const currentNode = nodeSubject.getValue()
   if (!currentNode.provisional) {
     throwFinalizedError();
   } else {
     createTree({
       initialState,
       createNode,
       pauser,
       hookMap,
       provisional: false,
       provisionalNode: node
     });
     return publishNode(nodeSubject.getValue());
   }
 };
Example #7
0
export default ({image$}) =>{
  const cropped$ = new BehaviorSubject(null)
  return {
    cropped$,
    DOM: image$ // has to be attached on 'update', the default, breaks if 'insert'
      .map( src=>reactComponent(ReactCropper, {src,onCrop:e=>cropped$.onNext(e),aspectRatio:1},'update') )
  }
}
Example #8
0
function deactivate(): void {
  gadgetsApi = null;
  allHomeFragmentsStream.onNext(Immutable.Set());
  if (disposables) {
    disposables.dispose();
    disposables = null;
  }
}
Example #9
0
 login: function (username,login,callback) {
   var user = users[username];
   if(user) {
     currentUser.onNext(user); 
     callback(null);
   } else {
     callback('Incorrect username/password');
   }
  }, 
Example #10
0
 _publishMessages(): void {
   const messages: Array<BusySignalMessageBusy> = [];
   for (const idMap of this._currentMessages.values()) {
     for (const message of idMap.values()) {
       messages.push(message);
     }
   }
   this._messageStream.onNext(messages);
 }
Example #11
0
  target.prototype.componentWillMount = function componentWillMount() {
    this.__subscription = stateSubject
      .map(state => (args.length ? pick(state, args) : state))
      .subscribe(state => this.setState(state),
        this.onError,
        this.onComplete);

    willMount.call(this);
  };
Example #12
0
const createRouterObservable = (history, prefix='') => {
  const historySubject = new Rx.BehaviorSubject(history.getCurrentLocation());
  const routeSubject = new Rx.ReplaySubject();
  let subRouters = [];

  history.listen((location) => historySubject.onNext(location));

  const route = (pattern, opts, cb) => {
    const finalOpts = typeof opts === 'function' ? { stream: opts } : opts;
    const joinedPattern = joinPattern(prefix, pattern);
    const subRouter = typeof cb === 'function' && cb (
      createRouterObservable(history, joinedPattern)
    );
    if (subRouter) {
      const subRouterObservable = subRouter.asObservable();
      subRouters = [subRouter, ...subRouters];
    }

    const routeObservable = createRouteObservable(
      joinedPattern,
      finalOpts,
      history,
      historySubject.asObservable(),
      subRouter
    );

    routeSubject.onNext(routeObservable);

    return api;
  };

  const allRoutesObservable = routeSubject
    .flatMap(obs => obs)
    .replay();

  const asObservable = () => allRoutesObservable;

  const start = () => {
    const allDisposable = new Rx.CompositeDisposable();
    allDisposable.add(allRoutesObservable.connect());
    subRouters.forEach(router => {
      allDisposable.add(router.start());
    })
    return allDisposable;
  };

  const api = { route, asObservable, start };

  return api;
}
  constructor(LocalStorage) {
    var key = 'todomvc-0.0.5';
    // a BehaviorSubject has an initial value and monitors for updates
    // in this case it is an array (empty or from local storage)
    // this Observable operates as the "onNext" handler for corresponding
    // actions
    this.updates = new Rx.BehaviorSubject(LocalStorage.save(key))

    // the "notes" parameter is the current array of notes and
    // the return value is the updated array of notes to be stored
    // the operation is an ACTION that returns a modified array
    // of notes. 
    this.todoList = this.updates.scan((todoList, operation) => {
      console.log("OPERATION")
      return operation(todoList);
    })

    // each updated note array is stored in local storage
    this.todoList.subscribe((update) => {
      console.log('SAVE', update)
      LocalStorage.save(key, update)
    })
  }
Example #14
0
  const route = (pattern, opts, cb) => {
    const finalOpts = typeof opts === 'function' ? { stream: opts } : opts;
    const joinedPattern = joinPattern(prefix, pattern);
    const subRouter = typeof cb === 'function' && cb (
      createRouterObservable(history, joinedPattern)
    );
    if (subRouter) {
      const subRouterObservable = subRouter.asObservable();
      subRouters = [subRouter, ...subRouters];
    }

    const routeObservable = createRouteObservable(
      joinedPattern,
      finalOpts,
      history,
      historySubject.asObservable(),
      subRouter
    );

    routeSubject.onNext(routeObservable);

    return api;
  };
Example #15
0
 .map( src=>reactComponent(ReactCropper, {src,onCrop:e=>cropped$.onNext(e),aspectRatio:1},'update') )
Example #16
0
export const createInitialNode = ({
  addChildrenSubject,
  getChildrenSubject,
  pauser,
  observable,
  hookMap,
  setNextState,
  setCompleted,
  provisional
}) => {
  const observableSubject = new Rx.ReplaySubject(1);
  // Great name right?
  const combinedObservable = observableSubject
    .flatMapLatest(obs => obs);
  const externalObservable = combinedObservable
    .replay();
  const child = createChildAccessor(addChildrenSubject, getChildrenSubject);
  const nodeSubject = new Rx.BehaviorSubject();
  const asObservable = () => externalObservable;
  const reduce = createReduce(
    combinedObservable,
    nodeSubject.asObservable(),
    hookMap,
    pauser
  );
  const connectDisposable = new Rx.CompositeDisposable();
  const connect = () => {
    const children = getChildrenSubject && getChildrenSubject.getValue();
    if (children) {
      const keys = Object.keys(children);
      keys.forEach(key => connectDisposable.add(
        children[key].getValue().connect()
      ));
    }
    connectDisposable.add(
      asObservable().connect()
    );
    return connectDisposable;
  };

  const setInitialState = (initialState) => {
    const currentNode = nodeSubject.getValue()
    if (!currentNode.provisional) {
      throwFinalizedError();
    } else {
      createTree({
        initialState,
        createNode,
        pauser,
        hookMap,
        provisional: false,
        provisionalNode: node
      });
      return publishNode(nodeSubject.getValue());
    }
  };
  const setNodeCompleted = () => {
    setCompleted();
    observableSubject.onCompleted();
    // Dispose on next tick so onComplete handlers
    // will be invoked.
    setTimeout(() => connectDisposable.dispose());
  };

  if (observable) {
    observableSubject.onNext(observable);
  }

  const nodeProps = {
    addChildrenSubject,
    getChildrenSubject,
    reduce,
    child,
    setNextState,
    setCompleted: setNodeCompleted,
    nodeSubject,
    provisional: !!provisional,
    provisionalNode: !!provisional && node,
    pauser,
    combinedObservable,
    observableSubject,
    asObservable,
    connect,
    setInitialState
  };

  const accessor = createNodeAccessor(nodeProps);

  const compose = nodeProps.compose = wrapInPublish((nodeMap) => createComposedNode({
    nodeMap,
    accessor,
    pauser,
    hookMap
  }));

  const node = Object.assign(
    wrapInPublish(accessor),
    nodeProps
  );

  nodeSubject.onNext(node);

  return node;
};
Example #17
0
  // Send `action->state` changes to the monitor
  RemoteDev.send(action.type, action.state);
}

// Subscribe to the remote monitor to synchronize local state
// Use it only when you want time travelling
RemoteDev.subscribe(state => {
  subject.onNext(toImmutable(state));
});

// Instead of using a `dispatch` function, you can just subscribe an observer,
// but you'll get only new states without knowing which action changed them
// subject.subscribe(data => { RemoteDev.send('', action.state); });

let store = toImmutable(initialSatate);
let subject = new Rx.BehaviorSubject(store);

subject.map(store => store.get('todos'))
  .distinctUntilChanged()
  .subscribe(persist.set);

todoActions.subjects.add.subscribe((text) => {
  store = store.updateIn(['todos'], (todos) => {
    return todos.push(todoRecord()({
      text
    }));
  });

  dispatch({ type: 'add', state: store });
});
Example #18
0
'use strict';

var Rx = require('rx'),
    replicate = require('../util/replicate'),
    h = require('virtual-hyperscript'),
    modelTodos$ = new Rx.BehaviorSubject(null),
    newTodoKeypress$ = new Rx.Subject(),
    todoCompleteChange$ = new Rx.Subject(),
    todoDeleteClick$ = new Rx.Subject(),
    todoEditDblclick$ = new Rx.Subject(),
    todoModifyKeyup$ = new Rx.Subject(),
    todoModifyBlur$ = new Rx.Subject(),
    todoToggleAll$ = new Rx.Subject(),
    todoClearCompletedTodos$ = new Rx.Subject(),
    vtree$;

function observe(TodosModel) {
    replicate(TodosModel.todos$, modelTodos$);
}

function vrenderSectionHeader() {
    return h('header#header', {}, [
        h('h1', ['todos']),
        h('input', {
            id: 'new-todo',
            value: '',
            placeholder: 'What needs to be done?',
            autofocus: true,
            'ev-keypress': function(ev) {
                newTodoKeypress$.onNext(ev);
            }
 setResultsMessages(newMessages) {
   this.resultsMessageSubject.onNext(newMessages);
 }
Example #20
0
export const removeAllListeners = () => stateSubject.dispose();
Example #21
0
export const updateStore = newState => stateSubject.onNext({ ...stateSubject.value, ...newState });
Example #22
0
export const createStore = initialState => stateSubject.onNext({ ...initialState });
Example #23
0
 const setNextState = (newState) => {
   subject.onNext(newState);
 }
 .tapOnCompleted((x) => this.stats.onCompleted())
Example #25
0
'use strict';
/*
 * ItemsView.
 * As output, Observable of vtree (Virtual DOM tree).
 * As input, ItemsModel.
 */
var Rx = require('rx');
var h = require('virtual-hyperscript');
var replicate = require('mvi-example/utils/replicate');

var modelItems$ = new Rx.BehaviorSubject(null);
var itemWidthChanged$ = new Rx.Subject();
var itemColorChanged$ = new Rx.Subject();
var removeClicks$ = new Rx.Subject();
var addOneClicks$ = new Rx.Subject();
var addManyClicks$ = new Rx.Subject();

function observe(ItemsModel) {
  replicate(ItemsModel.items$, modelItems$);
}

function vrenderTopButtons() {
  return h('div.topButtons', {}, [
    h('button',
      {'ev-click': function (ev) { addOneClicks$.onNext(ev); }},
      'Add New Item'
    ),
    h('button',
      {'ev-click': function (ev) { addManyClicks$.onNext(ev); }},
      'Add Many Items'
    )
Example #26
0
 history.listen((location) => historySubject.onNext(location));
this.toStat = _.curry((event, message) => this.stats.onNext({event, message}))
Example #28
0
/*
 * OperatorsMenuModel
 */
var Rx = require('rx');
var Examples = require('rxmarbles/data/examples');
var replicate = require('rxmarbles/utils').replicate;

var inputSelection$ = new Rx.BehaviorSubject();

function observe(interpreter) {
  replicate(interpreter.select$, inputSelection$);
};

var examples$ = Rx.Observable.just(Examples);

var defaultExampleKey = window.location.hash.replace("#", "") || "merge";

var selectedExample$ = inputSelection$
  .startWith(defaultExampleKey)
  .filter(function(key) { return (typeof key !== "undefined"); })
  .map(function(key) {
    var example = Examples[key];
    example.key = key;
    return example;
  });

module.exports = {
  observe: observe,
  examples$: examples$,
  selectedExample$: selectedExample$
};
var Rx = require('rx')

// the differance between Subject and AsyncSubject and BehaviourSubject
// var subject = new Rx.Subject();
// var subject = new Rx.AsyncSubject();
var subject = new Rx.BehaviorSubject('starting');

var source = Rx.Observable
  .interval(100)
  .take(20)

source.subscribe(subject);

var subscription = subject.subscribe(
  function onNext(val){
    console.log('Value :',val)
  },
  function onError(err){
    console.log('onError:', err)
  },
  function onCompleted() {
    console.log('onCompleted')
  }
)

Example #30
0
// //////////
function GLView ({drivers, props$}) {
  const {DOM, postMessage} = drivers

  let config = presets

  let initialized$ = new Rx.BehaviorSubject(false)
  let update$ = Rx.Observable.interval(16, 66666666667)

  let settings$ = props$.pluck('settings')
  // every time either activeTool or selection changes, reset/update transform controls
  let activeTool$ = settings$.pluck('activeTool').startWith(undefined)

  let renderer = null

  let composer = null
  let composers = []
  let fxaaPass = null
  let outScene = null
  let maskScene = null

  let scene = new THREE.Scene()
  let dynamicInjector = new THREE.Object3D() // all dynamic mapped objects reside here
  scene.dynamicInjector = dynamicInjector
  scene.add(dynamicInjector)

  let selectionsContainer = new THREE.Scene() // unfortunate way to handle things in three.js

  let camera = makeCamera(config.cameras[0])
  let controls = makeControls(config.controls[0]) // create 'orbit' controls
  let transformControls = new TransformControls(camera)

  let grid = new LabeledGrid(200, 200, 10, config.cameras[0].up)
  let shadowPlane = new ShadowPlane(2000, 2000, null, config.cameras[0].up)

  const actions = intent({DOM}, {camera, scene, transformControls})
  const state$ = model(props$, actions)

  // FIXME: proxies for now, not sure how to deal with them
  const meshAddedToScene$ = new Rx.ReplaySubject(1)
  const meshRemovedFromScene$ = new Rx.ReplaySubject(1)

  const outlineSelections$ = settings$
    .filter(s => !(s.toolSets.indexOf('view') !== -1 && s.toolSets.length === 1)) // in all but view only mode
    .combineLatest(state$, function (settings, state) {
      return state
    })

  const zoomToFit$ = settings$
    .filter(s => s.toolSets.indexOf('edit') === -1 && s.toolSets.length === 1) // only in view only mode
    .combineLatest(state$.pluck('items'), function (settings, items) {
      return items
    })
    .filter(i => i.length > 0)
    .distinctUntilChanged()

  /*
    const zoomToFit$ = meshAddedToScene$ //alternative implementation
    .combineLatest(settings$.filter(s=> s.appMode === 'viewer'), function(mesh, settings){
      return [dynamicInjector]
    })
    .distinctUntilChanged()*/

  // react to actions
  actions.zoomInOnPoint$
    .forEach((oAndP) => zoomInOn(oAndP.object, camera, {position: oAndP.point}))
  zoomToFit$
    .forEach((meshes) => zoomToFit(meshes[meshes.length - 1], camera, new THREE.Vector3()))

  let windowResizes$ = windowResizes(1) // get from intents/interactions ?

  function clearScene () {
    if (scene) {
      if (scene.dynamicInjector) {
        scene.remove(scene.dynamicInjector)
      }
      let dynamicInjector = new THREE.Object3D()
      scene.dynamicInjector = dynamicInjector

      scene.add(dynamicInjector)
    }
  }

  function addToScene (object) {
    scene.dynamicInjector.add(object)
  }
  function removeFromScene (object) {
    scene.dynamicInjector.remove(object)
  }

  function setupScene () {
    config.scenes['main']
      // TODO , update to be more generic
      .map(light => makeLight(light))
      .forEach(light => scene.add(light))
  }

  function render (scene, camera) {
    composers.forEach(c => c.render())
  // composer.passes[composer.passes.length-1].uniforms[ 'tDiffuse2' ].value = composers[0].renderTarget2
  // composer.passes[composer.passes.length-1].uniforms[ 'tDiffuse3' ].value = composers[1].renderTarget2
  }

  function update () {
    controls.update()
    transformControls.update()
    TWEEN.update()
  // if(camViewControls) camViewControls.update()
  }

  function configure (container) {
    // log.debug('initializing into container', container)

    if (!Detector.webgl) {
      // TODO: handle lacking webgl
    } else {
      renderer = new THREE.WebGLRenderer({
        antialias: false,
        preserveDrawingBuffer: true
      })
    }

    renderer.setClearColor('#fff')
    Object.keys(config.renderer).map(function (key) {
      // TODO add hasOwnProp check
      renderer[key] = config.renderer[key]
    })

    let pixelRatio = window.devicePixelRatio || 1
    renderer.setPixelRatio(pixelRatio)

    container.appendChild(renderer.domElement)
    // prevents zooming the 3d view from scrolling the window
    preventScroll(container)

    transformControls.setDomElement(container)

    // more init
    controls.setObservables(actions.filteredInteractions$)
    controls.addObject(camera)

    scene.add(camera)
    scene.add(shadowPlane)
    scene.add(transformControls)

    let ppData = setupPostProcess(camera, renderer, scene)
    // composer = ppData.composer
    composers = ppData.composers
    fxaaPass = ppData.fxaaPass
    outScene = ppData.outScene
    maskScene = ppData.maskScene

    initialized$.onNext(true)
  }

  // side effect ?
  function handleResize (sizeInfos) {
    // log.debug('setting glView size',sizeInfos)
    let {width, height, aspect} = sizeInfos

    if (width > 0 && height > 0 && camera && renderer) {
      renderer.setSize(width, height)
      camera.aspect = aspect
      camera.setSize(width, height)
      camera.updateProjectionMatrix()

      let pixelRatio = window.devicePixelRatio || 1
      fxaaPass.uniforms[ 'resolution' ].value.set(1 / (width * pixelRatio), 1 / (height * pixelRatio))

      composers.forEach(c => {
        c.reset()
        c.setSize(width * pixelRatio, height * pixelRatio)
      })
    }
  }

  // combine All needed components to apply any 'transforms' to their visuals
  let items$ = state$.pluck('items') // .distinctUntilChanged()
  // TODO : we DO  want distinctUntilChanged() to prevent spamming here at any state change
  // TODO we want to zoomToFit only when mode is viewer && we just recieved the FIRST model ??
  /* settings$
    .filter(s=> s.mode === 'viewer')
    .forEach(e=>console.log('settings',e))*/

  // do diffing to find what was added/changed
  let itemChanges$ = items$.scan(function (acc, x) {
    let cur = x
    let prev = acc.cur
    return {cur, prev}
  }, {prev: undefined, cur: undefined})
    .map(function (typeData) {
      let {cur, prev} = typeData
      let changes = extractChanges(prev, cur)
      // console.log('changes', changes)
      return changes
    })

  // experimenting with selections effects
  outlineSelections$
    .pluck('selectedMeshes')
    .distinctUntilChanged()
    .filter(exists)
    .forEach(function (selectedMeshes) {
      if (outScene) {
        const sceneItems = selectedMeshes.filter(exists)
        outScene.children = sceneItems
        maskScene.children = sceneItems
      }
    })

  // transformControls handling
  // we modify the transformControls mode based on the active tool
  // every time either activeTool or selection changes, reset/update transform controls
  let selectedMeshesChanges$ = state$.pluck('selectedMeshes').distinctUntilChanged()
    .scan(function (acc, x) {
      let cur = x
      let prev = acc.cur
      return {cur, prev}
    }, {prev: [], cur: []})
    .map(function (typeData) {
      let {cur, prev} = typeData
      let changes = extractChanges(prev, cur)
      return changes
    })
    .distinctUntilChanged()
    .shareReplay(1)

  combineLatestObj({
    selections: selectedMeshesChanges$,
    tool: activeTool$.distinctUntilChanged()
  })
    .forEach(function ({selections, tool}) {
      // console.log('updating transformControls',selections,tool)
      // remove transformControls from removed meshes
      selections.removed.map(mesh => transformControls.detach(mesh))

      selections.added.map(function (mesh) {
        if (tool && mesh && ['translate', 'rotate', 'scale'].indexOf(tool) > -1) {
          transformControls.attach(mesh)
          transformControls.setMode(tool)
        }
        else if ((!tool && mesh) || ['translate', 'rotate', 'scale'].indexOf(tool) === -1)
        { // tool is undefined, but we still had selections
          transformControls.detach(mesh)
        }
      })
    })

  // hande all the cases where events require re-rendering
  let reRender$ = merge(
    initialized$
      .filter(i => i === true)
      .do(i => handleResize({
        width: window.innerWidth,
        height: window.innerHeight,
        aspect: window.innerWidth / window.innerHeight
      }))

    , fromEvent(controls, 'change')
    , fromEvent(transformControls, 'change')
    , state$.pluck('selectedMeshes')

    , windowResizes$.do(handleResize) // we need the resize to take place before we render
  )
    .shareReplay(1)

  // /////////
  setupScene()

  update$.forEach(update)
  reRender$.forEach(render)

  // settings handling
  settings$ = settings$
    .filter(exists)
    .distinctUntilChanged()

  settings$.map(s => s.camera.autoRotate)
    .forEach(autoRotate => controls.autoRotate = autoRotate)

  settings$.map(s => s.grid.show)
    .forEach(function (showGrid) {
      scene.remove(grid)
      if (showGrid) {
        scene.add(grid)
      }
    })

  // react based on diffs
  itemChanges$
    .do(function (changes) {
      // console.log('reacting to changes', changes)
      changes.added.map(function (mesh) {
        addToScene(mesh)
        meshAddedToScene$.onNext(mesh)
      })
      changes.removed.map(function (mesh) {
        removeFromScene(mesh)
        meshRemovedFromScene$.onNext(mesh)
      })
    })
    .do(e => render())
    .forEach(e => e)

  // we do not want to change our container (DOM) but the contents (gl)
  const gLWidgeHelper = new GLWidgeHelper(configure)
  // new GLWidgeHelper(configure)}
  // gLWidgeHelper.setup()

  const vtree$ = Rx.Observable.just(
    <div className='glView'>
      {gLWidgeHelper}
    </div>
  )

  // screencapture test
  /* postMessage
    .filter(e=>e.hasOwnProperty('captureScreen'))
    .flatMap(e=>{
      let img = domElementToImage(renderer.domElement)

      let resolutions = e.captureScreen

      let images$ = resolutions.map(function(resolution){
          let [width,height] = resolution
          console.log('resolution',resolution)

          let obs = new Rx.Subject()
          aspectResize(img, width, height, e=>{
            obs.onNext(e)
            obs.onCompleted()})
          return obs
        })

      let results$ = Rx.Observable.forkJoin(images$)

      return results$
    })
    .forEach(e=>{
      e.map(img=>console.log(img))
    })*/

  return {
    DOM: vtree$,
    events: {
      // initialized:initialized$,
      shortSingleTaps$: actions.shortSingleTapsWPicking$,
      shortDoubleTaps$: actions.shortDoubleTapsWPicking$,
      longTaps$: actions.longTapsWPicking$,
      selectionsTransforms$: actions.selectionsTransforms$,
      selectedMeshes$: actions.selectMeshes$
    }
  }
}