<commit_before>define(function(require) {

  var Detection = require('lavaca/env/Detection'),
      PageView = require('lavaca/mvc/PageView'),
      Promise = require('lavaca/util/Promise'),
      viewManager = require('lavaca/mvc/ViewManager'),
      common = require('app/ui/common'),
      $ = require('$'),
      History = require('lavaca/net/History');
  require('lavaca/fx/Animation'); //jquery plugins

  /**
   * A View from which all other application Views can extend.
   * Adds support for animating between views.
   *
   * @class app.ui.views.BasePageView
   * @extends Lavaca.mvc.View
   *
   */
  var BasePageView = PageView.extend(function() {
    PageView.apply(this, arguments);
    this.mapEvent('.cancel', 'tap', this.onTapCancel);
    common.mapGlobals.call(this);
  }, {

    /**
     * The name of the template used by the view
     * @property {String} template
     * @default 'default'
     */
    template: 'default',
    /**
     * The name of the template used by the view
     * @property {Object} pageTransition
     * @default 'default'

     */
    pageTransition: {
      'in': '',
      'out': '',
      'inReverse': '',
      'outReverse': ''
    },
    /**
     * Executes when the template renders successfully. This implementation
     * adds support for animations between views, based off of the animation
     * property on the prototype.
     * @method onRenderSuccess
     *
     * @param {Event} e  The render event. This object should have a string property named "html"
     *   that contains the template's rendered HTML output.
     */
    onRenderSuccess: function() {
      // Scroll iPhone past url bar
      if (!navigator.standalone && (Detection.iphone || Detection.ipod)) {
        var height = document.documentElement.clientHeight;
        $(document.body).height(height + 60);
        setTimeout(scrollTo, 0, 0, 1);
      }
      PageView.prototype.onRenderSuccess.apply(this, arguments);
    },
    /**
     * Handler for when a cancel control is tapped
     * @method onTapCancel
     *
     * @param {Event} e  The tap event.
     */
    onTapCancel: function(e) {
      e.preventDefault();
      viewManager.dismiss(e.currentTarget);
    },
    /**
     * Executes when the user navigates to this view. This implementation
     * adds support for animations between views, based off of the animation
     * property on the prototype.
     * @method enter
     *
     * @param {jQuery} container  The parent element of all views
     * @param {Array} exitingViews  The views that are exiting as this one enters
     * @return {Lavaca.util.Promise} A promise
     */
    enter: function(container, exitingViews) {
      return PageView.prototype.enter.apply(this, arguments)
        .then(function() {
          if (History.isRoutingBack) {
            if (History.animationBreadcrumb.length > 0) {
              this.pageTransition = History.animationBreadcrumb.pop();
            }
          } else {
            History.animationBreadcrumb.push(this.pageTransition);
          }
          var animationIn = History.isRoutingBack ? this.pageTransition['inReverse']:this.pageTransition['in'],
              animationOut = History.isRoutingBack ? this.pageTransition['outReverse']:this.pageTransition['out'],
              i = -1,
              exitingView;

          var triggerEnterComplete = function() {
            this.trigger('entercomplete');
            this.shell.removeClass(animationIn);
          };

          if (Detection.animationEnabled && animationIn !== '') {

            if (exitingViews.length) {
              i = -1;
              while (!!(exitingView = exitingViews[++i])) {
                exitingView.shell.addClass(animationOut);
                if (animationOut === '') {
                  exitingView.exitPromise.resolve();
                }
              }
            }

            if ((this.layer > 0 || exitingViews.length > 0)) {
              this.shell
                  .nextAnimationEnd(triggerEnterComplete.bind(this))
                  .addClass(animationIn + ' current');
            } else {
              this.shell.addClass('current');
              this.trigger('entercomplete');
            }

          } else {
            this.shell.addClass('current');
            if (exitingViews.length > 0) {
              i = -1;
              while (!!(exitingView = exitingViews[++i])) {
                exitingView.shell.removeClass('current');
                if (exitingView.exitPromise) {
                  exitingView.exitPromise.resolve();
                }
              }
            }
            this.trigger('entercomplete');
          }
        });
    },
    /**
     * Executes when the user navigates away from this view. This implementation
     * adds support for animations between views, based off of the animation
     * property on the prototype.
     * @method exit
     *
     * @param {jQuery} container  The parent element of all views
     * @param {Array} enteringViews  The views that are entering as this one exits
     * @return {Lavaca.util.Promise} A promise
     */
    exit: function(container, enteringViews) {
      var animation = History.isRoutingBack ? this.pageTransition['outReverse'] : (enteringViews.length ? enteringViews[0].pageTransition['out'] : '');

      if (History.isRoutingBack && this.shell.data('layer-index') > 0) {
        this.pageTransition = History.animationBreadcrumb.pop();
        animation = this.pageTransition['outReverse'];
      }

      if (Detection.animationEnabled && animation) {
        this.exitPromise = new Promise(this);

        this.shell
          .nextAnimationEnd(function() {
            PageView.prototype.exit.apply(this, arguments).then(function() {
              this.exitPromise.resolve();
            });
            this.shell.removeClass(animation + ' current');
          }.bind(this))
          .addClass(animation);

        return this.exitPromise;
      } else {
        this.shell.removeClass('current');
        return PageView.prototype.exit.apply(this, arguments);
      }
    }
  });

  return BasePageView;

});
<commit_msg>Remove auto scroll past native url bar<commit_after>define(function(require) {

  var Detection = require('lavaca/env/Detection'),
      PageView = require('lavaca/mvc/PageView'),
      Promise = require('lavaca/util/Promise'),
      viewManager = require('lavaca/mvc/ViewManager'),
      common = require('app/ui/common'),
      $ = require('$'),
      History = require('lavaca/net/History');
  require('lavaca/fx/Animation'); //jquery plugins

  /**
   * A View from which all other application Views can extend.
   * Adds support for animating between views.
   *
   * @class app.ui.views.BasePageView
   * @extends Lavaca.mvc.View
   *
   */
  var BasePageView = PageView.extend(function() {
    PageView.apply(this, arguments);
    this.mapEvent('.cancel', 'tap', this.onTapCancel);
    common.mapGlobals.call(this);
  }, {

    /**
     * The name of the template used by the view
     * @property {String} template
     * @default 'default'
     */
    template: 'default',
    /**
     * The name of the template used by the view
     * @property {Object} pageTransition
     * @default 'default'

     */
    pageTransition: {
      'in': '',
      'out': '',
      'inReverse': '',
      'outReverse': ''
    },
    /**
     * Handler for when a cancel control is tapped
     * @method onTapCancel
     *
     * @param {Event} e  The tap event.
     */
    onTapCancel: function(e) {
      e.preventDefault();
      viewManager.dismiss(e.currentTarget);
    },
    /**
     * Executes when the user navigates to this view. This implementation
     * adds support for animations between views, based off of the animation
     * property on the prototype.
     * @method enter
     *
     * @param {jQuery} container  The parent element of all views
     * @param {Array} exitingViews  The views that are exiting as this one enters
     * @return {Lavaca.util.Promise} A promise
     */
    enter: function(container, exitingViews) {
      return PageView.prototype.enter.apply(this, arguments)
        .then(function() {
          if (History.isRoutingBack) {
            if (History.animationBreadcrumb.length > 0) {
              this.pageTransition = History.animationBreadcrumb.pop();
            }
          } else {
            History.animationBreadcrumb.push(this.pageTransition);
          }
          var animationIn = History.isRoutingBack ? this.pageTransition['inReverse']:this.pageTransition['in'],
              animationOut = History.isRoutingBack ? this.pageTransition['outReverse']:this.pageTransition['out'],
              i = -1,
              exitingView;

          var triggerEnterComplete = function() {
            this.trigger('entercomplete');
            this.shell.removeClass(animationIn);
          };

          if (Detection.animationEnabled && animationIn !== '') {

            if (exitingViews.length) {
              i = -1;
              while (!!(exitingView = exitingViews[++i])) {
                exitingView.shell.addClass(animationOut);
                if (animationOut === '') {
                  exitingView.exitPromise.resolve();
                }
              }
            }

            if ((this.layer > 0 || exitingViews.length > 0)) {
              this.shell
                  .nextAnimationEnd(triggerEnterComplete.bind(this))
                  .addClass(animationIn + ' current');
            } else {
              this.shell.addClass('current');
              this.trigger('entercomplete');
            }

          } else {
            this.shell.addClass('current');
            if (exitingViews.length > 0) {
              i = -1;
              while (!!(exitingView = exitingViews[++i])) {
                exitingView.shell.removeClass('current');
                if (exitingView.exitPromise) {
                  exitingView.exitPromise.resolve();
                }
              }
            }
            this.trigger('entercomplete');
          }
        });
    },
    /**
     * Executes when the user navigates away from this view. This implementation
     * adds support for animations between views, based off of the animation
     * property on the prototype.
     * @method exit
     *
     * @param {jQuery} container  The parent element of all views
     * @param {Array} enteringViews  The views that are entering as this one exits
     * @return {Lavaca.util.Promise} A promise
     */
    exit: function(container, enteringViews) {
      var animation = History.isRoutingBack ? this.pageTransition['outReverse'] : (enteringViews.length ? enteringViews[0].pageTransition['out'] : '');

      if (History.isRoutingBack && this.shell.data('layer-index') > 0) {
        this.pageTransition = History.animationBreadcrumb.pop();
        animation = this.pageTransition['outReverse'];
      }

      if (Detection.animationEnabled && animation) {
        this.exitPromise = new Promise(this);

        this.shell
          .nextAnimationEnd(function() {
            PageView.prototype.exit.apply(this, arguments).then(function() {
              this.exitPromise.resolve();
            });
            this.shell.removeClass(animation + ' current');
          }.bind(this))
          .addClass(animation);

        return this.exitPromise;
      } else {
        this.shell.removeClass('current');
        return PageView.prototype.exit.apply(this, arguments);
      }
    }
  });

  return BasePageView;

});
<|endoftext|>


<commit_before>/* =====================
	Pumoonkust gulpfile.
   ===================== */
'use strict';

var gulp = require('gulp');
var sass = require('gulp-ruby-sass');

gulp.task('sass', function() {
	var conf = {
		sourcemap: true,
	};

	return sass('./sass/*.scss', conf)
		.on('error', sass.logError)
		.pipe(gulp.dest('./css'));
});

