<commit_before>import { buffered } from '../common/generators';
import { Partition } from '../common/unionFind';

import * as DS from './delaney';

const typeMap = ds => {
  const result = {};

  for (const D of ds.elements())
    result[D] = [];

  for (let i = 0; i < ds.dim; ++i) {
    for (const D of ds.orbitReps2(i, i+1)) {
      const m = ds.m(i, i+1, D);
      for (const E of ds.orbit2(i, i+1, D))
        result[E].push(m);
    }
  }

  for (const D of ds.elements())
    result[D] = result[D].join(',');

  return result;
};

const typePartitionFolder = ds => {
  const types = typeMap(ds);

  return (p0, D0, E0) => {
    const p = p0.clone();
    let q = [[D0, E0]];

    while (q.length) {
      const [D, E] = q.shift();

      if (types[D] != types[E])
        return;
      else if (p.find(D) == p.find(E))
        continue;
      else {
        p.union(D, E);
        for (const i of ds.indices())
          q.push([ds.s(i, D), ds.s(i, E)]);
      }
    }

    return p;
  };
};

export const isMinimal = ds => {
  const fold = typePartitionFolder(ds);
  const p = new Partition();
  const D0 = ds.elements()[0];

  return ds.elements().slice(1).every(D => fold(p, D0, D) === undefined);
};

export const typePartition = ds => {
  const fold = typePartitionFolder(ds);
  const p = new Partition();
  const D0 = ds.elements()[0];

  return ds.elements().slice(1).reduce((p, D) => fold(p, D0, D) || p, p);
};

export const traversal = function*(ds, indices, seeds) {
  const seedsLeft = Array.from(seeds);
  const seen = {};
  const todo = {};

  for (const i of indices)
    todo[i] = [];

  while (true) {
    const i = indices.find(k => todo[k].length);
    const D = (i == null) ? seedsLeft.pop() : todo[i].shift();

    if (D == null)
      return;

    if (!seen[[D, i]]) {
      const Di = (i == null) ? D : ds.s(i, D);

      for (const k of indices) {
        if (k < 2)
          todo[k].unshift(Di);
        else
          todo[k].push(Di);
      }

      seen[[Di, null]] = seen[[D, i]] = seen[[Di, i]] = true;

      yield [D, i, Di];
    }
  }
};

export const partialOrientation = ds => {
  const ori = new Array(ds.size + 1);

  for (const [Di, i, D] of traversal(ds, ds.indices(), ds.elements())) {
    if (D && !ori[D])
      ori[D] = i == null ? 1 : -ori[Di];
  }

  return ori;
};

const forAllEdges = (ds, test) =>
  ds.elements().every(D => ds.indices().every(i => test(D, i)));

export const isLoopless = ds => forAllEdges(ds, (D, i) => D != ds.s(i, D));

export const isOriented = ds => {
  const ori = partialOrientation(ds);
  return forAllEdges(ds, (D, i) => ori[D] != ori[ds.s(i, D)]);
};

export const isWeaklyOriented = ds => {
  const ori = partialOrientation(ds);
  const test = (D, Di) => D == Di || ori[D] != ori[Di];
  return forAllEdges(ds, (D, i) => test(D, ds.s(i, D)));
};

export const orbitReps = (ds, indices, seeds) => {
  const result = [];
  for (const [_, i, D] of traversal(ds, indices, seeds || ds.elements())) {
    if (i == null)
      result.push(D);
  }
  return result;
};

export const orbits = (ds, indices, seeds) => {
  const seen = {};
  const result = [];

  for (const [_, i, D] of traversal(ds, indices, seeds || ds.elements())) {
    if (i == null)
      result.push([]);

    if (D && !seen[D]) {
      seen[D] = true;
      result[result.length - 1].push(D);
    }
  }

  return result;
};

export const isConnected = ds => orbitReps(ds, ds.indices()).length < 2;
export const orbit = (ds, indices, seed) => orbits(ds, indices, [seed])[0];

function* protocol(ds, gen) {
  const emap = {};
  let n = 1;

  for (const [Di, i, D] of gen) {
    if (emap[D] == null)
      emap[D] = n;

    yield i == null ? -1 : i;
    yield emap[Di];

    if (i != null)
      yield emap[D];

    if (emap[D] == n) {
      ++n;

      for (let i = 0; i < ds.dim; ++i)
        yield ds.v(i, i+1, D);
    }
  }
};

export const invariant = ds => {
  let best = null;

  for (const D0 of ds.elements()) {
    const trav = buffered(protocol(ds, traversal(ds, ds.indices(), [D0])));

    if (best == null)
      best = trav;
    else {
      for (let i = 0; ; ++i) {
        const next = trav.get(i);
        if (next == null)
          break;

        const d = next - best.get(i);
        if (d < 0)
          best = trav;
        else if (d > 0)
          break;
      }
    }
  }

  return best.result().generated;
};

export const morphism = (
  src, img, startSrc, startImg, typesSrc=typeMap(src), typesImg=typeMap(img)
) => {
  if (!isConnected(src))
    throw new Error('source symbol must be connected');

  if (src.dim != img.dim)
    throw new Error('dimensions must be equal');

  const q = [[startSrc, startImg]];
  const m = new Array(src.size + 1);
  m[startSrc] = startImg;

  while (q.length) {
    const [D, E] = q.shift();

    for (let i = 0; i <= src.dim; ++i) {
      const Di = src.s(i, D);
      const Ei = img.s(i, E);

      if (Di != null || Ei != null) {
        if (m[Di] == null && typesSrc[Di] == typesImg[Ei]) {
          q.push([Di, Ei]);
          m[Di] = Ei;
        }
        else if (m[Di] != Ei)
          return null;
      }
    }
  }

  return m;
};

export const automorphisms = ds => {
  const types = typeMap(ds);
  const result = [];

  for (let D = 1; D <= ds.size; ++D) {
    const phi = morphism(ds, ds, 1, D, types, types);
    if (phi != null)
      result.push(phi);
  }

  return result;
};

