<commit_before><link rel="import" href="../bower_components/the-graph/the-graph-thumb/the-graph-thumb.html">
<link rel="import" href="noflo-account.html">
<link rel="import" href="noflo-new-project.html">
<link rel="import" href="noflo-new-runtime.html">
<polymer-element name="noflo-main" attributes="open localProjects graphs components userRuntimes">
  <template>
    <noflo-account id="mainaccount" clientid="9d963a3d-8b6f-42fe-bb36-6fccecd039af"></noflo-account>
    <section id="projects">
      <h2>Projects
          <template if="{{ projectList === 'local' }}">
          <small>{{ localProjects.length }} projects</small>
          </template>
          <template if="{{ projectList === 'github' }}">
          <small>{{ remoteProjects.length }} projects</small>
          </template>
          <template if="{{ projectList === 'github' }}">
          <button on-click="{{ fetchGithub }}"><i class="fa fa-refresh"></i></button>
          </template>
      </h2>
      <ul class="tabs">
        <template if="{{ projectList === 'local' }}">
          <li class="selected">On device</li>
          <template if="{{ githubToken }}">
          <li on-click="{{ openGithub }}">GitHub</li>
          </template>
        </template>
        <template if="{{ projectList === 'github' }}">
        <li on-click="{{ openLocal }}">On device</li>
          <li class="selected">GitHub</li>
        </template>
      </ul>
      <ul class="projects">
        <template if="{{ projectList === 'local' }}">
        <li class="add">
          <h2>New project</h2>
          <button on-click="{{ newProject }}">Create</button>
        </li>
        <template repeat="{{ project in localProjects }}">
          <li on-click="{{ openProject }}" data-id="{{ project.id }}">
            <the-graph-thumb graph="{{ project.mainGraph }}" width="200" height="120"></the-graph-thumb>
            <h2>{{ project.name }}</h2>
            <p>{{ project.graphs.length }}&nbsp;graphs, {{ project.components.length }}&nbsp;components</p>
          </li>
        </template>
        </template>
        <template if="{{ projectList === 'github' }}">
        <template repeat="{{ project in remoteProjects }}">
          <li class="remote">
            <h2>{{ project.name }}</h2>
            <p>{{ project.description }}</p>
            <button on-click="{{ downloadProject }}" data-name="{{ project.name }}" data-repo="{{ project.repo }}"><i class="fa fa-cloud-download"></i></button>
          </li>
        </template>
        </template>
      </ul>
    </section>
    <template if="{{ gridToken }}">
    <section id="runtimes">
      <h2>
        Runtimes <small>{{ userRuntimes.length }} runtimes</small>
        <button on-click="{{ fetchFlowhub }}"><i class="fa fa-refresh"></i></button>
      </h2>
      <ul class="projects">
        <li class="add">
          <h2>New runtime</h2>
          <button on-click="{{ newRuntime }}">Register</button>
        </li>
        <template repeat="{{ userRuntimes }}">
        <li on-click="{{ openRuntime }}" data-id="{{ id }}" class="runtime {{ type }}">
          <h2>{{ label }}</h2>
          <p>{{ address }}</p>
        </li>
        </template>
      </ul>
    </section>
    </template>
  </template>
  <script>
    Polymer('noflo-main', {
      open: true,
      localProjects: [],
      remoteProjects: [],
      userRuntimes: [],
      githubToken: '',
      gridToken: '',
      user: null,
      projectList: 'local',
      languages: [
        'HTML',
        'JavaScript',
        'CoffeeScript',
        'C++'
      ],
      enteredView: function () {
        this.openChanged();
        this.$.mainaccount.addEventListener('githubtoken', function (event) {
          this.githubToken = event.detail;
        }.bind(this));
        this.$.mainaccount.addEventListener('gridtoken', function (event) {
          this.gridToken = event.detail;
        }.bind(this));
        this.$.mainaccount.addEventListener('user', function (event) {
          this.user = event.detail;
          this.fire('user', this.user);
        }.bind(this));
        this.$.mainaccount.addEventListener('login', function (event) {
          this.fetchFlowhub();
        }.bind(this));
        this.registry = require('flowhub-registry');
      },
      token: function () {
        this.fire('githubtoken', this.githubToken);
      },
      openChanged: function () {
        if (String(this.open) === 'true') {
          this.style.height = '100%';
          return;
        }
        this.style.height = '0px';
      },
      openLocal: function (event) {
        event.preventDefault();
        this.projectList = 'local';
      },
      openGithub: function (event) {
        event.preventDefault();
        if (!this.githubToken) {
          return;
        }
        event.preventDefault();
        this.projectList = 'github';
        if (this.remoteProjects.length === 0) {
          this.fetchGithub();
        }
      },
      downloadProject: function (event, details, sender) {
        event.preventDefault();
        if (!this.githubToken) {
          return;
        }
        this.fire('download', {
          project: {
            id: sender.getAttribute('data-name'),
            name: sender.getAttribute('data-name'),
            repo: sender.getAttribute('data-repo'),
            // TODO: Figure out platform from repo contents
            type: 'html',
            graphs: [],
            components: []
          },
          token: this.githubToken
        });
      },
      fetchGithub: function (event) {
        if (event) {
          event.preventDefault();
        }
        if (!this.githubToken) {
          return;
        }
        this.remoteProjects = [];
        var req = new XMLHttpRequest();
        req.onreadystatechange = function () {
          if (req.readyState !== 4) {
            return;
          }
          if (req.status !== 200) {
            return;
          }
          var repos = JSON.parse(req.responseText);
          repos.forEach(function (repo) {
            if (!repo.permissions.push) {
              return;
            }
            for (var i = 0; i < this.localProjects.length; i++) {
              if (this.localProjects[i].repo === repo.full_name) {
                return;
              }
            }
            if (this.languages.indexOf(repo.language) === -1) {
              return;
            }
            this.remoteProjects.push({
              name: repo.name,
              description: repo.description,
              repo: repo.full_name,
              language: repo.language
            });
          }.bind(this));
        }.bind(this);
        req.open('GET', 'https://api.github.com/user/repos?type=public&access_token=' + encodeURIComponent(this.githubToken), true);
        req.setRequestHeader('Accept', 'application/vnd.github.beta+json');
        req.send(null);
      },
      runtimes: function (runtime) {
        if (runtime.protocol === 'iframe') {
          return;
        }
        this.userRuntimes.push(runtime);
        this.fire('runtimes', this.userRuntimes);
      },
      fetchFlowhub: function (event) {
        if (event) {
          event.preventDefault();
        }
        if (!this.gridToken) {
          return;
        }
        this.registry.list(this.gridToken, function (err, runtimes) {
          if (err) {
            return;
          }
          var found = [];
          runtimes.forEach(function (runtime) {
            var inStore = null;
            if (runtime.protocol === 'iframe') {
              return;
            }
            this.userRuntimes.forEach(function (storedRuntime) {
              if (storedRuntime.id === runtime.runtime.id) {
                inStore = storedRuntime;
                found.push(inStore.id);
              }
            });
            if (!inStore) {
              this.fire('runtime', runtime.runtime);
              this.userRuntimes.push(runtime.runtime);
              return;
            }

            // Update existing
            var changed = false;
            Object.keys(runtime.runtime).forEach(function (attr) {
              if (inStore[attr] === runtime.runtime[attr]) {
                return;
              }
              inStore[attr] = runtime.runtime[attr];
              changed = true;
            });
            if (changed) {
              this.fire('runtime', inStore);
            }
          }.bind(this));

          // Remove deleted runtimes
          this.userRuntimes.forEach(function (runtime, index) {
            if (found.indexOf(runtime.id) !== -1) {
              return;
            }
            if (runtime.protocol === 'iframe') {
              return;
            }
            this.fire('removedruntime', runtime);
            this.userRuntimes.splice(index, 1);
          }.bind(this));
        }.bind(this));
      },
      preparedefaults: function () {
        // Every NoFlo UI instance should have at least the local IFRAME runtime available
        var hasIframeRuntime = false;
        var runtime = new this.registry.Runtime({
          label: 'Local NoFlo HTML5 environment',
          id: require('uuid')(),
          protocol: 'iframe',
          address: 'preview/iframe.html',
          type: 'noflo-browser'
        });
        this.userRuntimes.push(runtime.runtime);
        this.fire('runtimes', this.userRuntimes);
      },
      createdproject: function (project) {
        window.location.hash = '#project/' + project.id + '/' + project.main;
      },
      openProject: function (event, details, sender) {
        event.preventDefault();
        var project = null;
        this.localProjects.forEach(function (p) {
          if (p.id === sender.getAttribute('data-id')) {
            project = p;
          }
        });
        if (!project) {
          return;
        }
        window.location.hash = '#project/' + project.id + '/' + project.main;
      },
      openExample: function (event, details, sender) {
        event.preventDefault();
        window.location.hash = '#example/' + sender.getAttribute('data-id');
      },
      newProject: function (event) {
        event.preventDefault();
        var dialog = document.createElement('noflo-new-project');
        dialog.runtimes = this.userRuntimes;
        this.parentNode.parentNode.appendChild(dialog);
        dialog.addEventListener('new', function (event) {
          this.fire('newproject', event.detail);
        }.bind(this));
      },
      newRuntime: function (event) {
        event.preventDefault();
        var dialog = document.createElement('noflo-new-runtime');
        dialog.user = this.user;
        this.parentNode.parentNode.appendChild(dialog);
      },
      projects: function (project) {
        this.localProjects.push(project);
      },
      graphs: function (graph) {
        if (!graph.properties.project) {
          // TODO: Create a project for old-style sketches
          return;
        }
        this.localProjects.forEach(function (project) {
          if (!project.graphs) {
            project.graphs = [];
          }
          if (!project.components) {
            project.components = [];
          }
          if (graph.properties.project !== project.id) {
            return;
          }
          if (!project.main) {
            project.mainGraph = graph;
            project.main = graph.properties.id;
          }
          if (graph.properties.id === project.main) {
            project.mainGraph = graph;
          }
          for (var i = 0; i < project.graphs.length; i++) {
            if (project.graphs[i].properties.id === graph.properties.id) {
              // We already have this graph in the project
              return;
            }
          }
          project.graphs.push(graph);
        });
      },
      components: function (component) {
        this.localProjects.forEach(function (project) {
          if (!project.components) {
            project.components = [];
          }
          if (component.project !== project.id) {
            return;
          }
          for (var i = 0; i < project.components.length; i++) {
            if (project.components[i].id === component.id) {
              project.components[i] = component;
              return;
            }
          }
          project.components.push(component);
        });
      },
      route: function (routeData) {
        if (routeData.project) {
          this.localProjects.forEach(function (project) {
            if (project.id === routeData.project) {
              this.fire('project', project);

              if (routeData.route === 'graph') {
                while (routeData.graphs.length) {
                  var graphId = routeData.graphs.shift();
                  for (var i = 0; i < project.graphs.length; i++) {
                    if (project.graphs[i].properties.id === graphId) {
                      this.fire('graph', project.graphs[i]);
                    }
                  }
                }
              }
              if (routeData.route === 'component') {
                project.components.forEach(function (component) {
                  if (component.id === routeData.component) {
                    this.fire('component', component);
                  }
                }.bind(this));
              }
            }
          }.bind(this));
          return;
        }
      }
    });
  </script>