// Watch
gulp.task('sass:watch', function() {
	gulp.watch('./sass/**/*.scss', ['sass']);
});

// Default
gulp.task('default', function() {
});
<commit_msg>Gulp: Added a sass requirement to sass:watch<commit_after>/* =====================
	Pumoonkust gulpfile.
   ===================== */
'use strict';

var gulp = require('gulp');
var sass = require('gulp-ruby-sass');

gulp.task('sass', function() {
	var conf = {
		sourcemap: true,
	};

	return sass('./sass/*.scss', conf)
		.on('error', sass.logError)
		.pipe(gulp.dest('./css'));
});

// Watch
gulp.task('sass:watch', ['sass'], function() {
	gulp.watch('./sass/**/*.scss', ['sass']);
});

// Default
gulp.task('default', function() {
});
<|endoftext|>


<commit_before><commit_msg>Update ogario.v4.js<commit_after><|endoftext|>


<commit_before>import nanoid from 'nanoid';
import React, { Component } from 'react';
import Select from 'react-select';
import { addPreset, removePreset, subscribePresets, updatePreset } from '../../actions/budgets';
import { connect } from 'react-redux';
import { convertToList } from '../../helpers/reformat';

class PresetBudgets extends Component {
  constructor() {
    super();

    this.state = {
      name: '',
      preset: '',
    }

    this.onAdd = this.onAdd.bind(this);
    this.onChange = this.onChange.bind(this);
    this.onChangePreset = this.onChangePreset.bind(this);
    this.onDelete = this.onDelete.bind(this);
    this.onLoad = this.onLoad.bind(this);
    this.onUpdate = this.onUpdate.bind(this);
  }

  componentDidMount() {
    this.props.dispatch(subscribePresets());
  }

  onAdd(e) {
    e.preventDefault();
    const payload = {
      budget: {
        label: this.state.name,
        value: this.props.query,
        id: nanoid(),
      },
    };

    this.props.dispatch(addPreset(payload));

    this.setState({
      ...this.state,
      name: '',
    })
  }

  onChange(property, value) {
    this.setState({
      ...this.state,
      [property]: value,
    });
  }

  onChangePreset(preset) {
    this.setState({
      ...this.state,
      preset: preset,
    });
  }

  onDelete() {
    if(this.state.preset === null) {
      return;
    }

    const payload = {
      elementId: this.state.preset.id,
    };
    console.log(payload);
    this.props.dispatch(removePreset(payload));

    this.setState({
      ...this.state,
      preset: null,
    });
  }

  onLoad() {
    if(this.state.preset === null) {
      this.props.search({});
      return;
    }
    this.props.search(this.state.preset.value);
  }

  onUpdate() {
    console.log('Preset', this.state.preset.id);
    const payload = {
      preset: {
        label: this.state.preset.label,
        value: this.props.query,
        id: this.state.preset.id,
      }
    };
    this.props.dispatch(updatePreset(payload));
  }

  render () {
    return (

Load Budget

        Load:
        <Select
          className="select-bar"
          onChange={this.onChangePreset}
          options={this.props.presets}
          value={this.state.preset}
        />
        <button
          className="btn btn-primary"
          onClick={this.onLoad}>
          Load
        </button><button
          className="btn btn-primary"
          onClick={this.onUpdate}>
          Update
        </button>
        <button
          className="btn btn-primary"
          onClick={this.onDelete}>
          Delete
        </button>
        <form onSubmit={this.onAdd}>
          Save As:
          <input
            onChange={e => this.onChange('name', e.target.value)}
            value={this.state.name}
          >
          </input>
          <button
            className="btn btn-primary"
            type="submit">
            Save New
          </button>
        </form>

    );
  }
}

const mapPropsToState = state => ({
  presets: convertToList(state.budgets.presets),
  query: state.budgets.query,
});

export default connect(mapPropsToState)(PresetBudgets);
<commit_msg>Fix error loading updated preset<commit_after>import flatten from 'flat';
import nanoid from 'nanoid';
import React, { Component } from 'react';
import Select from 'react-select';
import { addPreset, removePreset, subscribePresets, updatePreset } from '../../actions/budgets';
import { connect } from 'react-redux';
import { convertToList } from '../../helpers/reformat';

class PresetBudgets extends Component {
  constructor() {
    super();

    this.state = {
      name: '',
      preset: '',
    }

    this.onAdd = this.onAdd.bind(this);
    this.onChange = this.onChange.bind(this);
    this.onChangePreset = this.onChangePreset.bind(this);
    this.onDelete = this.onDelete.bind(this);
    this.onLoad = this.onLoad.bind(this);
    this.onUpdate = this.onUpdate.bind(this);
  }

  componentDidMount() {
    this.props.dispatch(subscribePresets());
  }

  onAdd(e) {
    e.preventDefault();
    const payload = {
      budget: {
        label: this.state.name,
        value: this.props.query,
        id: nanoid(),
      },
    };
    this.props.dispatch(addPreset(flatten.unflatten({ ...payload })));

    this.setState({
      ...this.state,
      name: '',
    })
  }

  onChange(property, value) {
    this.setState({
      ...this.state,
      [property]: value,
    });
  }

  onChangePreset(preset) {
    this.setState({
      ...this.state,
      preset: preset,
    });
  }

  onDelete() {
    if(this.state.preset === null) {
      return;
    }

    const payload = {
      elementId: this.state.preset.id,
    };
    this.props.dispatch(removePreset(payload));

    this.setState({
      ...this.state,
      preset: null,
    });
  }

  onLoad() {
    if(this.state.preset === null) {
      this.props.search({});
      return;
    }

    const preset = this.props.presets.filter(preset => preset.id === this.state.preset.id)[0];
    const maintainedProps = this.props.maintainDepth.map((property) => preset.value[property]);
    let query = {...preset.value};
    let newQuery = {};

    this.props.maintainDepth.map((property) => {
      query[property] = undefined;
    });

    newQuery = flatten(query);
    this.props.maintainDepth.map((property, index) => {
      console.log(property);
      newQuery[property] = maintainedProps[index];
    });

    this.props.search(newQuery);
  }

  onUpdate() {

    const payload = {
      preset: {
        label: this.state.preset.label,
        value: this.props.query,
        id: this.state.preset.id,
      }
    };
    this.props.dispatch(updatePreset(flatten.unflatten({ ...payload })));
  }

  render () {
    return (

Load Budget

        Load:
        <Select
          className="select-bar"
          onChange={this.onChangePreset}
          options={this.props.presets}
          value={this.state.preset}
        />
        <button
          className="btn btn-primary"
          onClick={this.onLoad}>
          Load
        </button><button
          className="btn btn-primary"
          onClick={this.onUpdate}>
          Update
        </button>
        <button
          className="btn btn-primary"
          onClick={this.onDelete}>
          Delete
        </button>
        <form onSubmit={this.onAdd}>
          Save As:
          <input
            onChange={e => this.onChange('name', e.target.value)}
            required={true}
            value={this.state.name}
          >
          </input>
          <button
            className="btn btn-primary"
            type="submit">
            Save New
          </button>
        </form>

    );
  }
}

PresetBudgets.defaultProps = {
  maintainDepth: [
    'tags',
  ],
};

const mapPropsToState = state => ({
  presets: convertToList(state.budgets.presets),
  query: state.budgets.query,
});

export default connect(mapPropsToState)(PresetBudgets);
<|endoftext|>


<commit_before>import fs from 'fs';
import path from 'path';
import appRootDir from 'app-root-dir';

export default (noCoverage) => {
  const rootDir = appRootDir.get();
  const setupTestFilePath = path.resolve(rootDir, 'stanza', 'setup-test.js');
  const setupTestsFile = fs.existsSync(setupTestFilePath)
    ? setupTestFilePath
    : undefined;

  return {
    rootDir,
    setupTestFrameworkScriptFile: setupTestsFile,
    testEnvironment: 'node',
    testURL: 'http://localhost',
    moduleFileExtensions: ['js', 'jsx'],
    moduleDirectories: ['src', 'shared', 'node_modules'],
    transform: {
      '^.+\\.(js|jsx)$': path.resolve(__dirname, 'babel-transform.js'),
    },
    moduleNameMapper: {
      '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': path.resolve(
        __dirname,
        'asset-mock.js',
      ),
      '\\.(css|less|sass|scss)$': path.resolve(__dirname, 'style-mock.js'),
    },
    testPathIgnorePatterns: [
      '<rootDir>[/\\\\](build|docs|node_modules|scripts)[/\\\\]',
      '/_fixtures',
    ],
    collectCoverage: !noCoverage,
    coverageReporters: ['json', 'html', 'text-summary'],
    collectCoverageFrom: ['src/**/*.{js,jsx}'],
    coveragePathIgnorePatterns: ['/node_modules', '/test'],
  };
};
<commit_msg>add json file extension on jest<commit_after>import fs from 'fs';
import path from 'path';
import appRootDir from 'app-root-dir';

export default (noCoverage) => {
  const rootDir = appRootDir.get();
  const setupTestFilePath = path.resolve(rootDir, 'stanza', 'setup-test.js');
  const setupTestsFile = fs.existsSync(setupTestFilePath)
    ? setupTestFilePath
    : undefined;

  return {
    rootDir,
    setupTestFrameworkScriptFile: setupTestsFile,
    testEnvironment: 'node',
    testURL: 'http://localhost',
    moduleFileExtensions: ['js', 'jsx', 'json'],
    moduleDirectories: ['src', 'shared', 'node_modules'],
    transform: {
      '^.+\\.(js|jsx)$': path.resolve(__dirname, 'babel-transform.js'),
    },
    moduleNameMapper: {
      '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': path.resolve(
        __dirname,
        'asset-mock.js',
      ),
      '\\.(css|less|sass|scss)$': path.resolve(__dirname, 'style-mock.js'),
    },
    testPathIgnorePatterns: [
      '<rootDir>[/\\\\](build|docs|node_modules|scripts)[/\\\\]',
      '/_fixtures',
    ],
    collectCoverage: !noCoverage,
    coverageReporters: ['json', 'html', 'text-summary'],
    collectCoverageFrom: ['src/**/*.{js,jsx}'],
    coveragePathIgnorePatterns: ['/node_modules', '/test'],
  };
};
<|endoftext|>


<commit_before>'use strict'