if (require.main == module) {
  Array.prototype.toString = function() {
    return '[ ' + this.map(x => x && x.toString()).join(', ') + ' ]';
  };

  const test = ds => {
    const is = fn => fn(ds) ? 'is' : 'is not';

    console.log(`ds = ${ds}`);
    console.log();

    console.log(`    symbol ${is(isConnected)} connected.`);
    console.log(`    symbol ${is(isMinimal)} minimal.`);
    console.log(`    symbol ${is(isLoopless)} loopless.`);
    console.log(`    symbol ${is(isOriented)} oriented.`);
    console.log(`    symbol ${is(isWeaklyOriented)} weakly oriented.`);
    console.log(`    type partition: ${typePartition(ds)}`);

    const trav = [...traversal(ds, ds.indices(), ds.elements())];
    console.log(`    traversal: ${trav}`);
    console.log(`    invariant: ${invariant(ds)}`);
    console.log();

    console.log(`    0,1 orbit reps: ${orbitReps(ds, [0, 1])}`);
    console.log(`    1,2 orbit reps: ${orbitReps(ds, [1, 2])}`);
    console.log(`    0,2 orbit reps: ${orbitReps(ds, [0, 2])}`);
    console.log();

    console.log(`    0,1 orbits: ${orbits(ds, [0, 1])}`);
    console.log(`    1,2 orbits: ${orbits(ds, [1, 2])}`);
    console.log(`    0,2 orbits: ${orbits(ds, [0, 2])}`);
    console.log();

    console.log(`    0,1 orbit of 1: ${orbit(ds, [0, 1], 1)}`);
    console.log(`    1,2 orbit of 1: ${orbit(ds, [1, 2], 1)}`);
    console.log(`    0,2 orbit of 1: ${orbit(ds, [0, 2], 1)}`);
    console.log();

    const ori = partialOrientation(ds);
    console.log(`    partial orientation: ${ori}`);
    console.log();

    console.log(`    automorphisms: ${automorphisms(ds)}`);
    console.log();
    console.log();
  };

  test(DS.parse(`<1.1:24:
                2 4 6 8 10 12 14 16 18 20 22 24,
                16 3 5 7 9 11 13 15 24 19 21 23,
                10 9 20 19 14 13 22 21 24 23 18 17:
                8 4,3 3 3 3>`));

  test(DS.parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>'));
  test(DS.parse('<1.1:2 3:2,1 2,1 2,2:6,3 2,6>'));
  test(DS.parse('<1.1:6:4 6 5,5 4 6,4 6 5:3,6>'));
}
<commit_msg>properties: revise morphism() loop; add null test in typePartitionFolder()<commit_after>import { buffered } from '../common/generators';
import { Partition } from '../common/unionFind';

import * as DS from './delaney';

const typeMap = ds => {
  const result = {};

  for (const D of ds.elements())
    result[D] = [];

  for (let i = 0; i < ds.dim; ++i) {
    for (const D of ds.orbitReps2(i, i+1)) {
      const m = ds.m(i, i+1, D);
      for (const E of ds.orbit2(i, i+1, D))
        result[E].push(m);
    }
  }

  for (const D of ds.elements())
    result[D] = result[D].join(',');

  return result;
};

const typePartitionFolder = ds => {
  const types = typeMap(ds);

  return (p0, D0, E0) => {
    const p = p0.clone();
    let q = [[D0, E0]];

    while (q.length) {
      const [D, E] = q.shift();

      if (types[D] != types[E])
        return;
      else if (p.find(D) == p.find(E))
        continue;
      else if (D != null) {
        p.union(D, E);
        for (const i of ds.indices())
          q.push([ds.s(i, D), ds.s(i, E)]);
      }
    }

    return p;
  };
};

export const isMinimal = ds => {
  const fold = typePartitionFolder(ds);
  const p = new Partition();
  const D0 = ds.elements()[0];

  return ds.elements().slice(1).every(D => fold(p, D0, D) === undefined);
};

export const typePartition = ds => {
  const fold = typePartitionFolder(ds);
  const p = new Partition();
  const D0 = ds.elements()[0];

  return ds.elements().slice(1).reduce((p, D) => fold(p, D0, D) || p, p);
};

export const traversal = function*(ds, indices, seeds) {
  const seedsLeft = Array.from(seeds);
  const seen = {};
  const todo = {};

  for (const i of indices)
    todo[i] = [];

  while (true) {
    const i = indices.find(k => todo[k].length);
    const D = (i == null) ? seedsLeft.pop() : todo[i].shift();

    if (D == null)
      return;

    if (!seen[[D, i]]) {
      const Di = (i == null) ? D : ds.s(i, D);

      for (const k of indices) {
        if (k < 2)
          todo[k].unshift(Di);
        else
          todo[k].push(Di);
      }

      seen[[Di, null]] = seen[[D, i]] = seen[[Di, i]] = true;

      yield [D, i, Di];
    }
  }
};

export const partialOrientation = ds => {
  const ori = new Array(ds.size + 1);

  for (const [Di, i, D] of traversal(ds, ds.indices(), ds.elements())) {
    if (D && !ori[D])
      ori[D] = i == null ? 1 : -ori[Di];
  }

  return ori;
};

const forAllEdges = (ds, test) =>
  ds.elements().every(D => ds.indices().every(i => test(D, i)));

export const isLoopless = ds => forAllEdges(ds, (D, i) => D != ds.s(i, D));

export const isOriented = ds => {
  const ori = partialOrientation(ds);
  return forAllEdges(ds, (D, i) => ori[D] != ori[ds.s(i, D)]);
};

export const isWeaklyOriented = ds => {
  const ori = partialOrientation(ds);
  const test = (D, Di) => D == Di || ori[D] != ori[Di];
  return forAllEdges(ds, (D, i) => test(D, ds.s(i, D)));
};

export const orbitReps = (ds, indices, seeds) => {
  const result = [];
  for (const [_, i, D] of traversal(ds, indices, seeds || ds.elements())) {
    if (i == null)
      result.push(D);
  }
  return result;
};

export const orbits = (ds, indices, seeds) => {
  const seen = {};
  const result = [];

  for (const [_, i, D] of traversal(ds, indices, seeds || ds.elements())) {
    if (i == null)
      result.push([]);

    if (D && !seen[D]) {
      seen[D] = true;
      result[result.length - 1].push(D);
    }
  }

  return result;
};

export const isConnected = ds => orbitReps(ds, ds.indices()).length < 2;
export const orbit = (ds, indices, seed) => orbits(ds, indices, [seed])[0];

function* protocol(ds, gen) {
  const emap = {};
  let n = 1;

  for (const [Di, i, D] of gen) {
    if (emap[D] == null)
      emap[D] = n;

    yield i == null ? -1 : i;
    yield emap[Di];

    if (i != null)
      yield emap[D];

    if (emap[D] == n) {
      ++n;

      for (let i = 0; i < ds.dim; ++i)
        yield ds.v(i, i+1, D);
    }
  }
};

export const invariant = ds => {
  let best = null;

  for (const D0 of ds.elements()) {
    const trav = buffered(protocol(ds, traversal(ds, ds.indices(), [D0])));

    if (best == null)
      best = trav;
    else {
      for (let i = 0; ; ++i) {
        const next = trav.get(i);
        if (next == null)
          break;

        const d = next - best.get(i);
        if (d < 0)
          best = trav;
        else if (d > 0)
          break;
      }
    }
  }

  return best.result().generated;
};

export const morphism = (
  src, img, startSrc, startImg, typesSrc=typeMap(src), typesImg=typeMap(img)
) => {
  if (!isConnected(src))
    throw new Error('source symbol must be connected');

  if (src.dim != img.dim)
    throw new Error('dimensions must be equal');

  const q = [[startSrc, startImg]];
  const m = new Array(src.size + 1);

  while (q.length) {
    const [D, E] = q.shift();

    if (m[D] == E)
      continue;
    else if (m[D] != null || typesSrc[D] != typesImg[E])
      return null;
    else if (D != null) {
      m[D] = E;

      for (let i = 0; i <= src.dim; ++i)
        q.push([src.s(i, D), img.s(i, E)]);
    }
  }

  return m;
};

export const automorphisms = ds => {
  const types = typeMap(ds);
  const result = [];

  for (let D = 1; D <= ds.size; ++D) {
    const phi = morphism(ds, ds, 1, D, types, types);
    if (phi != null)
      result.push(phi);
  }

  return result;
};

if (require.main == module) {
  Array.prototype.toString = function() {
    return '[ ' + this.map(x => x && x.toString()).join(', ') + ' ]';
  };

  const test = ds => {
    const is = fn => fn(ds) ? 'is' : 'is not';

    console.log(`ds = ${ds}`);
    console.log();

    console.log(`    symbol ${is(isConnected)} connected.`);
    console.log(`    symbol ${is(isMinimal)} minimal.`);
    console.log(`    symbol ${is(isLoopless)} loopless.`);
    console.log(`    symbol ${is(isOriented)} oriented.`);
    console.log(`    symbol ${is(isWeaklyOriented)} weakly oriented.`);
    console.log(`    type partition: ${typePartition(ds)}`);

    const trav = [...traversal(ds, ds.indices(), ds.elements())];
    console.log(`    traversal: ${trav}`);
    console.log(`    invariant: ${invariant(ds)}`);
    console.log();

    console.log(`    0,1 orbit reps: ${orbitReps(ds, [0, 1])}`);
    console.log(`    1,2 orbit reps: ${orbitReps(ds, [1, 2])}`);
    console.log(`    0,2 orbit reps: ${orbitReps(ds, [0, 2])}`);
    console.log();

    console.log(`    0,1 orbits: ${orbits(ds, [0, 1])}`);
    console.log(`    1,2 orbits: ${orbits(ds, [1, 2])}`);
    console.log(`    0,2 orbits: ${orbits(ds, [0, 2])}`);
    console.log();

    console.log(`    0,1 orbit of 1: ${orbit(ds, [0, 1], 1)}`);
    console.log(`    1,2 orbit of 1: ${orbit(ds, [1, 2], 1)}`);
    console.log(`    0,2 orbit of 1: ${orbit(ds, [0, 2], 1)}`);
    console.log();

    const ori = partialOrientation(ds);
    console.log(`    partial orientation: ${ori}`);
    console.log();

    console.log(`    automorphisms: ${automorphisms(ds)}`);
    console.log();
    console.log();
  };

  test(DS.parse(`<1.1:24:
                2 4 6 8 10 12 14 16 18 20 22 24,
                16 3 5 7 9 11 13 15 24 19 21 23,
                10 9 20 19 14 13 22 21 24 23 18 17:
                8 4,3 3 3 3>`));

  test(DS.parse('<1.1:3:1 2 3,1 3,2 3:4 8,3>'));
  test(DS.parse('<1.1:2 3:2,1 2,1 2,2:6,3 2,6>'));
  test(DS.parse('<1.1:6:4 6 5,5 4 6,4 6 5:3,6>'));
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the MIT license
 * If a copy of the MIT license was not distributed with this file, you can
 * obtain one at http://www.mozillapopcorn.org/butter-license.txt */

define( [ "util/lang", "util/keys", "editor/editor", "util/uri", "text!layouts/media-editor.html" ],
  function( LangUtils, KeysUtils, Editor, Uri, EDITOR_LAYOUT ) {

  var MAX_MEDIA_INPUTS = 4,
      _parentElement =  LangUtils.domFragment( EDITOR_LAYOUT,".media-editor" ),
      _loadingSpinner = _parentElement.querySelector( ".media-loading-spinner" ),
      _primaryMediaWrapper = LangUtils.domFragment( EDITOR_LAYOUT, ".primary-media-wrapper" ),
      _altMediaWrapper = LangUtils.domFragment( EDITOR_LAYOUT, ".alt-media-wrapper" ),
      _containerElement = _parentElement.querySelector( ".butter-editor-body" ),
      _currentMediaWrapper = _containerElement.querySelector( ".current-media-wrapper" ),
      _addAlternateSourceBtn = _containerElement.querySelector( ".add-alternate-media-source-btn" ),
      _mediaErrorMessage = _containerElement.querySelector( ".media-error-message" ),
      _media,
      _inputCount = 0,
      _emptyInputs = 0,
      _this;

  function updateButterMedia() {
    var urlInputs = _currentMediaWrapper.querySelectorAll( "textarea" ),
        newMediaArr = [],
        url;

    for ( var i = 0, l = urlInputs.length; i < l; i++ ) {
      url = urlInputs[ i ].value;
      url = url.trim();

      // Deal with https://soundcloud URLs, which should actually be http://soundcloud (#2631)
      url = url.replace( /^https\:\/\/soundcloud\.com/, "http://soundcloud.com" );

      // Don't bother with empty strings
      if ( url ) {
        newMediaArr.push( url );
      }
    }
    if ( newMediaArr.length ) {
      showError( false );
      setLoadSpinner( true );
      _media.url = newMediaArr;
    }
  }

  function createInput( url, isPrimaryInput ) {
    var wrapper = isPrimaryInput ? _primaryMediaWrapper.cloneNode( true ) : _altMediaWrapper.cloneNode( true ),
        urlInput = wrapper.querySelector( ".current-media-input" ),
        applyBtn = wrapper.querySelector( ".butter-media-apply" ),
        altMediaLabel,
        deleteBtn,
        oldValue = "";

      urlInput.value = url;

      function checkInputMax() {
        if ( _inputCount >= MAX_MEDIA_INPUTS ) {
          _addAlternateSourceBtn.classList.add( "butter-disabled" );
        } else {
          _addAlternateSourceBtn.classList.remove( "butter-disabled" );
        }
      }

      function updateMediaOnChange() {
        if ( oldValue !== urlInput.value ) {
            updateButterMedia();
        }
      }

      function removeMediaWrapper() {
        if ( _inputCount > 1 && !isPrimaryInput ) {
          _currentMediaWrapper.removeChild( wrapper );
          _inputCount--;
          checkInputMax();
        }
      }

      function onInput() {
       if ( oldValue !== urlInput.value ) {
          applyBtn.classList.remove( "butter-disabled" );
        } else {
          applyBtn.classList.add( "butter-disabled" );
        }
      }

      function onEnter( e ) {
        if (  e.keyCode === KeysUtils.ENTER ) {
          e.preventDefault();
          updateMediaOnChange();
        }
      }

      if ( isPrimaryInput ) {
        altMediaLabel = wrapper.querySelector( ".alternate-media-label" );
        altMediaLabel.addEventListener( "click", function() {
          _parentElement.classList.toggle( "alternates-hidden" );
        }, false );
      }

      if ( !isPrimaryInput ) {
        deleteBtn = wrapper.querySelector( ".delete-media-btn" );
        deleteBtn.addEventListener( "click", function() {
          removeMediaWrapper( wrapper );
          updateButterMedia();
        }, false );
      }

      urlInput.addEventListener( "focus", function() {
        oldValue = urlInput.value;
      });

      applyBtn.addEventListener( "click", updateMediaOnChange, false );
      urlInput.addEventListener( "keydown", onEnter, false );
      urlInput.addEventListener( "input",  onInput, false );

      _this.wrapTextInputElement( urlInput );
      _currentMediaWrapper.appendChild( wrapper );
      _inputCount++;
      checkInputMax();
  }

  function setLoadSpinner( on ) {
    if ( on ) {
      _loadingSpinner.classList.remove( "hidden" );
    } else {
      _loadingSpinner.classList.add( "hidden" );
    }
  }

  function clearCurrentMediaList() {
    var input;

    while ( _currentMediaWrapper.firstChild ) {
      input = _currentMediaWrapper.querySelector( "textarea" );

      // count empty ones, so they can be added again
      if ( !input.value ) {
        _emptyInputs++;
      }

      _currentMediaWrapper.removeChild( _currentMediaWrapper.firstChild );
    }
    _inputCount = 0;
  }

  function setup() {
    var url = _media.url;

    clearCurrentMediaList();

    if ( !Array.isArray( url ) ) {
      url = [ url ];
    }

    for ( var i = 0, l = url.length; i < l; i++ ) {
      createInput( url[ i ], i === 0 );
    }
    while ( _emptyInputs ) {
      createInput( "" );
      _emptyInputs--;
    }
  }

  function showError( state ) {
    var inputs = _currentMediaWrapper.querySelectorAll( "textarea" );

    for ( var i = 0, l = inputs.length; i < l; i++ ) {
      if ( state ) {
        inputs[ i ].classList.add( "error" );
      } else {
        inputs[ i ].classList.remove( "error" );
      }
    }
    if ( state ) {
      _mediaErrorMessage.classList.remove( "hidden" );
    } else {
      _mediaErrorMessage.classList.add( "hidden" );
    }
  }

  function addAlternateSourceBtnHandler() {
    if ( _inputCount < MAX_MEDIA_INPUTS ) {
      createInput( "" );
    }
  }

  _addAlternateSourceBtn.addEventListener( "click", addAlternateSourceBtnHandler, false );

  function onMediaFailed() {
    showError( true );
    setLoadSpinner( false );
  }

  function onMediaReady() {
    showError( false );
    setLoadSpinner( false );
  }

  Editor.register( "media-editor", null, function( rootElement, butter ) {
    rootElement = _parentElement;
    _this = this;

    function onMediaContentChanged() {
      _media = butter.currentMedia;
      setup();
    }

    Editor.BaseEditor.extend( _this, butter, rootElement, {
      open: function() {
        _media = butter.currentMedia;

        _media.listen( "mediaready", onMediaReady );
        _media.listen( "mediacontentchanged", onMediaContentChanged );
        _media.listen( "mediafailed", onMediaFailed );

        setup();
        document.querySelector( ".butter-editor-header-media" ).classList.add( "butter-active" );
      },
      close: function() {
        _media.unlisten( "mediaready", onMediaReady );
        _media.unlisten( "mediacontentchanged", onMediaContentChanged );
        _media.unlisten( "mediafailed", onMediaFailed );
        document.querySelector( ".butter-editor-header-media" ).classList.remove( "butter-active" );
      }
    });
  });
});
<commit_msg>[#2588] Prevent loading spinner from endlessly spinning<commit_after>/* This Source Code Form is subject to the terms of the MIT license
 * If a copy of the MIT license was not distributed with this file, you can
 * obtain one at http://www.mozillapopcorn.org/butter-license.txt */

define( [ "util/lang", "util/keys", "editor/editor", "util/uri", "text!layouts/media-editor.html" ],
  function( LangUtils, KeysUtils, Editor, Uri, EDITOR_LAYOUT ) {

  var MAX_MEDIA_INPUTS = 4,
      _parentElement =  LangUtils.domFragment( EDITOR_LAYOUT,".media-editor" ),
      _loadingSpinner = _parentElement.querySelector( ".media-loading-spinner" ),
      _primaryMediaWrapper = LangUtils.domFragment( EDITOR_LAYOUT, ".primary-media-wrapper" ),
      _altMediaWrapper = LangUtils.domFragment( EDITOR_LAYOUT, ".alt-media-wrapper" ),
      _containerElement = _parentElement.querySelector( ".butter-editor-body" ),
      _currentMediaWrapper = _containerElement.querySelector( ".current-media-wrapper" ),
      _addAlternateSourceBtn = _containerElement.querySelector( ".add-alternate-media-source-btn" ),
      _mediaErrorMessage = _containerElement.querySelector( ".media-error-message" ),
      _media,
      _inputCount = 0,
      _emptyInputs = 0,
      _this;

  function updateButterMedia() {
    var urlInputs = _currentMediaWrapper.querySelectorAll( "textarea" ),
        newMediaArr = [],
        url;

    for ( var i = 0, l = urlInputs.length; i < l; i++ ) {
      url = urlInputs[ i ].value;
      url = url.trim();

      // Deal with https://soundcloud URLs, which should actually be http://soundcloud (#2631)
      url = url.replace( /^https\:\/\/soundcloud\.com/, "http://soundcloud.com" );

      // Don't bother with empty strings
      if ( url ) {
        newMediaArr.push( url );
      }
    }
    if ( newMediaArr.length ) {
      showError( false );
      setLoadSpinner( true );
      _media.url = newMediaArr;
    }
  }

  function createInput( url, isPrimaryInput ) {
    var wrapper = isPrimaryInput ? _primaryMediaWrapper.cloneNode( true ) : _altMediaWrapper.cloneNode( true ),
        urlInput = wrapper.querySelector( ".current-media-input" ),
        applyBtn = wrapper.querySelector( ".butter-media-apply" ),
        altMediaLabel,
        deleteBtn,
        oldValue = "";

      urlInput.value = url;

      function checkInputMax() {
        if ( _inputCount >= MAX_MEDIA_INPUTS ) {
          _addAlternateSourceBtn.classList.add( "butter-disabled" );
        } else {
          _addAlternateSourceBtn.classList.remove( "butter-disabled" );
        }
      }

      function updateMediaOnChange() {
        if ( oldValue !== urlInput.value ) {
            updateButterMedia();
        }
      }

      function removeMediaWrapper() {
        if ( _inputCount > 1 && !isPrimaryInput ) {
          _currentMediaWrapper.removeChild( wrapper );
          _inputCount--;
          checkInputMax();
        }
      }

      function onInput() {
       if ( oldValue !== urlInput.value ) {
          applyBtn.classList.remove( "butter-disabled" );
        } else {
          applyBtn.classList.add( "butter-disabled" );
        }
      }

      function onEnter( e ) {
        if (  e.keyCode === KeysUtils.ENTER ) {
          e.preventDefault();
          updateMediaOnChange();
        }
      }

      if ( isPrimaryInput ) {
        altMediaLabel = wrapper.querySelector( ".alternate-media-label" );
        altMediaLabel.addEventListener( "click", function() {
          _parentElement.classList.toggle( "alternates-hidden" );
        }, false );
      }

      if ( !isPrimaryInput ) {
        deleteBtn = wrapper.querySelector( ".delete-media-btn" );
        deleteBtn.addEventListener( "click", function() {
          removeMediaWrapper( wrapper );
          updateButterMedia();
        }, false );
      }

      urlInput.addEventListener( "focus", function() {
        oldValue = urlInput.value;
      });

      applyBtn.addEventListener( "click", updateMediaOnChange, false );
      urlInput.addEventListener( "keydown", onEnter, false );
      urlInput.addEventListener( "input",  onInput, false );

      _this.wrapTextInputElement( urlInput );
      _currentMediaWrapper.appendChild( wrapper );
      _inputCount++;
      checkInputMax();
  }

  function setLoadSpinner( on ) {
    if ( on ) {
      _loadingSpinner.classList.remove( "hidden" );
    } else {
      _loadingSpinner.classList.add( "hidden" );
    }
  }

  function clearCurrentMediaList() {
    var input;

    while ( _currentMediaWrapper.firstChild ) {
      input = _currentMediaWrapper.querySelector( "textarea" );

      // count empty ones, so they can be added again
      if ( !input.value ) {
        _emptyInputs++;
      }

      _currentMediaWrapper.removeChild( _currentMediaWrapper.firstChild );
    }
    _inputCount = 0;
  }

  function setup() {
    var url = _media.url;

    clearCurrentMediaList();

    if ( !Array.isArray( url ) ) {
      url = [ url ];
    }

    for ( var i = 0, l = url.length; i < l; i++ ) {
      createInput( url[ i ], i === 0 );
    }
    while ( _emptyInputs ) {
      createInput( "" );
      _emptyInputs--;
    }
  }

  function showError( state ) {
    var inputs = _currentMediaWrapper.querySelectorAll( "textarea" );

    for ( var i = 0, l = inputs.length; i < l; i++ ) {
      if ( state ) {
        inputs[ i ].classList.add( "error" );
      } else {
        inputs[ i ].classList.remove( "error" );
      }
    }
    if ( state ) {
      _mediaErrorMessage.classList.remove( "hidden" );
    } else {
      _mediaErrorMessage.classList.add( "hidden" );
    }
  }

  function addAlternateSourceBtnHandler() {
    if ( _inputCount < MAX_MEDIA_INPUTS ) {
      createInput( "" );
    }
  }

  _addAlternateSourceBtn.addEventListener( "click", addAlternateSourceBtnHandler, false );

  function onMediaFailed() {
    showError( true );
    setLoadSpinner( false );
  }

  function onMediaReady() {
    showError( false );
    setLoadSpinner( false );
  }

  Editor.register( "media-editor", null, function( rootElement, butter ) {
    rootElement = _parentElement;
    _this = this;

    function onMediaContentChanged() {
      _media = butter.currentMedia;
      setup();
    }

    Editor.BaseEditor.extend( _this, butter, rootElement, {
      open: function() {
        _media = butter.currentMedia;

        _media.listen( "mediaready", onMediaReady );
        _media.listen( "mediacontentchanged", onMediaContentChanged );
        _media.listen( "mediafailed", onMediaFailed );

        // Ensure the loading spinner is off when the media is ready. Otherwise, keep it spinning.
        if ( _media.ready ) {
          setLoadSpinner( false );
        } else {
          setLoadSpinner( true );
        }

        setup();
        document.querySelector( ".butter-editor-header-media" ).classList.add( "butter-active" );
      },
      close: function() {
        _media.unlisten( "mediaready", onMediaReady );
        _media.unlisten( "mediacontentchanged", onMediaContentChanged );
        _media.unlisten( "mediafailed", onMediaFailed );
        document.querySelector( ".butter-editor-header-media" ).classList.remove( "butter-active" );
      }
    });
  });
});
<|endoftext|>


<commit_before>(function(resourceModule) {
  resourceModule
    .factory('resourcesConf', function(configuration){
      return configuration.resources
    })
    .factory('Sample', function($resource, resourcesConf) {
      return $resource(resourcesConf.basePath + 'sample/_design/charts/_view/:viewName', {
        group: true,
        descending: false
      }, {
        time: makeView('time'),
        frequency: makeView('frequency'),
        stats: makeStatsView('stats'),
        dates: makeStatsView('dates'),
        statistics: makeStatsView('statistics', {
          group_level: 1
        }),
      })
    })
    .factory('Anemometer', function($resource, resourcesConf) {
      return $resource(resourcesConf.basePath + 'anemometer/:id', {}, {
        query: {
          isArray: true,
          method: 'GET',
          params: {
            id: '_all_docs',
            include_docs: true
          },
          transformResponse: function(data) {
            return getRows(data).map(function(item) {
              return item.doc
            })
          }
        }
      })
    })
    .factory('upload', function($http, $q, resourcesConf) {
      var numberOfDocs = resourcesConf.numberOfDocs
      return function(anemometer, iterator) {
        var numberOfUploads = Math.ceil(iterator.size() / numberOfDocs),
          promises = [],
          deferred = $q.defer()

          for (var i = 0; i < numberOfUploads; i++) {
            (function(batch) {
              var slice = iterator.slice(i * numberOfDocs, (i + 1) * numberOfDocs),
                promise = $http.post(resourcesConf.basePath + 'sample/_bulk_docs', {
                  docs: slice.map(function(item) {
                    if (item.length == 4 && item.reduce(function(acc, value) {
                      return acc && value != null && value != "" && angular.isNumber(parseFloat(value))
                    }, true)) {
                      return {
                        _id: anemometer._id + '_' + item[0],
                        a: anemometer._id,
                        t: item[0],
                        d: item[1],
                        p: item[2],
                        b: item[3],
                        s: item[2] / item[1] * 1.1176
                      }
                    }
                  }).filter(angular.identity)
                }).success(function() {
                  deferred.notify(batch)
                })

                promises.push(promise)
            }(i))

          };

        $q.all(promises).then(function(res) {
          deferred.resolve(res)
        })

        return {
          promise: deferred.promise,
          numberOfUploads: numberOfUploads
        }
      }
    })
    .factory('params', function($http, resourcesConf) {
      return $http({
        method: 'get',
        url: resourcesConf.basePath + 'sample/_design/charts',
        transformResponse: function(data) {
          var exports = {}
          eval(JSON.parse(data).views.lib.params)
          return exports
        }
      }).then(function(response) {
        return response.data
      })
    })

  function getRows(data) {
    return JSON.parse(data).rows
  }

  function makeView(viewName, isArray, transformResponse, params) {
    return {
      method: 'GET',
      transformResponse: transformResponse || getRows,
      isArray: isArray == null ? true : isArray,
      params: angular.extend({
        viewName: viewName,
        stale: 'update_after'
      }, params)
    }
  }

  function makeStatsView(viewName, params) {
    return makeView(viewName, false, function(data) {
      return (getRows(data)[0] || {}).value
    }, params || {})
  }
})(angular.module('Ventolone.resources', [
  'ngResource',
  'Ventolone.configuration'
]))
<commit_msg>resourceConf as closure scoped variable<commit_after>(function(resourceModule) {
  var resourcesConf

  resourceModule
    .config(function(configuration){
      resourcesConf = configuration.resources
    })
    .factory('Sample', function($resource) {
      return $resource(resourcesConf.basePath + 'sample/_design/charts/_view/:viewName', {
        group: true,
        descending: false
      }, {
        time: makeView('time'),
        frequency: makeView('frequency'),
        stats: makeStatsView('stats'),
        dates: makeStatsView('dates'),
        statistics: makeStatsView('statistics', {
          group_level: 1
        }),
      })
    })
    .factory('Anemometer', function($resource) {
      return $resource(resourcesConf.basePath + 'anemometer/:id', {}, {
        query: {
          isArray: true,
          method: 'GET',
          params: {
            id: '_all_docs',
            include_docs: true
          },
          transformResponse: function(data) {
            return getRows(data).map(function(item) {
              return item.doc
            })
          }
        }
      })
    })
    .factory('upload', function($http, $q) {
      var numberOfDocs = resourcesConf.numberOfDocs
      return function(anemometer, iterator) {
        var numberOfUploads = Math.ceil(iterator.size() / numberOfDocs),
          promises = [],
          deferred = $q.defer()

          for (var i = 0; i < numberOfUploads; i++) {
            (function(batch) {
              var slice = iterator.slice(i * numberOfDocs, (i + 1) * numberOfDocs),
                promise = $http.post(resourcesConf.basePath + 'sample/_bulk_docs', {
                  docs: slice.map(function(item) {
                    if (item.length == 4 && item.reduce(function(acc, value) {
                      return acc && value != null && value != "" && angular.isNumber(parseFloat(value))
                    }, true)) {
                      return {
                        _id: anemometer._id + '_' + item[0],
                        a: anemometer._id,
                        t: item[0],
                        d: item[1],
                        p: item[2],
                        b: item[3],
                        s: item[2] / item[1] * 1.1176
                      }
                    }
                  }).filter(angular.identity)
                }).success(function() {
                  deferred.notify(batch)
                })

                promises.push(promise)
            }(i))

          };

        $q.all(promises).then(function(res) {
          deferred.resolve(res)
        })

        return {
          promise: deferred.promise,
          numberOfUploads: numberOfUploads
        }
      }
    })
    .factory('params', function($http, resourcesConf) {
      return $http({
        method: 'get',
        url: resourcesConf.basePath + 'sample/_design/charts',
        transformResponse: function(data) {
          var exports = {}
          eval(JSON.parse(data).views.lib.params)
          return exports
        }
      }).then(function(response) {
        return response.data
      })
    })

  function getRows(data) {
    return JSON.parse(data).rows
  }

  function makeView(viewName, isArray, transformResponse, params) {
    return {
      method: 'GET',
      transformResponse: transformResponse || getRows,
      isArray: isArray == null ? true : isArray,
      params: angular.extend({
        viewName: viewName,
        stale: 'update_after'
      }, params)
    }
  }

  function makeStatsView(viewName, params) {
    return makeView(viewName, false, function(data) {
      return (getRows(data)[0] || {}).value
    }, params || {})
  }
})(angular.module('Ventolone.resources', [
  'ngResource',
  'Ventolone.configuration'
]))
<|endoftext|>


<commit_before>'use strict';
/**
 * Handles matching page URL with defined connectors and injecting scripts into content document
 */
define([
	'connectors',
	'config',
	'objects/injectResult',
	'customPatterns',
	'url-match'
], function(connectors, config, InjectResult, customPatterns, UrlMatch) {
	/**
	 * Ping the loaded page and checks if there is already loaded connector.
	 * If not injects it.
	 *
	 * @param {Number} tabId Tab ID
	 * @param {Object} connector Connector match objects
	 *
	 * @return {Promise} Promise that will be resolved with InjectResult value
	 */
	function pingAndInject(tabId, connector) {
		/*
		 * Ping the content page to see if the script is already in place.
		 *
		 * Sadly there is no way to silently check if the script has been
		 * already injected, so we will see an error in the background console
		 * on load of every supported page.
		 */
		return new Promise((resolve) => {
			chrome.tabs.sendMessage(tabId, { type: 'v2.onPing' }, (response) => {
				// if the message was sent to a non existing script or the script
				// does not implement the 'ping' message, we get response==undefined;
				if (response) {
					console.log('Subsequent ajax navigation, the scripts are already injected');
					resolve(new InjectResult(InjectResult.ALREADY_INJECTED, tabId, connector));
					return;
				}

				console.log('Loaded for the first time, injecting the scripts');

				// inject all scripts and jQuery, use slice to avoid mutating
				let scripts = connector.js.slice(0);

				scripts.unshift('core/content/connector.js');
				scripts.unshift('core/content/util.js');
				scripts.unshift('core/content/filter.js');
				scripts.unshift('core/content/reactor.js');
				// @ifdef DEBUG
				scripts.unshift('core/content/testReporter.js');
				// @endif
				scripts.unshift('vendor/jquery.min.js');

				// Needs to be the last script injected
				scripts.push('core/content/starter.js');

				// Waits for script to be fully injected before
				// injecting another one
				function injectWorker() {
					if (scripts.length > 0) {
						let jsFile = scripts.shift();
						let injectDetails = {
							file: jsFile,
							allFrames: connector.allFrames ? connector.allFrames : false
						};

						console.log(`Injecting ${jsFile}`);
						chrome.tabs.executeScript(tabId, injectDetails, injectWorker);
					} else {
						// done successfully
						resolve(new InjectResult(InjectResult.MATCHED_AND_INJECTED, tabId, connector));
					}
				}

				injectWorker();
			});
		});
	}

	/**
	 * Is triggered by chrome.tabs.onUpdated event
	 * Check for available connectors and injects matching connector into
	 * loaded page while returning info about the connector.
	 *
	 * @param {Object} tab Tab object
	 * @return {Promise} Promise that will be resolved with InjectResult value
	 */
	function onTabsUpdated(tab) {
		// Asynchronously preload all custom patterns and then start matching
		return customPatterns.getAllPatterns().then((customPatterns) => {
			for (let connector of connectors) {
				let matchOk = false;
				let patterns = connector.matches || [];

				if (customPatterns[connector.label]) {
					patterns = patterns.concat(customPatterns[connector.label]);
				}

				for (let pattern of patterns) {
					matchOk = matchOk || UrlMatch.test(tab.url, pattern);
				}

				if (matchOk) {
					return config.isConnectorEnabled(connector.label).then((isEnabled) => {
						if (!isEnabled) {
							return new InjectResult(InjectResult.MATCHED_BUT_DISABLED, tab.id, connector);
						}

						// Checks if there's already injected connector
						// and injects it if needed
						return pingAndInject(tab.id, connector);
					});

				}
			}

			return new InjectResult(InjectResult.NO_MATCH, tab.id, null);
		});
	}

	return { onTabsUpdated };
});
<commit_msg>Inject content scripts to disabled pages<commit_after>'use strict';
/**
 * Handles matching page URL with defined connectors and injecting scripts into content document
 */
define([
	'connectors',
	'config',
	'objects/injectResult',
	'customPatterns',
	'url-match'
], function(connectors, Config, InjectResult, customPatterns, UrlMatch) {
	/**
	 * Ping the loaded page and checks if there is already loaded connector.
	 * If not injects it.
	 *
	 * @param {Number} tabId Tab ID
	 * @param {Object} connector Connector match objects
	 *
	 * @return {Promise} Promise that will be resolved with InjectResult value
	 */
	function pingAndInject(tabId, connector) {
		/*
		 * Ping the content page to see if the script is already in place.
		 *
		 * Sadly there is no way to silently check if the script has been
		 * already injected, so we will see an error in the background console
		 * on load of every supported page.
		 */
		return new Promise((resolve) => {
			chrome.tabs.sendMessage(tabId, { type: 'v2.onPing' }, (response) => {
				if (response) {
					console.log('Subsequent ajax navigation, the scripts are already injected');
					resolve(new InjectResult(InjectResult.ALREADY_INJECTED, tabId, connector));
					return;
				}

				console.log('Loaded for the first time, injecting the scripts');

				// Inject all scripts and jQuery, use slice to avoid mutating
				let scripts = connector.js.slice(0);

				scripts.unshift('core/content/connector.js');
				scripts.unshift('core/content/util.js');
				scripts.unshift('core/content/filter.js');
				scripts.unshift('core/content/reactor.js');
				// @ifdef DEBUG
				scripts.unshift('core/content/testReporter.js');
				// @endif
				scripts.unshift('vendor/jquery.min.js');

				// Needs to be the last script injected
				scripts.push('core/content/starter.js');

				// Waits for script to be fully injected before
				// injecting another one
				function injectWorker() {
					if (scripts.length > 0) {
						let jsFile = scripts.shift();
						let injectDetails = {
							file: jsFile,
							allFrames: connector.allFrames ? connector.allFrames : false
						};

						console.log(`Injecting ${jsFile}`);
						chrome.tabs.executeScript(tabId, injectDetails, injectWorker);
					} else {
						Config.isConnectorEnabled(connector.label).then((isEnabled) => {
							if (!isEnabled) {
								resolve(new InjectResult(InjectResult.MATCHED_BUT_DISABLED, tabId, connector));
							} else {
								resolve(new InjectResult(InjectResult.MATCHED_AND_INJECTED, tabId, connector));
							}
						});
					}
				}

				injectWorker();
			});
		});
	}

	/**
	 * Is triggered by chrome.tabs.onUpdated event
	 * Check for available connectors and injects matching connector into
	 * loaded page while returning info about the connector.
	 *
	 * @param {Object} tab Tab object
	 * @return {Promise} Promise that will be resolved with InjectResult value
	 */
	function onTabsUpdated(tab) {
		// Asynchronously preload all custom patterns and then start matching
		return customPatterns.getAllPatterns().then((customPatterns) => {
			for (let connector of connectors) {
				let matchOk = false;
				let patterns = connector.matches || [];

				if (customPatterns[connector.label]) {
					patterns = patterns.concat(customPatterns[connector.label]);
				}

				for (let pattern of patterns) {
					matchOk = matchOk || UrlMatch.test(tab.url, pattern);
				}

				if (matchOk) {
					// Checks if there's already injected connector
					// and injects it if needed
					return pingAndInject(tab.id, connector);

				}
			}

			return new InjectResult(InjectResult.NO_MATCH, tab.id, null);
		});
	}

	return { onTabsUpdated };
});
<|endoftext|>


<commit_before>import getEventID from './getEventID';
import addRootDomEventListeners from './addRootDomEventListeners';
import EventRegistry from './EventRegistry';
import listenersStorage from './listenersStorage';
import listenerSetup from './hooks/listenerSetup';

function eventListener(e) {
    listenersStorage[getEventID(e.target)][e.type](e);
}

export default {

    /**
     * Set a event listeners on a node
     */

    addListener(domNode, type, listener) {

            const registry = EventRegistry[type];

            if (registry) {

                // is this activated, YET?
                if (!registry.activated) {

                    if (registry.setup) {
                        registry.setup();
                    } else if (registry.bubbles) {
                        let handler = listenerSetup(type, addRootDomEventListeners);
                        document.addEventListener(type, handler, false);
                    }

                    registry.activated = true;
                }

                const id = getEventID(domNode),
                    listeners = listenersStorage[id] || (listenersStorage[id] = {});

                if (!listeners[type]) {

                    if (registry.bubbles) {
                        ++registry.listenersCounter;
                    } else {
                        let handler = listenerSetup(type, eventListener);
                        domNode.addEventListener(type, handler, false);
                        domNode._infernoListeners = domNode._infernoListeners || {};
                        domNode._internoListeners[type] = handler;
                    }
                }

                listeners[type] = listener;
            }
        },
        /**
         * Remove event listeners from a node
         */
        removeListener(node, type) {

            const id = getEventID(node, true);

            if (id) {
                const listeners = listenersStorage[id];

                if (listeners && listeners[type]) {
                    listeners[type] = null;

                    const registry = EventRegistry[type];

                    if (registry) {
                        if (registry.bubbles) {
                            --registry.listenersCounter;
                        } else {
                            node.removeEventListener(type, node._infernoListeners[type]);
                            node._infernoListeners[type] = null;
                        }
                    }
                }
            }
        }
};<commit_msg>Fix typo<commit_after>import getEventID from './getEventID';
import addRootDomEventListeners from './addRootDomEventListeners';
import EventRegistry from './EventRegistry';
import listenersStorage from './listenersStorage';
import listenerSetup from './hooks/listenerSetup';

function eventListener(e) {
    listenersStorage[getEventID(e.target)][e.type](e);
}

export default {

    /**
     * Set a event listeners on a node
     */

    addListener(domNode, type, listener) {

            const registry = EventRegistry[type];

            if (registry) {

                // is this activated, YET?
                if (!registry.activated) {

                    if (registry.setup) {
                        registry.setup();
                    } else if (registry.bubbles) {
                        let handler = listenerSetup(type, addRootDomEventListeners);
                        document.addEventListener(type, handler, false);
                    }

                    registry.activated = true;
                }

                const id = getEventID(domNode),
                    listeners = listenersStorage[id] || (listenersStorage[id] = {});

                if (!listeners[type]) {

                    if (registry.bubbles) {
                        ++registry.listenersCounter;
                    } else {
                        let handler = listenerSetup(type, eventListener);
                        domNode.addEventListener(type, handler, false);
                        domNode._infernoListeners = domNode._infernoListeners || {};
                        domNode._infernoListeners[type] = handler;
                    }
                }

                listeners[type] = listener;
            }
        },
        /**
         * Remove event listeners from a node
         */
        removeListener(node, type) {

            const id = getEventID(node, true);

            if (id) {
                const listeners = listenersStorage[id];

                if (listeners && listeners[type]) {
                    listeners[type] = null;

                    const registry = EventRegistry[type];

                    if (registry) {
                        if (registry.bubbles) {
                            --registry.listenersCounter;
                        } else {
                            node.removeEventListener(type, node._infernoListeners[type]);
                            node._infernoListeners[type] = null;
                        }
                    }
                }
            }
        }
};<|endoftext|>


<commit_before>import {Content} from "native-base";
import React, {Component} from "react";
import {Dimensions, StyleSheet, TouchableOpacity, View} from "react-native";
import * as Progress from "react-native-progress";
import {Actions} from "react-native-router-flux";
import ImagePicker from "react-native-image-picker";

import Icon from "../components/iwapp-icons";
import * as colors from "../lib/colors";
import Text from "../components/text-lato";

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: "center",
        alignItems: "stretch",
        backgroundColor: colors.transparent,
        flexDirection: "row"
    },
    titleBarWrp: {
        justifyContent: "center",
        alignItems: "center",
        paddingVertical: 5,
        backgroundColor: colors.secondaryBlue
    },
    titleBar: {
        borderBottomWidth: 2,
        borderStyle: "solid",
        borderBottomColor: colors.buttonPrimary
    },
    title: {
        color: colors.white
    },
    contentUserWrp: {
        paddingHorizontal: 20,
        flexDirection: "row",
        justifyContent: "space-between",
        alignItems: "center",
        borderBottomColor: colors.lightGrey,
        borderBottomWidth: 1
    },
    iconsUserOptionsWrp: {
        flexDirection: "column",
        justifyContent: "center",
        alignItems: "center"
    },
    iconOptionsWrp: {
        flexDirection: "row",
        borderRadius: 20,
        width: 32,
        height: 32,
        alignItems: "center",
        justifyContent: "center",
        backgroundColor: colors.primaryBlue,
        marginTop: 6
    },
    userPhotoWrp: {
        flexDirection: "row",
        justifyContent: "flex-end",
        alignItems: "center"
    },
    photoWrp: {
        backgroundColor: colors.buttonPrimary,
        borderRadius: 100,
        width: 70,
        height: 70,
        alignItems: "center",
        justifyContent: "center"
    },
    iconEditPhotoWrp: {
        position: "absolute",
        bottom: 0,
        left: 55,
        borderRadius: 20,
        width: 22,
        height: 22,
        alignItems: "center",
        justifyContent: "center",
        backgroundColor: colors.secondaryBlue
    },
    textPhoto: {
        color: colors.white,
        fontSize: 40,
        fontWeight: "bold"
    },
    userInfoWrp: {
        paddingLeft: 18
    },
    textUser: {
        fontSize: 16,
        padding: 0
    },
    textEmail: {
        fontSize: 12,
        padding: 0
    },

    // STYLE FOR PROFILE PERCENTAGE COMPLETED
    contentAnswerWrp: {
        padding: 20
    },
    titleComplete: {
        marginBottom: 5
    },
    progressBarStyleWrp: {
        marginBottom: 20
    },
    progressBarStyle: {
        width: 200
    },

    // STYLE FOR BUTTON PERCENTAGE OF ANSWER
    progressQuestionnairesWrp: {
        flexDirection: "row",
        flexWrap: "wrap",
        justifyContent: "space-around"
    },
    progressQuestionnaire: {
        flexDirection: "column",
        alignItems: "stretch",
        marginTop: 20
    },
    iconQuestionnaireWrp: {
        position: "absolute",
        width: 80,
        height: 80,
        alignItems: "center",
        justifyContent: "center",
        backgroundColor: colors.transparent,
        top: 0,
        left: 0
    },
    titQuestionnaire: {
        fontSize: 14,
        padding: 0,
        textAlign: "center",
        marginTop: 3
    },
    percQuestionnaire: {
        fontSize: 11,
        padding: 0,
        textAlign: "center"
    }
});