</polymer-element>
<commit_msg>Also clear the tokens when user logs out<commit_after><link rel="import" href="../bower_components/the-graph/the-graph-thumb/the-graph-thumb.html">
<link rel="import" href="noflo-account.html">
<link rel="import" href="noflo-new-project.html">
<link rel="import" href="noflo-new-runtime.html">
<polymer-element name="noflo-main" attributes="open localProjects graphs components userRuntimes">
  <template>
    <noflo-account id="mainaccount" clientid="9d963a3d-8b6f-42fe-bb36-6fccecd039af"></noflo-account>
    <section id="projects">
      <h2>Projects
          <template if="{{ projectList === 'local' }}">
          <small>{{ localProjects.length }} projects</small>
          </template>
          <template if="{{ projectList === 'github' }}">
          <small>{{ remoteProjects.length }} projects</small>
          </template>
          <template if="{{ projectList === 'github' }}">
          <button on-click="{{ fetchGithub }}"><i class="fa fa-refresh"></i></button>
          </template>
      </h2>
      <ul class="tabs">
        <template if="{{ projectList === 'local' }}">
          <li class="selected">On device</li>
          <template if="{{ githubToken }}">
          <li on-click="{{ openGithub }}">GitHub</li>
          </template>
        </template>
        <template if="{{ projectList === 'github' }}">
        <li on-click="{{ openLocal }}">On device</li>
          <li class="selected">GitHub</li>
        </template>
      </ul>
      <ul class="projects">
        <template if="{{ projectList === 'local' }}">
        <li class="add">
          <h2>New project</h2>
          <button on-click="{{ newProject }}">Create</button>
        </li>
        <template repeat="{{ project in localProjects }}">
          <li on-click="{{ openProject }}" data-id="{{ project.id }}">
            <the-graph-thumb graph="{{ project.mainGraph }}" width="200" height="120"></the-graph-thumb>
            <h2>{{ project.name }}</h2>
            <p>{{ project.graphs.length }}&nbsp;graphs, {{ project.components.length }}&nbsp;components</p>
          </li>
        </template>
        </template>
        <template if="{{ projectList === 'github' }}">
        <template repeat="{{ project in remoteProjects }}">
          <li class="remote">
            <h2>{{ project.name }}</h2>
            <p>{{ project.description }}</p>
            <button on-click="{{ downloadProject }}" data-name="{{ project.name }}" data-repo="{{ project.repo }}"><i class="fa fa-cloud-download"></i></button>
          </li>
        </template>
        </template>
      </ul>
    </section>
    <template if="{{ gridToken }}">
    <section id="runtimes">
      <h2>
        Runtimes <small>{{ userRuntimes.length }} runtimes</small>
        <button on-click="{{ fetchFlowhub }}"><i class="fa fa-refresh"></i></button>
      </h2>
      <ul class="projects">
        <li class="add">
          <h2>New runtime</h2>
          <button on-click="{{ newRuntime }}">Register</button>
        </li>
        <template repeat="{{ userRuntimes }}">
        <li on-click="{{ openRuntime }}" data-id="{{ id }}" class="runtime {{ type }}">
          <h2>{{ label }}</h2>
          <p>{{ address }}</p>
        </li>
        </template>
      </ul>
    </section>
    </template>
  </template>
  <script>
    Polymer('noflo-main', {
      open: true,
      localProjects: [],
      remoteProjects: [],
      userRuntimes: [],
      githubToken: '',
      gridToken: '',
      user: null,
      projectList: 'local',
      languages: [
        'HTML',
        'JavaScript',
        'CoffeeScript',
        'C++'
      ],
      enteredView: function () {
        this.openChanged();
        this.$.mainaccount.addEventListener('githubtoken', function (event) {
          if (typeof event.detail !== 'string') {
            this.githubToken = '';
            return;
          }
          this.githubToken = event.detail;
        }.bind(this));
        this.$.mainaccount.addEventListener('gridtoken', function (event) {
          if (typeof event.detail !== 'string') {
            this.gridToken = '';
            return;
          }
          this.gridToken = event.detail;
        }.bind(this));
        this.$.mainaccount.addEventListener('user', function (event) {
          this.user = event.detail;
          this.fire('user', this.user);
        }.bind(this));
        this.$.mainaccount.addEventListener('login', function (event) {
          this.fetchFlowhub();
        }.bind(this));
        this.registry = require('flowhub-registry');
      },
      token: function () {
        this.fire('githubtoken', this.githubToken);
      },
      openChanged: function () {
        if (String(this.open) === 'true') {
          this.style.height = '100%';
          return;
        }
        this.style.height = '0px';
      },
      openLocal: function (event) {
        event.preventDefault();
        this.projectList = 'local';
      },
      openGithub: function (event) {
        event.preventDefault();
        if (!this.githubToken) {
          return;
        }
        event.preventDefault();
        this.projectList = 'github';
        if (this.remoteProjects.length === 0) {
          this.fetchGithub();
        }
      },
      downloadProject: function (event, details, sender) {
        event.preventDefault();
        if (!this.githubToken) {
          return;
        }
        this.fire('download', {
          project: {
            id: sender.getAttribute('data-name'),
            name: sender.getAttribute('data-name'),
            repo: sender.getAttribute('data-repo'),
            // TODO: Figure out platform from repo contents
            type: 'html',
            graphs: [],
            components: []
          },
          token: this.githubToken
        });
      },
      fetchGithub: function (event) {
        if (event) {
          event.preventDefault();
        }
        if (!this.githubToken) {
          return;
        }
        this.remoteProjects = [];
        var req = new XMLHttpRequest();
        req.onreadystatechange = function () {
          if (req.readyState !== 4) {
            return;
          }
          if (req.status !== 200) {
            return;
          }
          var repos = JSON.parse(req.responseText);
          repos.forEach(function (repo) {
            if (!repo.permissions.push) {
              return;
            }
            for (var i = 0; i < this.localProjects.length; i++) {
              if (this.localProjects[i].repo === repo.full_name) {
                return;
              }
            }
            if (this.languages.indexOf(repo.language) === -1) {
              return;
            }
            this.remoteProjects.push({
              name: repo.name,
              description: repo.description,
              repo: repo.full_name,
              language: repo.language
            });
          }.bind(this));
        }.bind(this);
        req.open('GET', 'https://api.github.com/user/repos?type=public&access_token=' + encodeURIComponent(this.githubToken), true);
        req.setRequestHeader('Accept', 'application/vnd.github.beta+json');
        req.send(null);
      },
      runtimes: function (runtime) {
        if (runtime.protocol === 'iframe') {
          return;
        }
        this.userRuntimes.push(runtime);
        this.fire('runtimes', this.userRuntimes);
      },
      fetchFlowhub: function (event) {
        if (event) {
          event.preventDefault();
        }
        if (!this.gridToken) {
          return;
        }
        this.registry.list(this.gridToken, function (err, runtimes) {
          if (err) {
            return;
          }
          var found = [];
          runtimes.forEach(function (runtime) {
            var inStore = null;
            if (runtime.protocol === 'iframe') {
              return;
            }
            this.userRuntimes.forEach(function (storedRuntime) {
              if (storedRuntime.id === runtime.runtime.id) {
                inStore = storedRuntime;
                found.push(inStore.id);
              }
            });
            if (!inStore) {
              this.fire('runtime', runtime.runtime);
              this.userRuntimes.push(runtime.runtime);
              return;
            }

            // Update existing
            var changed = false;
            Object.keys(runtime.runtime).forEach(function (attr) {
              if (inStore[attr] === runtime.runtime[attr]) {
                return;
              }
              inStore[attr] = runtime.runtime[attr];
              changed = true;
            });
            if (changed) {
              this.fire('runtime', inStore);
            }
          }.bind(this));

          // Remove deleted runtimes
          this.userRuntimes.forEach(function (runtime, index) {
            if (found.indexOf(runtime.id) !== -1) {
              return;
            }
            if (runtime.protocol === 'iframe') {
              return;
            }
            this.fire('removedruntime', runtime);
            this.userRuntimes.splice(index, 1);
          }.bind(this));
        }.bind(this));
      },
      preparedefaults: function () {
        // Every NoFlo UI instance should have at least the local IFRAME runtime available
        var hasIframeRuntime = false;
        var runtime = new this.registry.Runtime({
          label: 'Local NoFlo HTML5 environment',
          id: require('uuid')(),
          protocol: 'iframe',
          address: 'preview/iframe.html',
          type: 'noflo-browser'
        });
        this.userRuntimes.push(runtime.runtime);
        this.fire('runtimes', this.userRuntimes);
      },
      createdproject: function (project) {
        window.location.hash = '#project/' + project.id + '/' + project.main;
      },
      openProject: function (event, details, sender) {
        event.preventDefault();
        var project = null;
        this.localProjects.forEach(function (p) {
          if (p.id === sender.getAttribute('data-id')) {
            project = p;
          }
        });
        if (!project) {
          return;
        }
        window.location.hash = '#project/' + project.id + '/' + project.main;
      },
      openExample: function (event, details, sender) {
        event.preventDefault();
        window.location.hash = '#example/' + sender.getAttribute('data-id');
      },
      newProject: function (event) {
        event.preventDefault();
        var dialog = document.createElement('noflo-new-project');
        dialog.runtimes = this.userRuntimes;
        this.parentNode.parentNode.appendChild(dialog);
        dialog.addEventListener('new', function (event) {
          this.fire('newproject', event.detail);
        }.bind(this));
      },
      newRuntime: function (event) {
        event.preventDefault();
        var dialog = document.createElement('noflo-new-runtime');
        dialog.user = this.user;
        this.parentNode.parentNode.appendChild(dialog);
      },
      projects: function (project) {
        this.localProjects.push(project);
      },
      graphs: function (graph) {
        if (!graph.properties.project) {
          // TODO: Create a project for old-style sketches
          return;
        }
        this.localProjects.forEach(function (project) {
          if (!project.graphs) {
            project.graphs = [];
          }
          if (!project.components) {
            project.components = [];
          }
          if (graph.properties.project !== project.id) {
            return;
          }
          if (!project.main) {
            project.mainGraph = graph;
            project.main = graph.properties.id;
          }
          if (graph.properties.id === project.main) {
            project.mainGraph = graph;
          }
          for (var i = 0; i < project.graphs.length; i++) {
            if (project.graphs[i].properties.id === graph.properties.id) {
              // We already have this graph in the project
              return;
            }
          }
          project.graphs.push(graph);
        });
      },
      components: function (component) {
        this.localProjects.forEach(function (project) {
          if (!project.components) {
            project.components = [];
          }
          if (component.project !== project.id) {
            return;
          }
          for (var i = 0; i < project.components.length; i++) {
            if (project.components[i].id === component.id) {
              project.components[i] = component;
              return;
            }
          }
          project.components.push(component);
        });
      },
      route: function (routeData) {
        if (routeData.project) {
          this.localProjects.forEach(function (project) {
            if (project.id === routeData.project) {
              this.fire('project', project);

              if (routeData.route === 'graph') {
                while (routeData.graphs.length) {
                  var graphId = routeData.graphs.shift();
                  for (var i = 0; i < project.graphs.length; i++) {
                    if (project.graphs[i].properties.id === graphId) {
                      this.fire('graph', project.graphs[i]);
                    }
                  }
                }
              }
              if (routeData.route === 'component') {
                project.components.forEach(function (component) {
                  if (component.id === routeData.component) {
                    this.fire('component', component);
                  }
                }.bind(this));
              }
            }
          }.bind(this));
          return;
        }
      }
    });
  </script>
</polymer-element>

{'fraction_non_alphanumeric': 0.15101153406787782, 'fraction_numerical': 0.0038324059402292075, 'mean_word_length': 1.6768593410929178, 'pattern_counts': {'":': 0, '<': 227, '<?xml version=': 0, '>': 219, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2325570', 'n_tokens_mistral': 7449, 'n_tokens_neox': 7311, 'n_words': 1443}