window.angular.module('scegratooApp')
  .directive('sgtX3d', function SgtX3d ($window, X3domUtils, $http, $q, $templateCache, React, TreeView, _, R, InlineList, Project, Moveable, moveables, $routeParams, x3dom, x3dQuery) {
    const {
      flatten,
      eq
    } = R
    const {
      $,
      Rx
    } = $window

    const styles = {
      item: {
        padding: '5px',
        flex: '1'
      }
    }

    let colorCache

    const start = function (event) {
      const inline = $(event.hitObject).lastParent('inline')
      colorCache = inline.color()
      inline.color('yellow')
      inline.addClass('mousedown')

      Rx.Observable
        .fromEvent(document, 'mouseup')
        .take(1)
        .forEach(function stop (event) {
          inline.color(colorCache)
          inline.removeClass('mousedown')
          console.log('tada')
        })
    }

    const setUpInline = (inline, x3dNode) => {
      // add eventlisteners to make it selectable
      inline.addEventListener('mousedown', start)

      // keep a weak reference from the tranlation to the moveable around to
      // remove the event handlers the moveable registers on the translation
      // again if the translation is removed
      if (!moveables.has(inline)) {
        moveables.set(inline.parentNode, new Moveable(x3dNode,
          inline.parentNode, () => {}, 0, 'all'))
      }
    }

    return {
      restrict: 'AE',
      link: (scope, element, attrs) => {
        // load templates TODO: find better way than loading them manually
        var templates = [
          'templates/crosshair.html',
          'templates/planeSensor-X.html',
          'templates/planeSensor-Y.html'
        ]
        var promises = templates.map(value => $http.get(value, {cache: $templateCache}))

        $q.all(promises)
          .then(results => {
            // Normally this would be enough, but we want to make sure that the
            // cached value is only the template and not the whole response object
            // with status code, header, etc
            results.forEach(value => {
              $templateCache.put(value.config.url, value.data)
            })

            return Project.getInlines()
          })
          .then(({data: inlinesFromServer}) => {
            const div = $(document.createElement('div')).css(styles.item)
            const div2 = $(document.createElement('div')).css(styles.item)

            element.append(div)
            element.append(div2)

            scope.$watch(attrs.content, content => {
              div.html(content)

              const x3dNode = div.children().get(0)
              const inlines = flatten(div.get(0).querySelectorAll('inline'))

              const sidebar = (

                  <TreeView data={x3dNode} />
                  <InlineList inlines={inlinesFromServer} />

              )

              const watchedAttributes = [
                'diffuseColor',
                'orientation',
                'position',
                'render',
                'rotation',
                'scale',
                'translation'
              ]

              const rerender = _.throttle(React.render, 100)
              const x3dObserver = new window.MutationObserver(mutations => {
                mutations.forEach(mutation => {
                  if (mutation.type === 'attributes') {
                    if (['style', 'class', 'width', 'height'].some(name => name === mutation.attributeName)) {

                    } else if (watchedAttributes.some(eq(mutation.attributeName))) {
                      rerender(sidebar, div2.get(0))
                    } else {
                      console.log(mutation.attributeName)
                    }
                  } else if (mutation.type === 'childList') {
                    flatten(mutation.addedNodes)
                      .filter(node => node.nodeName.toLowerCase() === 'transform')
                      .forEach(transform => {
                        let inline = transform.children[0]

                        if (inline && inline.nodeName.toLowerCase() === 'inline') {
                          let x3dNode = $(inline).firstParent('x3d').get(0)

                          setUpInline(inline, x3dNode)
                        }
                      })

                    rerender(sidebar, div2.get(0))
                  }
                })
              })

              x3dObserver.observe(div.find('scene').get(0), {
                attributes: true,
                childList: true,
                subtree: true
              })

              // take care of the inlines already in the scene

              // rewrite all urls
              inlines.forEach(inline => {
                const url = inline.getAttribute('url')
                const project = $routeParams.project

                inline.setAttribute('url', `projects/${project}/src/${url}`)
              })

              // kick x3dom to init the runtime
              x3dom.reload()

              inlines.forEach(inline => setUpInline(inline, x3dNode))

              React.render(sidebar, div2.get(0))
              X3domUtils.setUp(div)
            })
          })
      }
    }
  })
<commit_msg>re-add crosshairs<commit_after>'use strict'

window.angular.module('scegratooApp')
  .directive('sgtX3d', function SgtX3d ($window, X3domUtils, $http, $q, $templateCache, React, TreeView, _, R, InlineList, Project, Moveable, moveables, $routeParams, x3dom, x3dQuery) {
    const {
      flatten,
      eq
    } = R
    const {
      $,
      Rx
    } = $window

    const styles = {
      item: {
        padding: '5px',
        flex: '1'
      }
    }

    let colorCache
    let crossHairs

    const start = function (event) {
      const inline = $(event.hitObject).lastParent('inline')
      colorCache = inline.color()
      inline.color('yellow')
      inline.addClass('mousedown')

      // add crossHairs
      inline.get(0).parentNode.appendChild(crossHairs)

      Rx.Observable
        .fromEvent(document, 'mouseup')
        .take(1)
        .forEach(function stop (event) {
          inline.color(colorCache)
          inline.removeClass('mousedown')

          // remove the crosshair
          if (crossHairs.parentNode) {
            crossHairs.parentNode.removeChild(crossHairs)
          }
        })
    }

    const setUpInline = (inline, x3dNode) => {
      // add eventlisteners to make it selectable
      inline.addEventListener('mousedown', start)

      // keep a weak reference from the tranlation to the moveable around to
      // remove the event handlers the moveable registers on the translation
      // again if the translation is removed
      if (!moveables.has(inline)) {
        moveables.set(inline.parentNode, new Moveable(x3dNode,
          inline.parentNode, () => {}, 0, 'all'))
      }
    }

    return {
      restrict: 'AE',
      link: (scope, element, attrs) => {
        // load templates TODO: find better way than loading them manually
        var templates = [
          'templates/crosshair.html',
          'templates/planeSensor-X.html',
          'templates/planeSensor-Y.html'
        ]
        var promises = templates.map(value => $http.get(value, {cache: $templateCache}))

        $q.all(promises)
          .then(results => {
            // Normally this would be enough, but we want to make sure that the
            // cached value is only the template and not the whole response object
            // with status code, header, etc
            results.forEach(value => {
              $templateCache.put(value.config.url, value.data)
            })

            return Project.getInlines()
          })
          .then(({data: inlinesFromServer}) => {
            const div = $(document.createElement('div')).css(styles.item)
            const div2 = $(document.createElement('div')).css(styles.item)

            element.append(div)
            element.append(div2)

            crossHairs = window.angular.element($templateCache.get('templates/crosshair.html')).get(0)

            scope.$watch(attrs.content, content => {
              div.html(content)

              const x3dNode = div.children().get(0)
              const inlines = flatten(div.get(0).querySelectorAll('inline'))

              const sidebar = (

                  <TreeView data={x3dNode} />
                  <InlineList inlines={inlinesFromServer} />

              )

              const watchedAttributes = [
                'diffuseColor',
                'orientation',
                'position',
                'render',
                'rotation',
                'scale',
                'translation'
              ]

              const rerender = _.throttle(React.render, 100)
              const x3dObserver = new window.MutationObserver(mutations => {
                mutations.forEach(mutation => {
                  if (mutation.type === 'attributes') {
                    if (['style', 'class', 'width', 'height'].some(name => name === mutation.attributeName)) {

                    } else if (watchedAttributes.some(eq(mutation.attributeName))) {
                      rerender(sidebar, div2.get(0))
                    } else {
                      console.log(mutation.attributeName)
                    }
                  } else if (mutation.type === 'childList') {
                    flatten(mutation.addedNodes)
                      .filter(node => node.nodeName.toLowerCase() === 'transform')
                      .forEach(transform => {
                        let inline = transform.children[0]

                        if (inline && inline.nodeName.toLowerCase() === 'inline') {
                          let x3dNode = $(inline).firstParent('x3d').get(0)

                          setUpInline(inline, x3dNode)
                        }
                      })

                    rerender(sidebar, div2.get(0))
                  }
                })
              })

              x3dObserver.observe(div.find('scene').get(0), {
                attributes: true,
                childList: true,
                subtree: true
              })

              // take care of the inlines already in the scene

              // rewrite all urls
              inlines.forEach(inline => {
                const url = inline.getAttribute('url')
                const project = $routeParams.project

                inline.setAttribute('url', `projects/${project}/src/${url}`)
              })

              // kick x3dom to init the runtime
              x3dom.reload()

              inlines.forEach(inline => setUpInline(inline, x3dNode))

              React.render(sidebar, div2.get(0))
              X3domUtils.setUp(div)
            })
          })
      }
    }
  })
<|endoftext|>


<commit_before>/*******************************
        Release Settings
*******************************/

// release settings
module.exports = {

  // path to components for repos
  source     : './dist/components/',

  // modified asset paths for component repos
  paths: {
    source : '../themes/default/assets/',
    output : 'assets/'
  },

  templates: {
    bower    : './tasks/config/admin/templates/bower.json',
    composer : './tasks/config/admin/templates/composer.json',
    package  : './tasks/config/admin/templates/package.json',
    meteor   : {
      css       : './tasks/config/admin/templates/css-package.js',
      component : './tasks/config/admin/templates/component-package.js',
      less      : './tasks/config/admin/templates/less-package.js',
    },
    readme : './tasks/config/admin/templates/README.md',
    notes  : './RELEASE-NOTES.md'
  },

  org         : 'Semantic-Org',
  repo        : 'Semantic-UI',

  // files created for package managers
  files: {
    composer : 'composer.json',
    config   : 'semantic.json',
    npm      : 'package.json',
    meteor   : 'package.js'
  },

  // root name for distribution repos
  distRepoRoot      : 'Semantic-UI-',

  // root name for single component repos
  componentRepoRoot : 'UI-',

  // root name for package managers
  packageRoot          : 'semantic-ui-',

  // root path to repos
  outputRoot  : '../repos/',

  homepage    : 'http://www.semantic-ui.com',

  // distributions that get separate repos
  distributions: [
    'LESS',
    'CSS'
  ],

  // components that get separate repositories for bower/npm
  components : [
    'accordion',
    'ad',
    'api',
    'breadcrumb',
    'button',
    'card',
    'checkbox',
    'comment',
    'dimmer',
    'divider',
    'dropdown',
    'embed',
    'feed',
    'flag',
    'form',
    'grid',
    'header',
    'icon',
    'image',
    'input',
    'item',
    'label',
    'list',
    'loader',
    'menu',
    'message',
    'modal',
    'nag',
    'popup',
    'progress',
    'rail',
    'rating',
    'reset',
    'reveal',
    'search',
    'segment',
    'shape',
    'sidebar',
    'site',
    'statistic',
    'step',
    'sticky',
    'tab',
    'table',
    'transition',
    'visibility'
  ]
};
<commit_msg>Add container<commit_after>/*******************************
        Release Settings
*******************************/