class Profile extends Component {

    constructor (props) {
        super(props);
        this.state = {
            avatarSource: {}
        };
    }

    getQuestionnaires () {
        return [
            {color: colors.demographicsSection, name: "Demographics", key: "demographics", icon: "iw-demographics", value: 1, percentage: "100"},
            {color: colors.buildingsSection, name: "Buildings", key: "buildings", icon: "iw-buildings", value: 0.3, percentage: "30"},
            {color: colors.heatingSection, name: "Heating", key: "heating", icon: "iw-heating", value: 0.8, percentage: "80"},
            {color: colors.coolingSection, name: "Cooling", key: "cooling", icon: "iw-cooling", value: 1, percentage: "100"},
            {color: colors.statisticsSection, name: "Statistics", key: "statistics", icon: "iw-statistics", value: 0.5, percentage: "50"}
        ];
    }

    showImagePicker () {
        const options = {
            title: null, // specify null or empty string to remove the title
            cancelButtonTitle: "Cancella",
            takePhotoButtonTitle: "Dalla fotocamera", // specify null or empty string to remove this button
            chooseFromLibraryButtonTitle: "Dalla galleria", // specify null or empty string to remove this button
            cameraType: "front", // "front" or "back"
            mediaType: "photo", // "photo" or "video"
            maxWidth: 80, // photos only
            maxHeight: 80, // photos only
            aspectX: 1, // android only - aspectX:aspectY, the cropping image's ratio of width to height
            aspectY: 1, // android only - aspectX:aspectY, the cropping image's ratio of width to height
            quality: 1, // 0 to 1, photos only
            angle: 0, // android only, photos only
            allowsEditing: true, // Built in functionality to resize/reposition the image after selection
            noData: false, // photos only - disables the base64 `data` field from being generated (greatly improves performance on large photos)
            storageOptions: { // if this key is provided, the image will get saved in the documents directory on ios, and the pictures directory on android (rather than a temporary directory)
                skipBackup: true, // ios only - image will NOT be backed up to icloud
                path: "images" // ios only - will save image at /Documents/images rather than the root
            }
        };
        ImagePicker.showImagePicker(options, (response) => {
            if (response.didCancel) {
                console.log("User cancelled image picker");
            } else if (response.error) {
                console.log("ImagePicker Error: ", response.error);
            } else {
                // uri (on android)
                const source = {uri: response.uri, isStatic: true};

                this.setState({
                    avatarSource: source
                });
            }
        });
    }

    renderUserImage () {
        return (
            <View style={styles.userPhotoWrp}>
                <View style={styles.photoWrp}>
                    <Text style={styles.textPhoto}>{"C"}</Text>
                </View>
                <TouchableOpacity
                    onPress={() => this.showImagePicker()}
                    style={styles.iconEditPhotoWrp}
                    transparent={true}
                >
                    <Icon
                        color={colors.iconWhite}
                        name="iw-edit"
                        size={16}
                        style={{backgroundColor: colors.transparent}}
                    />
                </TouchableOpacity>
                <View style={styles.userInfoWrp}>
                    <Text style={styles.textUser}>{"Username"}</Text>
                    <Text style={styles.textEmail}>{"username@email.com"}</Text>
                </View>
            </View>
        );
    }

    renderUserOption () {
        return (
            <View style={styles.iconsUserOptionsWrp}>
                <TouchableOpacity onPress={Actions.modifyProfile} style={styles.iconOptionsWrp} transparent={true}>
                    <Icon
                        color={colors.iconWhite}
                        name="iw-edit"
                        size={23}
                        style={{backgroundColor: colors.transparent}}
                    />
                </TouchableOpacity>
                <TouchableOpacity style={styles.iconOptionsWrp} transparent={true}>
                    <Icon
                        color={colors.iconWhite}
                        name="iw-gauge"
                        size={23}
                        style={{backgroundColor: colors.transparent}}
                    />
                </TouchableOpacity>
            </View>
        );
    }