// release settings
module.exports = {

  // path to components for repos
  source     : './dist/components/',

  // modified asset paths for component repos
  paths: {
    source : '../themes/default/assets/',
    output : 'assets/'
  },

  templates: {
    bower    : './tasks/config/admin/templates/bower.json',
    composer : './tasks/config/admin/templates/composer.json',
    package  : './tasks/config/admin/templates/package.json',
    meteor   : {
      css       : './tasks/config/admin/templates/css-package.js',
      component : './tasks/config/admin/templates/component-package.js',
      less      : './tasks/config/admin/templates/less-package.js',
    },
    readme : './tasks/config/admin/templates/README.md',
    notes  : './RELEASE-NOTES.md'
  },

  org         : 'Semantic-Org',
  repo        : 'Semantic-UI',

  // files created for package managers
  files: {
    composer : 'composer.json',
    config   : 'semantic.json',
    npm      : 'package.json',
    meteor   : 'package.js'
  },

  // root name for distribution repos
  distRepoRoot      : 'Semantic-UI-',

  // root name for single component repos
  componentRepoRoot : 'UI-',

  // root name for package managers
  packageRoot          : 'semantic-ui-',

  // root path to repos
  outputRoot  : '../repos/',

  homepage    : 'http://www.semantic-ui.com',

  // distributions that get separate repos
  distributions: [
    'LESS',
    'CSS'
  ],

  // components that get separate repositories for bower/npm
  components : [
    'accordion',
    'ad',
    'api',
    'breadcrumb',
    'button',
    'card',
    'checkbox',
    'comment',
    'container',
    'dimmer',
    'divider',
    'dropdown',
    'embed',
    'feed',
    'flag',
    'form',
    'grid',
    'header',
    'icon',
    'image',
    'input',
    'item',
    'label',
    'list',
    'loader',
    'menu',
    'message',
    'modal',
    'nag',
    'popup',
    'progress',
    'rail',
    'rating',
    'reset',
    'reveal',
    'search',
    'segment',
    'shape',
    'sidebar',
    'site',
    'statistic',
    'step',
    'sticky',
    'tab',
    'table',
    'transition',
    'visibility'
  ]
};
<|endoftext|>


<commit_before>import Operator from '../Operator';
import {isChangeSet} from '../ChangeSet';
import {constant, extend, isFunction} from 'vega-util';

var SKIP = {skip: true};

/**
 * Perform operator updates in response to events. Applies an
 * update function to compute a new operator value. If the update function
 * returns a {@link ChangeSet}, the operator will be pulsed with those tuple
 * changes. Otherwise, the operator value will be updated to the return value.
 * @param {EventStream|Operator} source - The event source to react to.
 *   This argument can be either an EventStream or an Operator.
 * @param {Operator|function(object):Operator} target - The operator to update.
 *   This argument can either be an Operator instance or (if the source
 *   argument is an EventStream), a function that accepts an event object as
 *   input and returns an Operator to target.
 * @param {function(Parameters,Event): *} [update] - Optional update function
 *   to compute the new operator value, or a literal value to set. Update
 *   functions expect to receive a parameter object and event as arguments.
 *   This function can either return a new operator value or (if the source
 *   argument is an EventStream) a {@link ChangeSet} instance to pulse
 *   the target operator with tuple changes.
 * @param {object} [params] - The update function parameters.
 * @param {object} [options] - Additional options hash. If not overridden,
 *   updated operators will be skipped by default.
 * @param {boolean} [options.skip] - If true, the operator will
 *  be skipped: it will not be evaluated, but its dependents will be.
 * @param {boolean} [options.force] - If true, the operator will
 *   be re-evaluated even if its value has not changed.
 * @return {Dataflow}
 */
export default function(source, target, update, params, options) {
  var fn = source instanceof Operator ? onOperator : onStream;
  fn(this, source, target, update, params, options);
  return this;
}

function onStream(df, stream, target, update, params, options) {
  var opt = extend({}, options, SKIP), func, op;

  if (!isFunction(target)) target = constant(target);

  if (update === undefined) {
    func = function(e) {
      df.touch(target(e));
    };
  } else if (isFunction(update)) {
    op = new Operator(null, update, params, false);
    func = function(e) {
      var v, t = target(e);
      op.evaluate(e);
      isChangeSet(v = op.value) ? df.pulse(t, v, options) : df.update(t, v, opt);
    };
  } else {
    func = function(e) {
      df.update(target(e), update, opt);
    };
  }

  stream.apply(func);
}

function onOperator(df, source, target, update, params, options) {
  var func, op;

  if (update === undefined) {
    op = target;
  } else {
    func = isFunction(update) ? update : constant(update);
    update = !target ? func : function(_, pulse) {
      var value = func(_, pulse);
      return target.skip()
        ? value
        : (target.skip(true).value = value);
    };

    op = new Operator(null, update, params, false);
    op.modified(options && options.force);
    op.rank = 0;

    if (target) {
      op.skip(true); // skip first invocation
      op.value = target.value;
      op.targets().add(target);
    }
  }

  source.targets().add(op);
}
<commit_msg>Fix operator evaluation skip bug upon signal update.<commit_after>import Operator from '../Operator';
import {isChangeSet} from '../ChangeSet';
import {constant, extend, isFunction} from 'vega-util';

var SKIP = {skip: true};

/**
 * Perform operator updates in response to events. Applies an
 * update function to compute a new operator value. If the update function
 * returns a {@link ChangeSet}, the operator will be pulsed with those tuple
 * changes. Otherwise, the operator value will be updated to the return value.
 * @param {EventStream|Operator} source - The event source to react to.
 *   This argument can be either an EventStream or an Operator.
 * @param {Operator|function(object):Operator} target - The operator to update.
 *   This argument can either be an Operator instance or (if the source
 *   argument is an EventStream), a function that accepts an event object as
 *   input and returns an Operator to target.
 * @param {function(Parameters,Event): *} [update] - Optional update function
 *   to compute the new operator value, or a literal value to set. Update
 *   functions expect to receive a parameter object and event as arguments.
 *   This function can either return a new operator value or (if the source
 *   argument is an EventStream) a {@link ChangeSet} instance to pulse
 *   the target operator with tuple changes.
 * @param {object} [params] - The update function parameters.
 * @param {object} [options] - Additional options hash. If not overridden,
 *   updated operators will be skipped by default.
 * @param {boolean} [options.skip] - If true, the operator will
 *  be skipped: it will not be evaluated, but its dependents will be.
 * @param {boolean} [options.force] - If true, the operator will
 *   be re-evaluated even if its value has not changed.
 * @return {Dataflow}
 */
export default function(source, target, update, params, options) {
  var fn = source instanceof Operator ? onOperator : onStream;
  fn(this, source, target, update, params, options);
  return this;
}

function onStream(df, stream, target, update, params, options) {
  var opt = extend({}, options, SKIP), func, op;

  if (!isFunction(target)) target = constant(target);

  if (update === undefined) {
    func = function(e) {
      df.touch(target(e));
    };
  } else if (isFunction(update)) {
    op = new Operator(null, update, params, false);
    func = function(e) {
      var v, t = target(e);
      op.evaluate(e);
      isChangeSet(v = op.value) ? df.pulse(t, v, options) : df.update(t, v, opt);
    };
  } else {
    func = function(e) {
      df.update(target(e), update, opt);
    };
  }

  stream.apply(func);
}

function onOperator(df, source, target, update, params, options) {
  var func, op;

  if (update === undefined) {
    op = target;
  } else {
    func = isFunction(update) ? update : constant(update);
    update = !target ? func : function(_, pulse) {
      var value = func(_, pulse);
      if (!target.skip()) {
        target.skip(value !== this.value).value = value;
      }
      return value;
    };

    op = new Operator(null, update, params, false);
    op.modified(options && options.force);
    op.rank = 0;

    if (target) {
      op.skip(true); // skip first invocation
      op.value = target.value;
      op.targets().add(target);
    }
  }

  source.targets().add(op);
}
<|endoftext|>


<commit_before>var gulp = require('gulp'),
    sass = require('gulp-sass'),
    autoprefixer = require('gulp-autoprefixer'),
    minifycss = require('gulp-minify-css'),
    sourcemaps = require('gulp-sourcemaps'),
    jshint = require('gulp-jshint'),
    stylish = require('jshint-stylish')
    uglify = require('gulp-uglify'),
    imagemin = require('gulp-imagemin'),
    rename = require('gulp-rename'),
    concat = require('gulp-concat'),
    notify = require('gulp-notify'),
    cache = require('gulp-cache'),
    livereload = require('gulp-livereload'),
    express = require('express'),
    del = require('del'),
    stripDebug = require('gulp-strip-debug'),
    browserSync = require('browser-sync'),
    argv = require('yargs').argv,
    gulpif = require('gulp-if'),
    todo = require('gulp-todo'),
    plumber = require('gulp-plumber'),
    ngannotate = require('gulp-ng-annotate'),
    replace = require('gulp-replace');

var options = {liveReload: false};

/**
 * browser-sync task for starting a server. This will open a browser for you. Point multiple browsers / devices to the same url and watch the magic happen.
 * Depends on: watch
 */
gulp.task('browser-sync', ['watch'], function() {

  // Watch any files in dist/*, reload on change
  gulp.watch(['dist/**']).on('change', function(){browserSync.reload({});notify({ message: 'Reload browser' });});

  return browserSync({
      server: {
          baseDir: "./dist"
      },
      ghostMode: {
        clicks: true,
        location: true,
        forms: true,
        scroll: true
      },
      open: "external",
      injectChanges: true, // inject CSS changes (false force a reload)
      browser: ["google chrome"],
      scrollProportionally: true, // Sync viewports to TOP position
      scrollThrottle: 50,
    });
});

/**
 * Build and copy all styles, scripts, images and fonts.
 * Depends on: clean
 */
gulp.task('build', ['clean'], function() {
    gulp.start('styles', 'scripts', 'images', 'copy', 'todo');
});

/**
 * Cleans the `dist` folder and other generated files
 */
gulp.task('clean', function(cb) {
    del(['dist', 'todo.md', 'todo.json'], cb);
});

/**
 * Clears the cache used by gulp-cache
 */
gulp.task('clear-cache', function() {

  // Or, just call this for everything
  cache.clearAll();
});

/**
 * Copies all to dist/
 */
gulp.task('copy', function() {

  // copy all jpg's as they are not handled by the images task
  gulp.src( 'src/img/**/*.jpg')
    .pipe(gulp.dest('dist/img'));

  // copy all fonts
  gulp.src( 'src/fonts/**')
    .pipe(gulp.dest('dist/fonts'));

  // copy all html && json
  gulp.src( ['src/js/app/**/*.html', 'src/js/app/**/*.json'])
    .pipe(gulp.dest('dist/js/app'));

  //copy the error test page
  gulp.src('src/errortest.html')
    .pipe(gulp.dest('dist/'));

  // copy the index.html
   return gulp.src('src/index.html')
    .pipe(gulpif(options.liveReload, replace(/(\<\/body\>)/g, "<script>document.write('<script src=\"http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1\"></' + 'script>')</script>$1")))
    .pipe(gulp.dest('dist/'));
});

/**
 * Default task.
 * Depends on: build
 */
gulp.task('default', ['build']);

/**
 * Task to start a Express server on port 4000.
 */
gulp.task('express', function(){
  var app = express(), port = 4000;
  var url = require('url');
  var proxy = require('proxy-middleware');
  app.use(express.static(__dirname + "/dist"));
  app.use('/api', proxy(url.parse('http://localhost:1337')));
  app.listen(port);
  console.log('started webserver on port ' + port);
});

/**
 * Task to start a Express server on port 4000 and used the live reload functionality.
 * Depends on: express, live-reload
 */
gulp.task('express-lr', ['express', 'live-reload'], function(){});

/**
 * Task to optimize and deploy all images found in folder `src/img/**`. Result is copied to `dist/img`
 */
gulp.task('images', function() {
  return gulp.src('src/img/**/*')
    .pipe(plumber())
    .pipe(cache(imagemin({ optimizationLevel: 5, progressive: true, interlaced: true })))
    .pipe(gulp.dest('dist/img'));
});

/**
 * Start the live reload server. Live reload will be triggered when a file in the `dist` folder changes. This will add a live-reload script to the index.html page, which makes it all happen.
 * Depends on: watch
 */
gulp.task('live-reload', ['watch'], function() {

  options.liveReload = true;
  // first, delete the index.html from the dist folder as we will copy it later
  del(['dist/index.html']);

  // add livereload script to the index.html
  gulp.src(['src/index.html'])
   .pipe(replace(/(\<\/body\>)/g, "<script>document.write('<script src=\"http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1\"></' + 'script>')</script>$1"))
   .pipe(gulp.dest('dist'));

  // Create LiveReload server
  livereload.listen();

  // Watch any files in dist/*, reload on change
  gulp.watch(['dist/**']).on('change', livereload.changed);
});

/**
 * Task to handle and deploy all javascript, application & vendor
 *
 * Depends on: scripts-app, scripts-vendor
 */
gulp.task('scripts', ['scripts-app','scripts-vendor']);

/**
 * Removes the node_modules
 */
gulp.task('remove',['clean'], function(cb){
  del('node_modules', cb);
});

/**
 * Minifies all javascript found in the `src/js/**` folder. All files will be concatenated into `app.js`.  Minified and non-minified versions are copied to the dist folder.
 * This will also generete sourcemaps for the minified version.
 */
gulp.task('scripts-app', [], function() {
  return gulp.src(['src/js/app/huna.js','src/js/app/**/*.js'])
    .pipe(plumber())
    .pipe(sourcemaps.init())
    .pipe(jshint())
    .on('error', notify.onError(function (error) {
      return error.message;
     }))
    .pipe(jshint.reporter(stylish))
    .pipe(concat('app.js'))
    .pipe(gulp.dest('dist/js'))
    .pipe(rename({suffix: '.min'}))
    .pipe(gulpif(!argv.dev, stripDebug()))
    .pipe(ngannotate())
    .pipe(gulpif(!argv.dev, uglify()))
    .on('error', handleError)
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('dist/js'));
});

/**
 * Task to handle all vendor specific javasript. All vendor javascript will be copied to the dist directory. Also a concatinated version will be made, available in \dist\assets\js\vendor\vendor.js
 */
gulp.task('scripts-vendor', function() {
    // script must be included in the right order. First include angular, then angular-route
  return gulp.src(['src/js/vendor/angularjs/1.3.14/angular.min.js','src/js/vendor/angularjs/1.3.14/angular-route.min.js','src/js/vendor/**/*.js'])
    .pipe(gulp.dest('dist/js/vendor'))
    .pipe(concat('vendor.js'))
    .pipe(gulp.dest('dist/js/vendor'));
});

/**
 * Compile Sass into Css and minify it. Minified and non-minified versions are copied to the dist folder.
 * This will also auto prefix vendor specific rules.
 */
gulp.task('styles', function() {
  return gulp.src('src/styles/main.scss')
    .pipe(plumber())
    .pipe(sass({ style: 'expanded' }))
    .on('error', handleError)
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .on('error', handleError)
    .pipe(gulp.dest('dist/css'))
    .pipe(rename({suffix: '.min'}))
    .pipe(minifycss())
    .pipe(gulp.dest('dist/css'));
});

/**
 * Output TODO's & FIXME's in markdown and json file as well
 */
gulp.task('todo', function() {
    gulp.src('src/js/app/**/*.js')
      .pipe(plumber())
      .pipe(todo())
      .pipe(gulp.dest('./')) //output todo.md as markdown
      .pipe(todo.reporter('json', {fileName: 'todo.json'}))
      .pipe(gulp.dest('./')) //output todo.json as json
});

/**
 * Watches changes to template, Sass, javascript and image files. On change this will run the appropriate task, either: copy styles, scripts or images.
 */
gulp.task('watch', function() {

  // watch html files
  gulp.watch('src/**/*.html', ['copy']);

  // Watch .scss files
  gulp.watch('src/styles/**/*.scss', ['styles']);

  // Watch app .js files
  gulp.watch('src/js/app/**/*', ['scripts-app']);

  // Watch vendor .js files
  gulp.watch('src/js/vendor/**/*', ['scripts-vendor']);

  // Watch image files
  gulp.watch('src/img/**/*', ['images']);
});

function handleError (error) {

    //If you want details of the error in the console
    console.log(error.toString());

    this.emit('end');
}<commit_msg>Build separate huna.js library<commit_after>var gulp = require('gulp'),
    sass = require('gulp-sass'),
    autoprefixer = require('gulp-autoprefixer'),
    minifycss = require('gulp-minify-css'),
    sourcemaps = require('gulp-sourcemaps'),
    jshint = require('gulp-jshint'),
    stylish = require('jshint-stylish')
    uglify = require('gulp-uglify'),
    imagemin = require('gulp-imagemin'),
    rename = require('gulp-rename'),
    concat = require('gulp-concat'),
    notify = require('gulp-notify'),
    cache = require('gulp-cache'),
    livereload = require('gulp-livereload'),
    express = require('express'),
    del = require('del'),
    stripDebug = require('gulp-strip-debug'),
    browserSync = require('browser-sync'),
    argv = require('yargs').argv,
    gulpif = require('gulp-if'),
    todo = require('gulp-todo'),
    plumber = require('gulp-plumber'),
    ngannotate = require('gulp-ng-annotate'),
    replace = require('gulp-replace');

var options = {liveReload: false};

/**
 * browser-sync task for starting a server. This will open a browser for you. Point multiple browsers / devices to the same url and watch the magic happen.
 * Depends on: watch
 */
gulp.task('browser-sync', ['watch'], function() {

  // Watch any files in dist/*, reload on change
  gulp.watch(['dist/**']).on('change', function(){browserSync.reload({});notify({ message: 'Reload browser' });});

  return browserSync({
      server: {
          baseDir: "./dist"
      },
      ghostMode: {
        clicks: true,
        location: true,
        forms: true,
        scroll: true
      },
      open: "external",
      injectChanges: true, // inject CSS changes (false force a reload)
      browser: ["google chrome"],
      scrollProportionally: true, // Sync viewports to TOP position
      scrollThrottle: 50,
    });
});

/**
 * Build and copy all styles, scripts, images and fonts.
 * Depends on: clean
 */
gulp.task('build', ['clean'], function() {
    gulp.start('styles', 'scripts', 'images', 'copy', 'todo');
});

/**
 * Cleans the `dist` folder and other generated files
 */
gulp.task('clean', function(cb) {
    del(['dist', 'todo.md', 'todo.json'], cb);
});

/**
 * Clears the cache used by gulp-cache
 */
gulp.task('clear-cache', function() {

  // Or, just call this for everything
  cache.clearAll();
});

/**
 * Copies all to dist/
 */
gulp.task('copy', function() {

  // copy all jpg's as they are not handled by the images task
  gulp.src( 'src/img/**/*.jpg')
    .pipe(gulp.dest('dist/img'));

  // copy all fonts
  gulp.src( 'src/fonts/**')
    .pipe(gulp.dest('dist/fonts'));

  // copy all html && json
  gulp.src( ['src/js/app/**/*.html', 'src/js/app/**/*.json'])
    .pipe(gulp.dest('dist/js/app'));

  //copy the error test page
  gulp.src('src/errortest.html')
    .pipe(gulp.dest('dist/'));

  // copy the index.html
   return gulp.src('src/index.html')
    .pipe(gulpif(options.liveReload, replace(/(\<\/body\>)/g, "<script>document.write('<script src=\"http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1\"></' + 'script>')</script>$1")))
    .pipe(gulp.dest('dist/'));
});

/**
 * Default task.
 * Depends on: build
 */
gulp.task('default', ['build']);

/**
 * Task to start a Express server on port 4000.
 */
gulp.task('express', function(){
  var app = express(), port = 4000;
  var url = require('url');
  var proxy = require('proxy-middleware');
  app.use(express.static(__dirname + "/dist"));
  app.use('/api', proxy(url.parse('http://localhost:1337')));
  app.listen(port);
  console.log('started webserver on port ' + port);
});