    renderProfilePercentage () {
        return (
            <View style={styles.progressBarStyleWrp}>
                <Progress.Bar
                    borderColor={colors.secondaryBlue}
                    borderRadius={30}
                    borderWidth={1}
                    color={colors.secondaryBlue}
                    height={6}
                    progress={0.5}
                    style={styles.progressBarStyle}
                    unfilledColor={colors.white}
                />
            </View>
        );
    }

    renderQuestionnairesProgress (questionnaire) {
        return (
            <View key={questionnaire.key} style={styles.progressQuestionnaire}>
                <Progress.Circle
                    animating={false}
                    borderWidth={0}
                    color={questionnaire.color}
                    progress={questionnaire.value}
                    size={80}
                />
                <TouchableOpacity style={styles.iconQuestionnaireWrp} transparent={true}>
                    <Icon
                        color={questionnaire.color}
                        name={questionnaire.icon}
                        size={52}
                    />
                </TouchableOpacity>
                <View>
                    <Text style={styles.titQuestionnaire}>
                        {questionnaire.name}
                    </Text>
                    <Text style={styles.percQuestionnaire}>
                        {questionnaire.percentage}
                        {"% completato"}
                    </Text>
                </View>
            </View>
        );
    }

    render () {
        const {height} = Dimensions.get("window");
        return (
            <View style={styles.container}>
                <Content style={{backgroundColor: colors.background, height: height}}>
                    <View style={styles.titleBarWrp}>
                        <View style={styles.titleBar}>
                            <Text style={styles.title}>{"IL MIO PROFILO"}</Text>
                        </View>
                    </View>
                    <View style={[styles.contentUserWrp, {height: height * 0.2}]}>
                        {this.renderUserImage()}
                        {this.renderUserOption()}
                    </View>
                    <View style={[styles.contentAnswerWrp, {height: height * 0.8}]}>
                        <Text style={styles.titleComplete}>{"Completa il profilo"}</Text>
                        {this.renderProfilePercentage()}
                        <View style={styles.progressQuestionnairesWrp}>
                            {this.getQuestionnaires().map(this.renderQuestionnairesProgress)}
                        </View>
                    </View>
                </Content>
            </View>
        );
    }
}

export default Profile;
<commit_msg>Added user data to profile<commit_after>import {Map} from "immutable";
import {Content} from "native-base";
import React, {Component, PropTypes} from "react";
import {Dimensions, StyleSheet, TouchableOpacity, View} from "react-native";
import * as Progress from "react-native-progress";
import {Actions} from "react-native-router-flux";
import ImagePicker from "react-native-image-picker";
import {connect} from "react-redux";