/**
 * Task to start a Express server on port 4000 and used the live reload functionality.
 * Depends on: express, live-reload
 */
gulp.task('express-lr', ['express', 'live-reload'], function(){});

/**
 * Task to optimize and deploy all images found in folder `src/img/**`. Result is copied to `dist/img`
 */
gulp.task('images', function() {
  return gulp.src('src/img/**/*')
    .pipe(plumber())
    .pipe(cache(imagemin({ optimizationLevel: 5, progressive: true, interlaced: true })))
    .pipe(gulp.dest('dist/img'));
});

/**
 * Start the live reload server. Live reload will be triggered when a file in the `dist` folder changes. This will add a live-reload script to the index.html page, which makes it all happen.
 * Depends on: watch
 */
gulp.task('live-reload', ['watch'], function() {

  options.liveReload = true;
  // first, delete the index.html from the dist folder as we will copy it later
  del(['dist/index.html']);

  // add livereload script to the index.html
  gulp.src(['src/index.html'])
   .pipe(replace(/(\<\/body\>)/g, "<script>document.write('<script src=\"http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1\"></' + 'script>')</script>$1"))
   .pipe(gulp.dest('dist'));

  // Create LiveReload server
  livereload.listen();

  // Watch any files in dist/*, reload on change
  gulp.watch(['dist/**']).on('change', livereload.changed);
});

/**
 * Task to handle and deploy all javascript, application & vendor
 *
 * Depends on: scripts-app, scripts-vendor
 */
gulp.task('scripts', ['scripts-app','scripts-vendor']);

/**
 * Removes the node_modules
 */
gulp.task('remove',['clean'], function(cb){
  del('node_modules', cb);
});

/**
 * Minifies all javascript found in the `src/js/**` folder. All files will be concatenated into `app.js`.  Minified and non-minified versions are copied to the dist folder.
 * This will also generete sourcemaps for the minified version.
 */
gulp.task('scripts-app', [], function() {
  // build separate huna lib
  gulp.src('src/js/app/huna.js')
    // dev code
    .pipe(plumber())
    .pipe(sourcemaps.init())
    .on('error', notify.onError(function (error) {
      return error.message;
     }))
    .pipe(gulp.dest('dist/js'))
    // production code
    .pipe(rename({suffix: '.min'}))
    .pipe(stripDebug())
    .pipe(uglify())
    .on('error', handleError)
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('dist/js'));

  return gulp.src(['src/js/app/huna.js','src/js/app/**/*.js'])
    .pipe(plumber())
    .pipe(sourcemaps.init())
    .pipe(jshint())
    .on('error', notify.onError(function (error) {
      return error.message;
     }))
    .pipe(jshint.reporter(stylish))
    .pipe(concat('app.js'))
    .pipe(gulp.dest('dist/js'))
    .pipe(rename({suffix: '.min'}))
    .pipe(gulpif(!argv.dev, stripDebug()))
    .pipe(ngannotate())
    .pipe(gulpif(!argv.dev, uglify()))
    .on('error', handleError)
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('dist/js'));
});

/**
 * Task to handle all vendor specific javasript. All vendor javascript will be copied to the dist directory. Also a concatinated version will be made, available in \dist\assets\js\vendor\vendor.js
 */
gulp.task('scripts-vendor', function() {
    // script must be included in the right order. First include angular, then angular-route
  return gulp.src(['src/js/vendor/angularjs/1.3.14/angular.min.js','src/js/vendor/angularjs/1.3.14/angular-route.min.js','src/js/vendor/**/*.js'])
    .pipe(gulp.dest('dist/js/vendor'))
    .pipe(concat('vendor.js'))
    .pipe(gulp.dest('dist/js/vendor'));
});

/**
 * Compile Sass into Css and minify it. Minified and non-minified versions are copied to the dist folder.
 * This will also auto prefix vendor specific rules.
 */
gulp.task('styles', function() {
  return gulp.src('src/styles/main.scss')
    .pipe(plumber())
    .pipe(sass({ style: 'expanded' }))
    .on('error', handleError)
    .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
    .on('error', handleError)
    .pipe(gulp.dest('dist/css'))
    .pipe(rename({suffix: '.min'}))
    .pipe(minifycss())
    .pipe(gulp.dest('dist/css'));
});

/**
 * Output TODO's & FIXME's in markdown and json file as well
 */
gulp.task('todo', function() {
    gulp.src('src/js/app/**/*.js')
      .pipe(plumber())
      .pipe(todo())
      .pipe(gulp.dest('./')) //output todo.md as markdown
      .pipe(todo.reporter('json', {fileName: 'todo.json'}))
      .pipe(gulp.dest('./')) //output todo.json as json
});

/**
 * Watches changes to template, Sass, javascript and image files. On change this will run the appropriate task, either: copy styles, scripts or images.
 */
gulp.task('watch', function() {

  // watch html files
  gulp.watch('src/**/*.html', ['copy']);

  // Watch .scss files
  gulp.watch('src/styles/**/*.scss', ['styles']);

  // Watch app .js files
  gulp.watch('src/js/app/**/*', ['scripts-app']);

  // Watch vendor .js files
  gulp.watch('src/js/vendor/**/*', ['scripts-vendor']);

  // Watch image files
  gulp.watch('src/img/**/*', ['images']);
});

function handleError (error) {

    //If you want details of the error in the console
    console.log(error.toString());

    this.emit('end');
}<|endoftext|>


<commit_before>'use strict';

var NVG = class {
	constructor(input) {
		this.document = 'nvg';
		this.items = [];
		this.version = '2.0.2'
		if (typeof input == 'string'){
			//do stuff with input object
			try {
				input = JSON.parse(input);
				for (var key in input){
					this[key] = input[key];
				}
			}catch (e) {
				//So parse as JSON failed, try to parse it as xml
				this.parseXML(input);
			}
		}
	}
	getItems(){ //returns all items
		return this.items;
	}
	parseXML(xml){
		//parse XML string to JSON
		function tagAttributes(nodes, current){
			for (var i = 0; i < nodes.length; i++){
				var node = nodes[i];
				var nodeName = node.nodeName.split(':');
				if(nodeName[0] == 'dc' || nodeName[0] == 'dcterms'){
					nodeName = nodeName[0];
				}else{
					nodeName = nodeName[1];
				}
				if(node.nodeType == 1 && nodeName){
					nodeName = nodeName.toLowerCase();
					switch (nodeName) {
						case 'begin':
							current[nodeName] = node.textContent;
							break;
						case 'end':
							current[nodeName] = node.textContent;
							break;
						case 'dc':
						case 'dcterms':
							current[node.nodeName] = node.textContent;
							break;
						case 'content':
							current[nodeName] = node.textContent;
							break;
						case 'exclude':
							if (!current.hasOwnProperty(nodeName)){
								current.exclusion = [];
							}
							tagAttributes(node.childNodes, current.exclusion);
							break;
						case 'extendeddata':
							if (!current.hasOwnProperty(nodeName)){
								current[nodeName] = {};
								current[nodeName].simpledata = [];
							}
							nodeAttibutes(node, current[nodeName]);
							parseSubNodes(node.childNodes, current[nodeName]);
							break;
						case 'extension':
							console.log('TODO tagAttributes: '  + nodeName);
							// TODO How to handle extended data
							current[nodeName] = [];//this is for node
							break;
						case 'metadata':
							current[nodeName] = {};
							tagAttributes(node.childNodes, current[nodeName]);
							break;
						case 'textinfo':
							current[nodeName] = node.textContent;
							break;
						case 'timespan':
							current[nodeName] = {};
							tagAttributes(node.childNodes, current[nodeName]);
							break;
						case 'timestamp':
							current[nodeName] = node.textContent;
							break;
						case 'simplefield':
							if (!current.hasOwnProperty(nodeName)){
								current[nodeName] = [];
							}
							var field = {};
							nodeAttibutes(node, field);
							current[nodeName].push(field);
							break;
						case 'arcband-ring':
						case 'circular-ring':
						case 'elliptic-ring':
						case 'linear-ring':
						case 'rect-ring':
							var exclude = {};
							exclude.drawable = nodeName.replace('-','');
							nodeAttibutes(node, exclude);
							current.push(exclude);
							break;
						default:
							//Debug logging, remove later
							if(['arc','arcband','arrow','circle','composite','content-item','corridor','ellipse','g','multipoint','orbit','point','polygon','polyline','rect','text'].lastIndexOf(nodeName) == -1){
								console.log('TODO tagAttributes default: ' + nodeName);
							}
					}
				}
			}
		}
		function nodeAttibutes(node, current){
			Array.prototype.slice.call(node.attributes).forEach(function(attr) {
				if (attr.name == 'modifiers' || attr.name == 'style') {
					current[attr.name] = {};
					var attr_list = attr.value.trim().split(';');
					for (var j = 0; j < attr_list.length; j++){
						if(attr_list[j]){
							var s = attr_list[j].split(':');
							if(s[0] && s[1])current[attr.name][s[0].trim()] = isNaN(Number(s[1].trim()))?s[1].trim():Number(s[1].trim());
						}
					}
				return;
				}
				if (attr.name == 'points') {
					current[attr.name] = [];
					var attr_list = attr.value.trim().split(' ');
					for (var j = 0; j < attr_list.length; j++){
						if(attr_list[j]){
							var s = attr_list[j].split(',');
							if(s[0] && s[1])current[attr.name].push([Number(s[0]),Number(s[1])]);
						}
					}
				return;
				}
				current[attr.name] = isNaN(Number(attr.value))?attr.value:Number(attr.value);
			});
		}
		function parseSubNodes(nodes, current){
			for (var i = 0; i < nodes.length; i++){
				var node = nodes[i];
				if(node.nodeType == 1){
					var nodeName = node.nodeName.split(':')[1] || node.nodeName;
					nodeName = nodeName.toLowerCase();
					var item = {};
					if (['extendeddata','extension','metadata','schema','section','simpledata','simplefield'].lastIndexOf(nodeName) != -1){

						switch (nodeName) {
							case 'extendeddata':
								current[nodeName] = item;
								nodeAttibutes(node, item);
								tagAttributes(node.childNodes, item);
								break;
							case 'extension':
								console.log('TODO parsesubnodes: ' + nodeName)
								// TODO How to handle extended data
								current[nodeName] = [];//this is for root level
								break;
							case 'metadata':
								console.log('TODO parsesubnodes: ' + nodeName)
								// TODO How to handle metadata data
								current[nodeName] = item;
								break;
							case 'schema':
								if (!current.hasOwnProperty(nodeName)){
									current[nodeName] = [];
								}
								current[nodeName].push(item);
								nodeAttibutes(node, item);
								tagAttributes(node.childNodes, item);
								break;
							case 'section':
								if (!current.hasOwnProperty('simpledatasection')){
									current.simpledatasection = [];
								}
								current.simpledatasection.push(item);
								nodeAttibutes(node, item);
								item.simpledata = [];
								parseSubNodes(node.childNodes, item);
								break;
							case 'simpledata':
								nodeAttibutes(node, item);
								item.value = node.textContent;
								current.simpledata.push(item);
								tagAttributes(node.childNodes, item);
								break;
							case 'simplefield':
								current[nodeName] = item;
								nodeAttibutes(node, item);
								tagAttributes(node.childNodes, item);
								break;
							default:
								console.log('TODO parsesubnodes default: ' + nodeName)
						}
					}else{ //This is all drawables
						nodeAttibutes(node, item);
						item.drawable = nodeName;

						if(node.childNodes.length){
							tagAttributes(node.childNodes, item);
						}
						if(item.drawable == 'g' || item.drawable == 'composite'){
							item.items = [];
							parseSubNodes(node.childNodes, item);
						}
						current.items.push(item);
					}
				}
			}
		}

		var xml = (new DOMParser()).parseFromString(xml , "text/xml");
		if(xml.firstChild.nodeName == 'nvg' || xml.firstChild.nodeName.split(':')[1] == 'nvg'){//check that we actually are parsing NVG but ignore namespace
			this.version = xml.firstChild.getAttribute('version');
			this.items = [];
			var nodes = xml.firstChild.childNodes;
			parseSubNodes(nodes, this);
		}
	}
	toGeoJSON(){
		//parse this to GeoJSON
		function distBearing(point, dist, bearing){
			var angularDist = dist/6371e3;
			var bearing = bearing * (Math.PI/180);
			var lng = point[0] * (Math.PI/180);
			var lat = point[1] * (Math.PI/180);
			//console.log(lng + ' ' + lat)
			var lat2 = Math.asin(Math.sin(lat)*Math.cos(angularDist)+Math.cos(lat)*Math.sin(angularDist)*Math.cos(bearing));
			var lng2 = (lng+Math.atan2(Math.sin(bearing)*Math.sin(angularDist)*Math.cos(lat),Math.cos(angularDist)-Math.sin(lat)*Math.sin(lat2)));
			lat2 = lat2/(Math.PI/180);
			lng2 = ((lng2/(Math.PI/180))+540)%360-180;
			return [lng2,lat2];
		}

		function items2features(items, geometrycollection){
			var features = [];
			for (var i = 0; i < items.length; i++){
				var item = items[i];
				var feature = { "type": "Feature", "properties" : {}};
				for (var key in item){
					if(key == 'uri'){
						feature.id = item.uri;
					}else{
						feature.properties[key] = item[key];
					}
				}
				switch (item.drawable) {
					case 'arc':
						feature.geometry = {"type": "LineString"};
						feature.geometry.coordinates = [];
						var startangle = item.startangle;
						var endangle = item.endangle;
						if(startangle > endangle) endangle += 360;
						for (var j = startangle; j <= endangle; j+=2){
							var radius = item.rx * item.ry / Math.sqrt(Math.pow(item.ry * Math.cos(j * (Math.PI/180)),2) + Math.pow(item.rx * Math.sin(j * (Math.PI/180)),2));
							feature.geometry.coordinates.push(distBearing([item.cx,item.cy], radius, j));
						}
						break;
					case 'arcband':
						feature.geometry = {"type": "Polygon"};
						feature.geometry.coordinates = [[]];
						var startangle = item.startangle;
						var endangle = item.endangle;
						if(startangle > endangle) endangle += 360;
						for (var j = startangle; j <= endangle; j+=2){
							var radius = item.rx * item.ry / Math.sqrt(Math.pow(item.ry * Math.cos(j * (Math.PI/180)),2) + Math.pow(item.rx * Math.sin(j * (Math.PI/180)),2));
							feature.geometry.coordinates[0].push(distBearing([item.cx,item.cy], item.minr, j));
						}
						for (var j = endangle; j >= startangle; j-=2){
							var radius = item.rx * item.ry / Math.sqrt(Math.pow(item.ry * Math.cos(j * (Math.PI/180)),2) + Math.pow(item.rx * Math.sin(j * (Math.PI/180)),2));
							feature.geometry.coordinates[0].push(distBearing([item.cx,item.cy], item.maxr, j));
						}
						feature.geometry.coordinates[0].push(distBearing([item.cx,item.cy], item.minr, startangle));
						break;
					case 'arrow':
						//feature.geometry = {"type": "Polygon"};
						// TODO: create polygon
						break;
					case 'circle':
						feature.geometry = {"type": "Polygon"};
						feature.geometry.coordinates = [[]];
						for (var j = 360; j >= 0; j-=5){
							feature.geometry.coordinates[0].push(distBearing([item.cx,item.cy], item.r, j));
						}
						break;
					case 'composite':
						//Flatten composites at the moment
						var subfeatures = items2features(item.items, true);
						for (key in subfeatures){
							subfeatures[key].properties.parent = {};//feature.properties;
							if(item.uri)subfeatures[key].properties.parent.uri = item.uri;
							features.push(subfeatures[key]);
						}
						break;
					case 'corridor':
						//feature.geometry = {"type": "Polygon"};
						// TODO: create polygon
						break;
					case 'ellipse':
						feature.geometry = {"type": "Polygon"};
						feature.geometry.coordinates = [[]];
						for (var j = 360; j >= 0; j-=2){
							var radius = item.rx * item.ry / Math.sqrt(Math.pow(item.ry * Math.cos(j * (Math.PI/180)),2) + Math.pow(item.rx * Math.sin(j * (Math.PI/180)),2));
							feature.geometry.coordinates[0].push(distBearing([item.cx,item.cy], radius, j+item.rotation));
						}
						break;
					case 'g':
						//Flatten groups
						var subfeatures = items2features(item.items, true);
						for (key in subfeatures){
							subfeatures[key].properties.parent = {};//feature.properties;
							if(item.uri)subfeatures[key].properties.parent.uri = item.uri;
							features.push(subfeatures[key]);
						}
						break;
					case 'multipoint':
						feature.geometry = {"type": "MultiPoint"};
						feature.geometry.coordinates = item.points;
						delete feature.properties.points;
						break;
					case 'orbit':
						//feature.geometry = {"type": "Polygon"};
						// TODO: create polygon
						break;
					case 'point':
						feature.geometry = {"type": "Point"};
						feature.geometry.coordinates = [item.x, item.y];
						break;
					case 'polygon':
						feature.geometry = {"type": "Polygon"};
						feature.geometry.coordinates = [item.points];
						delete feature.properties.points;
						break;
					case 'polyline':
						feature.geometry = {"type": "LineString"};
						feature.geometry.coordinates = item.points;
						delete feature.properties.points;
						break;
					case 'rect':
						//feature.geometry = {"type": "Polygon"};
						// TODO: create polygon
						break;
					case 'text':
						feature.geometry = {"type": "Point"};
						feature.geometry.coordinates = [item.x, item.y];
						break;
					default:
						console.log('TODO parse item default: ' + item.drawable)
				}
				if(feature.geometry){
					features.push(feature);
				}
			}
			return features;
		}

		var geoJSON = {};
		geoJSON.type = 'FeatureCollection';
		for (var key in this){
			if(key == 'items'){
				geoJSON.features = items2features(this.items, false);
			}else{
				geoJSON[key] = this[key];
			}
		}

		return geoJSON;
	}
	toXML(){
		//parse this to NVG XML
	}

};
<commit_msg>an arc can be rotated<commit_after>'use strict';