import Icon from "../components/iwapp-icons";
import * as colors from "../lib/colors";
import Text from "../components/text-lato";

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: "center",
        alignItems: "stretch",
        backgroundColor: colors.transparent,
        flexDirection: "row"
    },
    titleBarWrp: {
        justifyContent: "center",
        alignItems: "center",
        paddingVertical: 5,
        backgroundColor: colors.secondaryBlue
    },
    titleBar: {
        borderBottomWidth: 2,
        borderStyle: "solid",
        borderBottomColor: colors.buttonPrimary
    },
    title: {
        color: colors.white
    },
    contentUserWrp: {
        paddingHorizontal: 20,
        flexDirection: "row",
        justifyContent: "space-between",
        alignItems: "center",
        borderBottomColor: colors.lightGrey,
        borderBottomWidth: 1
    },
    iconsUserOptionsWrp: {
        flexDirection: "column",
        justifyContent: "center",
        alignItems: "center"
    },
    iconOptionsWrp: {
        flexDirection: "row",
        borderRadius: 20,
        width: 32,
        height: 32,
        alignItems: "center",
        justifyContent: "center",
        backgroundColor: colors.primaryBlue,
        marginTop: 6
    },
    userPhotoWrp: {
        flexDirection: "row",
        justifyContent: "flex-end",
        alignItems: "center"
    },
    photoWrp: {
        backgroundColor: colors.buttonPrimary,
        borderRadius: 100,
        width: 70,
        height: 70,
        alignItems: "center",
        justifyContent: "center"
    },
    iconEditPhotoWrp: {
        position: "absolute",
        bottom: 0,
        left: 55,
        borderRadius: 20,
        width: 22,
        height: 22,
        alignItems: "center",
        justifyContent: "center",
        backgroundColor: colors.secondaryBlue
    },
    textPhoto: {
        color: colors.white,
        fontSize: 40,
        fontWeight: "bold"
    },
    userInfoWrp: {
        paddingLeft: 18
    },
    textUser: {
        fontSize: 16,
        padding: 0
    },
    textEmail: {
        fontSize: 12,
        padding: 0
    },

    // STYLE FOR PROFILE PERCENTAGE COMPLETED
    contentAnswerWrp: {
        padding: 20
    },
    titleComplete: {
        marginBottom: 5
    },
    progressBarStyleWrp: {
        marginBottom: 20
    },
    progressBarStyle: {
        width: 200
    },

    // STYLE FOR BUTTON PERCENTAGE OF ANSWER
    progressQuestionnairesWrp: {
        flexDirection: "row",
        flexWrap: "wrap",
        justifyContent: "space-around"
    },
    progressQuestionnaire: {
        flexDirection: "column",
        alignItems: "stretch",
        marginTop: 20
    },
    iconQuestionnaireWrp: {
        position: "absolute",
        width: 80,
        height: 80,
        alignItems: "center",
        justifyContent: "center",
        backgroundColor: colors.transparent,
        top: 0,
        left: 0
    },
    titQuestionnaire: {
        fontSize: 14,
        padding: 0,
        textAlign: "center",
        marginTop: 3
    },
    percQuestionnaire: {
        fontSize: 11,
        padding: 0,
        textAlign: "center"
    }
});

class Profile extends Component {

    static propTypes = {
        asteroid: PropTypes.object.isRequired,
        collections: PropTypes.object.isRequired,
        userId: PropTypes.string
    }

    constructor (props) {
        super(props);
        this.state = {
            avatarSource: {}
        };
    }

    componentDidMount () {
        this.props.asteroid.subscribe("users");
    }

    getUser () {
        return this.props.collections.getIn(["users", this.props.userId]) || Map();
    }

    getQuestionnaires () {
        return [
            {color: colors.demographicsSection, name: "Demographics", key: "demographics", icon: "iw-demographics", value: 1, percentage: "100"},
            {color: colors.buildingsSection, name: "Buildings", key: "buildings", icon: "iw-buildings", value: 0.3, percentage: "30"},
            {color: colors.heatingSection, name: "Heating", key: "heating", icon: "iw-heating", value: 0.8, percentage: "80"},
            {color: colors.coolingSection, name: "Cooling", key: "cooling", icon: "iw-cooling", value: 1, percentage: "100"},
            {color: colors.statisticsSection, name: "Statistics", key: "statistics", icon: "iw-statistics", value: 0.5, percentage: "50"}
        ];
    }

    showImagePicker () {
        const options = {
            title: null, // specify null or empty string to remove the title
            cancelButtonTitle: "Cancella",
            takePhotoButtonTitle: "Dalla fotocamera", // specify null or empty string to remove this button
            chooseFromLibraryButtonTitle: "Dalla galleria", // specify null or empty string to remove this button
            cameraType: "front", // "front" or "back"
            mediaType: "photo", // "photo" or "video"
            maxWidth: 80, // photos only
            maxHeight: 80, // photos only
            aspectX: 1, // android only - aspectX:aspectY, the cropping image's ratio of width to height
            aspectY: 1, // android only - aspectX:aspectY, the cropping image's ratio of width to height
            quality: 1, // 0 to 1, photos only
            angle: 0, // android only, photos only
            allowsEditing: true, // Built in functionality to resize/reposition the image after selection
            noData: false, // photos only - disables the base64 `data` field from being generated (greatly improves performance on large photos)
            storageOptions: { // if this key is provided, the image will get saved in the documents directory on ios, and the pictures directory on android (rather than a temporary directory)
                skipBackup: true, // ios only - image will NOT be backed up to icloud
                path: "images" // ios only - will save image at /Documents/images rather than the root
            }
        };
        ImagePicker.showImagePicker(options, (response) => {
            if (response.didCancel) {
                console.log("User cancelled image picker");
            } else if (response.error) {
                console.log("ImagePicker Error: ", response.error);
            } else {
                // uri (on android)
                const source = {uri: response.uri, isStatic: true};

                this.setState({
                    avatarSource: source
                });
            }
        });
    }

    renderUserImage () {
        const username = this.getUser().getIn(["profile", "username"]) || "";
        const email = this.getUser().getIn(["emails", "0", "address"]) || "";
        return (
            <View style={styles.userPhotoWrp}>
                <View style={styles.photoWrp}>
                    <Text style={styles.textPhoto}>{(username[0] || email[0]).toUpperCase()}</Text>
                </View>
                <TouchableOpacity
                    onPress={() => this.showImagePicker()}
                    style={styles.iconEditPhotoWrp}
                    transparent={true}
                >
                    <Icon
                        color={colors.iconWhite}
                        name="iw-edit"
                        size={16}
                        style={{backgroundColor: colors.transparent}}
                    />
                </TouchableOpacity>
                <View style={styles.userInfoWrp}>
                    <Text style={styles.textUser}>{username}</Text>
                    <Text style={styles.textEmail}>{email}</Text>
                </View>
            </View>
        );
    }

    renderUserOption () {
        return (
            <View style={styles.iconsUserOptionsWrp}>
                <TouchableOpacity onPress={Actions.modifyProfile} style={styles.iconOptionsWrp} transparent={true}>
                    <Icon
                        color={colors.iconWhite}
                        name="iw-edit"
                        size={23}
                        style={{backgroundColor: colors.transparent}}
                    />
                </TouchableOpacity>
                <TouchableOpacity style={styles.iconOptionsWrp} transparent={true}>
                    <Icon
                        color={colors.iconWhite}
                        name="iw-gauge"
                        size={23}
                        style={{backgroundColor: colors.transparent}}
                    />
                </TouchableOpacity>
            </View>
        );
    }

    renderProfilePercentage () {
        return (
            <View style={styles.progressBarStyleWrp}>
                <Progress.Bar
                    borderColor={colors.secondaryBlue}
                    borderRadius={30}
                    borderWidth={1}
                    color={colors.secondaryBlue}
                    height={6}
                    progress={0.5}
                    style={styles.progressBarStyle}
                    unfilledColor={colors.white}
                />
            </View>
        );
    }

    renderQuestionnairesProgress (questionnaire) {
        return (
            <View key={questionnaire.key} style={styles.progressQuestionnaire}>
                <Progress.Circle
                    animating={false}
                    borderWidth={0}
                    color={questionnaire.color}
                    progress={questionnaire.value}
                    size={80}
                />
                <TouchableOpacity style={styles.iconQuestionnaireWrp} transparent={true}>
                    <Icon
                        color={questionnaire.color}
                        name={questionnaire.icon}
                        size={52}
                    />
                </TouchableOpacity>
                <View>
                    <Text style={styles.titQuestionnaire}>
                        {questionnaire.name}
                    </Text>
                    <Text style={styles.percQuestionnaire}>
                        {questionnaire.percentage}
                        {"% completato"}
                    </Text>
                </View>
            </View>
        );
    }

    render () {
        const {height} = Dimensions.get("window");
        return (
            <View style={styles.container}>
                <Content style={{backgroundColor: colors.background, height: height}}>
                    <View style={styles.titleBarWrp}>
                        <View style={styles.titleBar}>
                            <Text style={styles.title}>{"IL MIO PROFILO"}</Text>
                        </View>
                    </View>
                    <View style={[styles.contentUserWrp, {height: height * 0.2}]}>
                        {this.renderUserImage()}
                        {this.renderUserOption()}
                    </View>
                    <View style={[styles.contentAnswerWrp, {height: height * 0.8}]}>
                        <Text style={styles.titleComplete}>{"Completa il profilo"}</Text>
                        {this.renderProfilePercentage()}
                        <View style={styles.progressQuestionnairesWrp}>
                            {this.getQuestionnaires().map(this.renderQuestionnairesProgress)}
                        </View>
                    </View>
                </Content>
            </View>
        );
    }
}

function mapStateToProps (state) {
    return {
        collections: state.collections,
        userId: state.userId
    };
}
export default connect(mapStateToProps)(Profile);
<|endoftext|>


<commit_before>function app_modules(api){
    /* Application module loader */
    var m = '', err = '', cfg = ''
       ,fs = require('fs')

    for(m in api.cfg.app.modules){
        try {// load: app_modules/userman/app_back_userman.js
            if(fs.statSync(__dirname + '/' + m).isDirectory()){
                cfg = api.cfg.app.modules[m]
                m = './' + m + '/app_back_' + m + '.js'
            }
        } catch(ex){
            err = err || 'Error load app module(s) from `config`:\n'// ':' is used
        }
        if(!cfg){// load: app_modules/pingback.js
            cfg = api.cfg.app.modules[m]
            m = './' + m + '.js'
        }
        try {
            require(m)(api, cfg)
        } catch(ex){
            err += m.replace(/[.]js/, '[.js]') + ' !!!' + ex + '\n'
        }
        cfg = ''
    }
    if(':' != err[err.length - 2]) api._err(err)
    //!view.desktop.BackendTools
}

module.exports = app_modules
<commit_msg>app modules loading: show `err.stack`<commit_after>function app_modules(api){
    /* Application module loader */
    var m = '', err = '', cfg = ''
       ,fs = require('fs')

    for(m in api.cfg.app.modules){
        try {// load: app_modules/userman/app_back_userman.js
            if(fs.statSync(__dirname + '/' + m).isDirectory()){
                cfg = api.cfg.app.modules[m]
                m = './' + m + '/app_back_' + m + '.js'
            }
        } catch(ex){
            err = err || 'Error load app module(s) from `config`:\n'// ':' is used
        }
        if(!cfg){// load: app_modules/pingback.js
            cfg = api.cfg.app.modules[m]
            m = './' + m + '.js'
        }
        try {
            require(m)(api, cfg)
        } catch(ex){
            err += m.replace(/[.]js/, '[.js]') + ':\n!!!' + ex.stack + '\n'
        }
        cfg = ''
    }
    if(':' != err[err.length - 2]) api._err(err)
    //!view.desktop.BackendTools
}

module.exports = app_modules
<|endoftext|>


<commit_before>'use strict'

//let validator = require('mc-extension-validator')
let glob = require('glob')

const EXT_PATH = process.cwd() + '/node_modules/mc-ext-*'