var NVG = class {
	constructor(input) {
		this.document = 'nvg';
		this.items = [];
		this.version = '2.0.2'
		if (typeof input == 'string'){
			//do stuff with input object
			try {
				input = JSON.parse(input);
				for (var key in input){
					this[key] = input[key];
				}
			}catch (e) {
				//So parse as JSON failed, try to parse it as xml
				this.parseXML(input);
			}
		}
	}
	getItems(){ //returns all items
		return this.items;
	}
	parseXML(xml){
		//parse XML string to JSON
		function tagAttributes(nodes, current){
			for (var i = 0; i < nodes.length; i++){
				var node = nodes[i];
				var nodeName = node.nodeName.split(':');
				if(nodeName[0] == 'dc' || nodeName[0] == 'dcterms'){
					nodeName = nodeName[0];
				}else{
					nodeName = nodeName[1];
				}
				if(node.nodeType == 1 && nodeName){
					nodeName = nodeName.toLowerCase();
					switch (nodeName) {
						case 'begin':
							current[nodeName] = node.textContent;
							break;
						case 'end':
							current[nodeName] = node.textContent;
							break;
						case 'dc':
						case 'dcterms':
							current[node.nodeName] = node.textContent;
							break;
						case 'content':
							current[nodeName] = node.textContent;
							break;
						case 'exclude':
							if (!current.hasOwnProperty(nodeName)){
								current.exclusion = [];
							}
							tagAttributes(node.childNodes, current.exclusion);
							break;
						case 'extendeddata':
							if (!current.hasOwnProperty(nodeName)){
								current[nodeName] = {};
								current[nodeName].simpledata = [];
							}
							nodeAttibutes(node, current[nodeName]);
							parseSubNodes(node.childNodes, current[nodeName]);
							break;
						case 'extension':
							console.log('TODO tagAttributes: '  + nodeName);
							// TODO How to handle extended data
							current[nodeName] = [];//this is for node
							break;
						case 'metadata':
							current[nodeName] = {};
							tagAttributes(node.childNodes, current[nodeName]);
							break;
						case 'textinfo':
							current[nodeName] = node.textContent;
							break;
						case 'timespan':
							current[nodeName] = {};
							tagAttributes(node.childNodes, current[nodeName]);
							break;
						case 'timestamp':
							current[nodeName] = node.textContent;
							break;
						case 'simplefield':
							if (!current.hasOwnProperty(nodeName)){
								current[nodeName] = [];
							}
							var field = {};
							nodeAttibutes(node, field);
							current[nodeName].push(field);
							break;
						case 'arcband-ring':
						case 'circular-ring':
						case 'elliptic-ring':
						case 'linear-ring':
						case 'rect-ring':
							var exclude = {};
							exclude.drawable = nodeName.replace('-','');
							nodeAttibutes(node, exclude);
							current.push(exclude);
							break;
						default:
							//Debug logging, remove later
							if(['arc','arcband','arrow','circle','composite','content-item','corridor','ellipse','g','multipoint','orbit','point','polygon','polyline','rect','text'].lastIndexOf(nodeName) == -1){
								console.log('TODO tagAttributes default: ' + nodeName);
							}
					}
				}
			}
		}
		function nodeAttibutes(node, current){
			Array.prototype.slice.call(node.attributes).forEach(function(attr) {
				if (attr.name == 'modifiers' || attr.name == 'style') {
					current[attr.name] = {};
					var attr_list = attr.value.trim().split(';');
					for (var j = 0; j < attr_list.length; j++){
						if(attr_list[j]){
							var s = attr_list[j].split(':');
							if(s[0] && s[1])current[attr.name][s[0].trim()] = isNaN(Number(s[1].trim()))?s[1].trim():Number(s[1].trim());
						}
					}
				return;
				}
				if (attr.name == 'points') {
					current[attr.name] = [];
					var attr_list = attr.value.trim().split(' ');
					for (var j = 0; j < attr_list.length; j++){
						if(attr_list[j]){
							var s = attr_list[j].split(',');
							if(s[0] && s[1])current[attr.name].push([Number(s[0]),Number(s[1])]);
						}
					}
				return;
				}
				current[attr.name] = isNaN(Number(attr.value))?attr.value:Number(attr.value);
			});
		}
		function parseSubNodes(nodes, current){
			for (var i = 0; i < nodes.length; i++){
				var node = nodes[i];
				if(node.nodeType == 1){
					var nodeName = node.nodeName.split(':')[1] || node.nodeName;
					nodeName = nodeName.toLowerCase();
					var item = {};
					if (['extendeddata','extension','metadata','schema','section','simpledata','simplefield'].lastIndexOf(nodeName) != -1){

						switch (nodeName) {
							case 'extendeddata':
								current[nodeName] = item;
								nodeAttibutes(node, item);
								tagAttributes(node.childNodes, item);
								break;
							case 'extension':
								console.log('TODO parsesubnodes: ' + nodeName)
								// TODO How to handle extended data
								current[nodeName] = [];//this is for root level
								break;
							case 'metadata':
								console.log('TODO parsesubnodes: ' + nodeName)
								// TODO How to handle metadata data
								current[nodeName] = item;
								break;
							case 'schema':
								if (!current.hasOwnProperty(nodeName)){
									current[nodeName] = [];
								}
								current[nodeName].push(item);
								nodeAttibutes(node, item);
								tagAttributes(node.childNodes, item);
								break;
							case 'section':
								if (!current.hasOwnProperty('simpledatasection')){
									current.simpledatasection = [];
								}
								current.simpledatasection.push(item);
								nodeAttibutes(node, item);
								item.simpledata = [];
								parseSubNodes(node.childNodes, item);
								break;
							case 'simpledata':
								nodeAttibutes(node, item);
								item.value = node.textContent;
								current.simpledata.push(item);
								tagAttributes(node.childNodes, item);
								break;
							case 'simplefield':
								current[nodeName] = item;
								nodeAttibutes(node, item);
								tagAttributes(node.childNodes, item);
								break;
							default:
								console.log('TODO parsesubnodes default: ' + nodeName)
						}
					}else{ //This is all drawables
						nodeAttibutes(node, item);
						item.drawable = nodeName;

						if(node.childNodes.length){
							tagAttributes(node.childNodes, item);
						}
						if(item.drawable == 'g' || item.drawable == 'composite'){
							item.items = [];
							parseSubNodes(node.childNodes, item);
						}
						current.items.push(item);
					}
				}
			}
		}

		var xml = (new DOMParser()).parseFromString(xml , "text/xml");
		if(xml.firstChild.nodeName == 'nvg' || xml.firstChild.nodeName.split(':')[1] == 'nvg'){//check that we actually are parsing NVG but ignore namespace
			this.version = xml.firstChild.getAttribute('version');
			this.items = [];
			var nodes = xml.firstChild.childNodes;
			parseSubNodes(nodes, this);
		}
	}
	toGeoJSON(){
		//parse this to GeoJSON
		function distBearing(point, dist, bearing){
			var angularDist = dist/6371e3;
			var bearing = bearing * (Math.PI/180);
			var lng = point[0] * (Math.PI/180);
			var lat = point[1] * (Math.PI/180);
			//console.log(lng + ' ' + lat)
			var lat2 = Math.asin(Math.sin(lat)*Math.cos(angularDist)+Math.cos(lat)*Math.sin(angularDist)*Math.cos(bearing));
			var lng2 = (lng+Math.atan2(Math.sin(bearing)*Math.sin(angularDist)*Math.cos(lat),Math.cos(angularDist)-Math.sin(lat)*Math.sin(lat2)));
			lat2 = lat2/(Math.PI/180);
			lng2 = ((lng2/(Math.PI/180))+540)%360-180;
			return [lng2,lat2];
		}

		function items2features(items, geometrycollection){
			var features = [];
			for (var i = 0; i < items.length; i++){
				var item = items[i];
				var feature = { "type": "Feature", "properties" : {}};
				for (var key in item){
					if(key == 'uri'){
						feature.id = item.uri;
					}else{
						feature.properties[key] = item[key];
					}
				}
				switch (item.drawable) {
					case 'arc':
						feature.geometry = {"type": "LineString"};
						feature.geometry.coordinates = [];
						var startangle = item.startangle;
						var endangle = item.endangle;
						if(startangle > endangle) endangle += 360;
						for (var j = startangle; j <= endangle; j+=2){
							var radius = item.rx * item.ry / Math.sqrt(Math.pow(item.ry * Math.cos(j * (Math.PI/180)),2) + Math.pow(item.rx * Math.sin(j * (Math.PI/180)),2));
							feature.geometry.coordinates.push(distBearing([item.cx,item.cy], radius, item.rotation?j+item.rotation:j));
						}
						break;
					case 'arcband':
						feature.geometry = {"type": "Polygon"};
						feature.geometry.coordinates = [[]];
						var startangle = item.startangle;
						var endangle = item.endangle;
						if(startangle > endangle) endangle += 360;
						for (var j = startangle; j <= endangle; j+=2){
							var radius = item.rx * item.ry / Math.sqrt(Math.pow(item.ry * Math.cos(j * (Math.PI/180)),2) + Math.pow(item.rx * Math.sin(j * (Math.PI/180)),2));
							feature.geometry.coordinates[0].push(distBearing([item.cx,item.cy], item.minr, j));
						}
						for (var j = endangle; j >= startangle; j-=2){
							var radius = item.rx * item.ry / Math.sqrt(Math.pow(item.ry * Math.cos(j * (Math.PI/180)),2) + Math.pow(item.rx * Math.sin(j * (Math.PI/180)),2));
							feature.geometry.coordinates[0].push(distBearing([item.cx,item.cy], item.maxr, j));
						}
						feature.geometry.coordinates[0].push(distBearing([item.cx,item.cy], item.minr, startangle));
						break;
					case 'arrow':
						//feature.geometry = {"type": "Polygon"};
						// TODO: create polygon
						break;
					case 'circle':
						feature.geometry = {"type": "Polygon"};
						feature.geometry.coordinates = [[]];
						for (var j = 360; j >= 0; j-=5){
							feature.geometry.coordinates[0].push(distBearing([item.cx,item.cy], item.r, j));
						}
						break;
					case 'composite':
						//Flatten composites at the moment
						var subfeatures = items2features(item.items, true);
						for (key in subfeatures){
							subfeatures[key].properties.parent = {};//feature.properties;
							if(item.uri)subfeatures[key].properties.parent.uri = item.uri;
							features.push(subfeatures[key]);
						}
						break;
					case 'corridor':
						//feature.geometry = {"type": "Polygon"};
						// TODO: create polygon
						break;
					case 'ellipse':
						feature.geometry = {"type": "Polygon"};
						feature.geometry.coordinates = [[]];
						for (var j = 360; j >= 0; j-=2){
							var radius = item.rx * item.ry / Math.sqrt(Math.pow(item.ry * Math.cos(j * (Math.PI/180)),2) + Math.pow(item.rx * Math.sin(j * (Math.PI/180)),2));
							feature.geometry.coordinates[0].push(distBearing([item.cx,item.cy], radius, j+item.rotation));
						}
						break;
					case 'g':
						//Flatten groups
						var subfeatures = items2features(item.items, true);
						for (key in subfeatures){
							subfeatures[key].properties.parent = {};//feature.properties;
							if(item.uri)subfeatures[key].properties.parent.uri = item.uri;
							features.push(subfeatures[key]);
						}
						break;
					case 'multipoint':
						feature.geometry = {"type": "MultiPoint"};
						feature.geometry.coordinates = item.points;
						delete feature.properties.points;
						break;
					case 'orbit':
						//feature.geometry = {"type": "Polygon"};
						// TODO: create polygon
						break;
					case 'point':
						feature.geometry = {"type": "Point"};
						feature.geometry.coordinates = [item.x, item.y];
						break;
					case 'polygon':
						feature.geometry = {"type": "Polygon"};
						feature.geometry.coordinates = [item.points];
						delete feature.properties.points;
						break;
					case 'polyline':
						feature.geometry = {"type": "LineString"};
						feature.geometry.coordinates = item.points;
						delete feature.properties.points;
						break;
					case 'rect':
						//feature.geometry = {"type": "Polygon"};
						// TODO: create polygon
						break;
					case 'text':
						feature.geometry = {"type": "Point"};
						feature.geometry.coordinates = [item.x, item.y];
						break;
					default:
						console.log('TODO parse item default: ' + item.drawable)
				}
				if(feature.geometry){
					features.push(feature);
				}
			}
			return features;
		}

		var geoJSON = {};
		geoJSON.type = 'FeatureCollection';
		for (var key in this){
			if(key == 'items'){
				geoJSON.features = items2features(this.items, false);
			}else{
				geoJSON[key] = this[key];
			}
		}

		return geoJSON;
	}
	toXML(){
		//parse this to NVG XML
	}

};
<|endoftext|>