let registry = {

  _extensions: {},
  _typesByFqids: {},

  /**
   * Resolve all `mc-ext-*` modules and register them
   *
   * @param  {string} dir Path mission control extensions
   */
  resolve: function resolve(dir) {
    let modules = glob.sync(dir)

    modules.forEach(module => {
      this.register(require(module))
    })
  },

  /**
   * Reload all extensions
   */
  reload: function reload() {

    // Reset all of our internal registry pieces
    this._extensions = {}
    this._typesByFqids = {}

    // Reload the modules
    this.resolve(EXT_PATH)
  },

  /**
   * [register provided module]
   * @param  {Object} module [Module instance]
   */
  register: function register(module) {
    if (typeof this._extensions[module.vendor] !== 'object') {
      this._extensions[module.vendor] = {}
    }

    // TODO: validate extension index

    this._extensions[module.vendor][module.id] = {
      vendor: module.vendor,
      id: module.id,
      name: module.name,
      description: module.description
    }

    this.registerStages(module)
    // TODO: register log types

  },

  registerStages: function registerStages(module) {

    if (Array.isArray(module.stages)) {

      module.stages.forEach(st => {

        // TODO: validate stage type

        // Register the stage as vendor.extension_id.stages.example
        let fqid = module.vendor + '.' + module.id + '.stages.' + st.id
        this._typesByFqids[fqid] = st

      })
    }

  },

  /**
   * Get an item/type from the extension via the FQID (full-qualified identifier)
   *
   * @param  {string} name [dot syntax path]
   * @return {object}      [resolved extension path]
   */
  get: function get(name) {
    if (typeof this._typesByFqids[name] !== 'undefined') {
      return this._typesByFqids[name]
    } else {
      throw new Error('Extension FQID "' + name + '" not found')
    }
  },

  /**
   * Get stage types
   *
   * @return {Array}
   */
  getStageTypes: function getStageTypes() {
    let stages = []

    for (let fqid in this._typesByFqids) {
      // Shallow clone so we don't mutate the stage object
      let stage = Object.assign({}, this._typesByFqids[fqid])
        stage.fqid = fqid
        stages.push(stage)
    }

    return stages
  }

}

// Initially load extensions
registry.resolve(EXT_PATH)

module.exports = registry
<commit_msg>Change abbrivated stage variable to full word, more explicit<commit_after>'use strict'

//let validator = require('mc-extension-validator')
let glob = require('glob')

const EXT_PATH = process.cwd() + '/node_modules/mc-ext-*'

let registry = {

  _extensions: {},
  _typesByFqids: {},

  /**
   * Resolve all `mc-ext-*` modules and register them
   *
   * @param  {string} dir Path mission control extensions
   */
  resolve: function resolve(dir) {
    let modules = glob.sync(dir)

    modules.forEach(module => {
      this.register(require(module))
    })
  },

  /**
   * Reload all extensions
   */
  reload: function reload() {

    // Reset all of our internal registry pieces
    this._extensions = {}
    this._typesByFqids = {}

    // Reload the modules
    this.resolve(EXT_PATH)
  },

  /**
   * [register provided module]
   * @param  {Object} module [Module instance]
   */
  register: function register(module) {
    if (typeof this._extensions[module.vendor] !== 'object') {
      this._extensions[module.vendor] = {}
    }

    // TODO: validate extension index

    this._extensions[module.vendor][module.id] = {
      vendor: module.vendor,
      id: module.id,
      name: module.name,
      description: module.description
    }

    this.registerStages(module)
    // TODO: register log types

  },

  registerStages: function registerStages(module) {

    if (Array.isArray(module.stages)) {

      module.stages.forEach(stage => {

        // TODO: validate stage type

        // Register the stage as vendor.extension_id.stages.example
        let fqid = module.vendor + '.' + module.id + '.stages.' + stage.id
        this._typesByFqids[fqid] = stage

      })
    }

  },

  /**
   * Get an item/type from the extension via the FQID (full-qualified identifier)
   *
   * @param  {string} name [dot syntax path]
   * @return {object}      [resolved extension path]
   */
  get: function get(name) {
    if (typeof this._typesByFqids[name] !== 'undefined') {
      return this._typesByFqids[name]
    } else {
      throw new Error('Extension FQID "' + name + '" not found')
    }
  },

  /**
   * Get stage types
   *
   * @return {Array}
   */
  getStageTypes: function getStageTypes() {
    let stages = []

    for (let fqid in this._typesByFqids) {
      // Shallow clone so we don't mutate the stage object
      let stage = Object.assign({}, this._typesByFqids[fqid])
        stage.fqid = fqid
        stages.push(stage)
    }

    return stages
  }

}

// Initially load extensions
registry.resolve(EXT_PATH)

module.exports = registry
<|endoftext|>


<commit_before>import autobind from "autobind-decorator";
import PropTypes from 'prop-types';
import { Component, createElement } from "react";
import hoistStatics from "hoist-non-react-statics";
import invariant from "invariant";

import InteractionManager from "./InteractionManager";

// http://stackoverflow.com/questions/1026069/capitalize-the-first-letter-of-string-in-javascript
function capitalize(s) {
	return s && s[0].toUpperCase() + s.slice(1);
}

function getDisplayName(WrappedComponent) {
	return (WrappedComponent && WrappedComponent.displayName) ||
			(WrappedComponent && WrappedComponent.name) ||
			'Component';
}

// Helps track hot reloading.
var nextVersion = 0;

/*
	The container function takes three parameters:
		* modelName - model will be stored in a property variable named `modelName`. For modelName of "foo"
				the model can be accessed using this.props.foo (required)
		* collectionPropName - property name where the f.lux collection can be found on the properties. This
				container does not utilize the context. (required)
		* options
			- `idProp` - property name containing the model's id. If set, the model is
				automatically retrieved in `componentDidMount()`.
			- `withRef` - `true` specifies the ref="wrappedInstance" for the wrapped element
				(default=false).

	The container sets up the following functions and values on the proeprties object based on the two parameters
	(assume parameters modelName='user' and fluxStoreName='userStore'):
		1) this.props.clearUser() - unsets the model property variable, ie this.props.user will be set to null
		2) this.props.clearUserError() - unsets the last error that occurred during the previous fetchUser()
				request
		3) this.props.fetchUserError - the error that occurred during the previous fetchUser() request.
		4) this.props.fetchUser(modelId, force) - the function to trigger a fetch of model with id=modelId. The
				force flag will cause the fetch to take place even if one is currently in progress.
		5) this.props.fetchUserCalled() - gets if a previous call to fetchUser() has been made. Useful for
				componentDidUpdate() guards to prevent unnecessary fetch calls.
		6) this.props.isFetchingUser() - returns true if fetching is in progress
		7) this.props.refrectModel(modelId) - trigger a call to fetchUser(modelId, true) with the force
				flag set to true.
		8) this.prpos.user` - the fetched model.
		9) this.prpos.userId` - the ID of the model [being] fetched.

	Lifecycle (assume modelName='user':
		Not fetched: this.props.user is null/undefined and this.props.isFetchingUser() returns false
		Fetching: this.props.isFetchingUser() returns true
		Fetched: this.props.user contains the model instance and this.isFetchingUser() returns false
*/
export default function fetchModelContainer(modelName, collectionPropName, options={}) {
	const { idProp=null, withRef=false } = options;
	const progressStateProp = `${modelName}Fetching`;

	// Helps track hot reloading.
	const version = nextVersion++;

	return function wrapWithContainer(WrappedComponent) {
		class FetchModelContainer extends Component {
			constructor(props, context) {
				super(props, context);

				// Helps track hot reloading.
				this.version = version;

				this.collection = props[collectionPropName];
				this.endpointId = this.collection && this.collection.endpoint.id;
				this.model = null;
				this.modelId = null;
				this.startFetchTime = null;
				this.mounted = true;
				this.displayName = getDisplayName(WrappedComponent);

				this.state = {
					isFetching: false,
					error: null,
				};
			}

			componentWillMount() {
				this.checkForCollectionChange(this.props);
				this.clearCache();
			}

			componentDidMount() {
				const modelId = idProp && this.props[idProp];

				if (modelId && this.modelId != modelId) {
					InteractionManager.runAfterInteractions( () => this.fetchModel(modelId, true) );
				}
			}

			componentWillUnmount() {
				this.mounted = false;
			}

			componentWillReceiveProps(nextProps) {
				this.checkForCollectionChange(nextProps);
			}

			checkForCollectionChange(props) {
				const collection = props[collectionPropName];

				if (collection === this.collection) { return }

				this.collection = collection;

				if (!collection) {
					this.collection = null;
					this.endpointId = null;
					this.clearModel();
				} else {
					const nextModelId = idProp && this.props[idProp];

					 if (this.endpointId !== collection.endpoint.id) {
						// TODO - should the model be refetched?

						this.clearModel();
					}

					this.endpointId = collection.endpoint.id;

					if (idProp && nextModelId != this.modelId) {
						this.clearModel();
						this.clearError();
						InteractionManager.runAfterInteractions(
								() => this.fetchModel(nextModelId, true)
							);
					} else if (this.modelId) {
						this.model = collection.get(this.modelId);
					}
				}
			}

			clearCache() {
				this.model = null;
				this.modelId = null;
				this.startFetchTime = null;
			}

			@autobind
			clearError() {
				if (this.mounted) {
					this.setState({ error: null });
				}
			}

			@autobind
			clearModel() {
				this.model = null;
				this.modelId = null;
				this.startFetchTime = null;

				if (this.mounted) {
					this.setState({
							isFetching: false,
						});
				}
			}

			@autobind
			fetchCalled() {
				return this.modelId || this.startFetchTime;
			}

			@autobind
			fetchModel(modelId, force=false) {
				modelId = modelId || (idProp && this.props[idProp]);

				if ((this.startFetchTime || this.modelId) && !force) { return }

				invariant(this.collection, `Could not find "${collectionPropName}"" in the props of ` +
					`<${this.constructor.displayName}>. Wrap the root component in a storeContainer(), or explicitly ` +
					`pass "${collectionPropName}" collection as a prop to <${this.constructor.displayName}>.`
				)

				const time = Date.now();

				this.model = null;
				this.modelId = modelId;
				this.startFetchTime = time;

				if (this.mounted) {
					this.setState({
							isFetching: true,
							error: null,
						});
				}
				this.collection.find(modelId)
					.then( model => {
							if (this.startFetchTime != time) {
								console.info(
									`fetchModelContainer::fetchModel() - fetch for ${modelName}:${modelId} @${time} ` +
									`superceded by @${this.startFetchTime} - force=${force}`
								);

								return;
							}

							this.startFetchTime = null;
							this.model = model;

							if (this.mounted) {
								this.setState({
										isFetching: false,
									});
							}
						})
					.catch( error => {
						// only report errors for most recent request
						if (this.startFetchTime == time) {
							// remove the timestamp
							this.startFetchTime = null;

							if (this.mounted) {
								this.setState({
										isFetching: false,
										error
									});
							}
						}

						console.warn(
							`fetchModelContainer::fetchModelMixin() - Error find ${modelName}: ` +
							`id=${modelId}, error=${error}`
						);

						if (error.stack) {
							console.warn(error.stack);
						}
					})

			}

			getWrappedInstance() {
				invariant(
						withRef,
						`To access the wrapped instance, you need to specify  { withRef: true } as the fourth `+
							`argument of the fetchModelContainer() call.`
					);

				var wrapped = this.refs.wrappedInstance;

				while(wrapped.refs.wrappedInstance) {
					wrapped = wrapped.refs.wrappedInstance;
				}

				return wrapped;
			}

			@autobind
			isFetching() {
				return (this.state.isFetching || this.startFetchTime) && this.mounted;
			}

			mergeProps() {
				return {
					...this.props,
					[`clear${capitalize(modelName)}Error`]: this.clearError,
					[`clear${capitalize(modelName)}`]: this.clearModel,
					[`fetch${capitalize(modelName)}`]: this.fetchModel,
					[`fetch${capitalize(modelName)}Called`]: this.fetchCalled,
					[`isFetching${capitalize(modelName)}`]: this.isFetching,
					[`refetch${capitalize(modelName)}`]: this.refetchModel,
					[modelName]: this.model,
					[`${modelName}Id`]: this.state.modelId,
					[`${modelName}Error`]: this.state.error,
				}
			}

			@autobind
			refetchModel(modelId) {
				this.fetchModel(modelId, true);
			}

			render() {
				const mergedProps = this.mergeProps();

				if (withRef) {
					return this.renderedElement = createElement(WrappedComponent, {
							...mergedProps,
							ref: 'wrappedInstance'
						});
				} else {
					return this.renderedElement = createElement(WrappedComponent, mergedProps);
				}
			}
		}

		FetchModelContainer.displayName = `FetchModelContainer(${getDisplayName(WrappedComponent)})`
		FetchModelContainer.WrappedComponent = WrappedComponent

		FetchModelContainer.propTypes = {
			[collectionPropName]: PropTypes.object,
		}

		if (process.env.NODE_ENV !== "production") {
			FetchModelContainer.prototype.componentWillUpdate = function componentWillUpdate() {
				if (this.version === version) {
					return
				}

				// We are hot reloading!
				this.version = version
				this.clearCache()
			}

			return hoistStatics(FetchModelContainer, WrappedComponent)
		} else {
			return FetchModelContainer;
		}
	}
}<commit_msg>compare modelId and refetch if different<commit_after>import autobind from "autobind-decorator";
import PropTypes from 'prop-types';
import { Component, createElement } from "react";
import hoistStatics from "hoist-non-react-statics";
import invariant from "invariant";

import InteractionManager from "./InteractionManager";

// http://stackoverflow.com/questions/1026069/capitalize-the-first-letter-of-string-in-javascript
function capitalize(s) {
	return s && s[0].toUpperCase() + s.slice(1);
}

function getDisplayName(WrappedComponent) {
	return (WrappedComponent && WrappedComponent.displayName) ||
			(WrappedComponent && WrappedComponent.name) ||
			'Component';
}

// Helps track hot reloading.
var nextVersion = 0;

/*
	The container function takes three parameters:
		* modelName - model will be stored in a property variable named `modelName`. For modelName of "foo"
				the model can be accessed using this.props.foo (required)
		* collectionPropName - property name where the f.lux collection can be found on the properties. This
				container does not utilize the context. (required)
		* options
			- `idProp` - property name containing the model's id. If set, the model is
				automatically retrieved in `componentDidMount()`.
			- `withRef` - `true` specifies the ref="wrappedInstance" for the wrapped element
				(default=false).

	The container sets up the following functions and values on the proeprties object based on the two parameters
	(assume parameters modelName='user' and fluxStoreName='userStore'):
		1) this.props.clearUser() - unsets the model property variable, ie this.props.user will be set to null
		2) this.props.clearUserError() - unsets the last error that occurred during the previous fetchUser()
				request
		3) this.props.fetchUserError - the error that occurred during the previous fetchUser() request.
		4) this.props.fetchUser(modelId, force) - the function to trigger a fetch of model with id=modelId. The
				force flag will cause the fetch to take place even if one is currently in progress.
		5) this.props.fetchUserCalled() - gets if a previous call to fetchUser() has been made. Useful for
				componentDidUpdate() guards to prevent unnecessary fetch calls.
		6) this.props.isFetchingUser() - returns true if fetching is in progress
		7) this.props.refrectModel(modelId) - trigger a call to fetchUser(modelId, true) with the force
				flag set to true.
		8) this.prpos.user` - the fetched model.
		9) this.prpos.userId` - the ID of the model [being] fetched.

	Lifecycle (assume modelName='user':
		Not fetched: this.props.user is null/undefined and this.props.isFetchingUser() returns false
		Fetching: this.props.isFetchingUser() returns true
		Fetched: this.props.user contains the model instance and this.isFetchingUser() returns false
*/
export default function fetchModelContainer(modelName, collectionPropName, options={}) {
	const { idProp=null, withRef=false } = options;
	const progressStateProp = `${modelName}Fetching`;

	// Helps track hot reloading.
	const version = nextVersion++;

	return function wrapWithContainer(WrappedComponent) {
		class FetchModelContainer extends Component {
			constructor(props, context) {
				super(props, context);

				// Helps track hot reloading.
				this.version = version;

				this.collection = props[collectionPropName];
				this.endpointId = this.collection && this.collection.endpoint.id;
				this.model = null;
				this.modelId = null;
				this.startFetchTime = null;
				this.mounted = true;
				this.displayName = getDisplayName(WrappedComponent);

				this.state = {
					isFetching: false,
					error: null,
				};
			}

			componentWillMount() {
				this.checkForCollectionChange(this.props);
				this.clearCache();
			}

			componentDidMount() {
				const modelId = idProp && this.props[idProp];

				if (modelId && this.modelId != modelId) {
					InteractionManager.runAfterInteractions( () => this.fetchModel(modelId, true) );
				}
			}

			componentWillUnmount() {
				this.mounted = false;
			}

			componentWillReceiveProps(nextProps) {
				this.checkForCollectionChange(nextProps);
			}

			checkForCollectionChange(props) {
				const collection = props[collectionPropName];

				if (collection === this.collection) { return }

				this.collection = collection;

				if (!collection) {
					this.collection = null;
					this.endpointId = null;
					this.clearModel();
				} else {
					const nextModelId = idProp && this.props[idProp];

					 if (this.endpointId !== collection.endpoint.id) {
						// TODO - should the model be refetched?

						this.clearModel();
					}

					this.endpointId = collection.endpoint.id;

					if (idProp && nextModelId != this.modelId) {
						this.clearModel();
						this.clearError();
						InteractionManager.runAfterInteractions(
								() => this.fetchModel(nextModelId, true)
							);
					} else if (this.modelId) {
						this.model = collection.get(this.modelId);
					}
				}
			}

			clearCache() {
				this.model = null;
				this.modelId = null;
				this.startFetchTime = null;
			}

			@autobind
			clearError() {
				if (this.mounted) {
					this.setState({ error: null });
				}
			}

			@autobind
			clearModel() {
				this.model = null;
				this.modelId = null;
				this.startFetchTime = null;

				if (this.mounted) {
					this.setState({
							isFetching: false,
						});
				}
			}

			@autobind
			fetchCalled() {
				return this.modelId || this.startFetchTime;
			}

			@autobind
			fetchModel(modelId, force=false) {
				modelId = modelId || (idProp && this.props[idProp]);

				if ((this.startFetchTime || this.modelId == modelId) && !force) { return }

				invariant(this.collection, `Could not find "${collectionPropName}"" in the props of ` +
					`<${this.constructor.displayName}>. Wrap the root component in a storeContainer(), or explicitly ` +
					`pass "${collectionPropName}" collection as a prop to <${this.constructor.displayName}>.`
				)

				const time = Date.now();

				this.model = null;
				this.modelId = modelId;
				this.startFetchTime = time;

				if (this.mounted) {
					this.setState({
							isFetching: true,
							error: null,
						});
				}
				this.collection.find(modelId)
					.then( model => {
							if (this.startFetchTime != time) {
								console.info(
									`fetchModelContainer::fetchModel() - fetch for ${modelName}:${modelId} @${time} ` +
									`superceded by @${this.startFetchTime} - force=${force}`
								);

								return;
							}

							this.startFetchTime = null;
							this.model = model;

							if (this.mounted) {
								this.setState({
										isFetching: false,
									});
							}
						})
					.catch( error => {
						// only report errors for most recent request
						if (this.startFetchTime == time) {
							// remove the timestamp
							this.startFetchTime = null;

							if (this.mounted) {
								this.setState({
										isFetching: false,
										error
									});
							}
						}

						console.warn(
							`fetchModelContainer::fetchModelMixin() - Error find ${modelName}: ` +
							`id=${modelId}, error=${error}`
						);

						if (error.stack) {
							console.warn(error.stack);
						}
					})

			}

			getWrappedInstance() {
				invariant(
						withRef,
						`To access the wrapped instance, you need to specify  { withRef: true } as the fourth `+
							`argument of the fetchModelContainer() call.`
					);

				var wrapped = this.refs.wrappedInstance;

				while(wrapped.refs.wrappedInstance) {
					wrapped = wrapped.refs.wrappedInstance;
				}

				return wrapped;
			}

			@autobind
			isFetching() {
				return (this.state.isFetching || this.startFetchTime) && this.mounted;
			}

			mergeProps() {
				return {
					...this.props,
					[`clear${capitalize(modelName)}Error`]: this.clearError,
					[`clear${capitalize(modelName)}`]: this.clearModel,
					[`fetch${capitalize(modelName)}`]: this.fetchModel,
					[`fetch${capitalize(modelName)}Called`]: this.fetchCalled,
					[`isFetching${capitalize(modelName)}`]: this.isFetching,
					[`refetch${capitalize(modelName)}`]: this.refetchModel,
					[`fetch${capitalize(modelName)}Id`]: this.modelId,
					[modelName]: this.model,
					[`${modelName}Id`]: this.state.modelId,
					[`${modelName}Error`]: this.state.error,
				}
			}

			@autobind
			refetchModel(modelId) {
				this.fetchModel(modelId, true);
			}

			render() {
				const mergedProps = this.mergeProps();

				if (withRef) {
					return this.renderedElement = createElement(WrappedComponent, {
							...mergedProps,
							ref: 'wrappedInstance'
						});
				} else {
					return this.renderedElement = createElement(WrappedComponent, mergedProps);
				}
			}
		}

		FetchModelContainer.displayName = `FetchModelContainer(${getDisplayName(WrappedComponent)})`
		FetchModelContainer.WrappedComponent = WrappedComponent

		FetchModelContainer.propTypes = {
			[collectionPropName]: PropTypes.object,
		}

		if (process.env.NODE_ENV !== "production") {
			FetchModelContainer.prototype.componentWillUpdate = function componentWillUpdate() {
				if (this.version === version) {
					return
				}

				// We are hot reloading!
				this.version = version
				this.clearCache()
			}

			return hoistStatics(FetchModelContainer, WrappedComponent)
		} else {
			return FetchModelContainer;
		}
	}
}<|endoftext|>


<commit_before>var cordova = require('cordova');

module.exports = {

  register: function (onNotification, success, fail, config) {
    var client = AeroGear.UnifiedPushClient(config.simplePush.variantID, config.simplePush.variantSecret, config.pushServerURL + '/rest/registry/device');

    var req = navigator.push.register();
    req.onsuccess = function (e) {
      config.deviceToken = req.result;
      var settings = {
        success: success,
        error: fail
      };

      settings.metadata = config;
      client.registerWithPushServer(settings);
    };

    navigator.mozSetMessageHandler('push', function (message) {
      onNotification(message);
    });
  }
};

require("cordova/exec/proxy").add("push", module.exports);<commit_msg>change config from 'simplePush' to fireboxes to be inline with cordova naming and added support for firefoxos only config<commit_after>var cordova = require('cordova');

module.exports = {
  register: function (onNotification, success, fail, config) {
    var pushConfig = config.firefoxos || config;
    var client = AeroGear.UnifiedPushClient(pushConfig.variantID, pushConfig.variantSecret, config.pushServerURL + '/rest/registry/device');

    var req = navigator.push.register();
    req.onsuccess = function (e) {
      config.deviceToken = req.result;
      var settings = {
        success: success,
        error: fail
      };

      settings.metadata = config;
      client.registerWithPushServer(settings);
    };

    navigator.mozSetMessageHandler('push', function (message) {
      onNotification(message);
    });
  }
};

require("cordova/exec/proxy").add("push", module.exports);<|endoftext|>