<commit_before>import _ from 'underscore';
import React from 'react/addons';
import Router from 'react-router';
import RetinaImage from 'react-retina-image';
import ImageCard from './ImageCard.react';
import Promise from 'bluebird';
import metrics from '../utils/MetricsUtil';
import classNames from 'classnames';
import repositoryActions from '../actions/RepositoryActions';
import repositoryStore from '../stores/RepositoryStore';
import accountStore from '../stores/AccountStore';
import accountActions from '../actions/AccountActions';
import imageActions from '../actions/ImageActions';
import imageStore from '../stores/ImageStore';

var _searchPromise = null;

module.exports = React.createClass({
  mixins: [Router.Navigation, Router.State],
  getInitialState: function () {
    return {
      query: '',
      loading: repositoryStore.loading(),
      repos: repositoryStore.all(),
      images: imageStore.all(),
      imagesErr: imageStore.error,
      username: accountStore.getState().username,
      verified: accountStore.getState().verified,
      accountLoading: accountStore.getState().loading,
      error: repositoryStore.getState().error,
      currentPage: repositoryStore.getState().currentPage,
      totalPage: repositoryStore.getState().totalPage,
      previousPage: repositoryStore.getState().previousPage,
      nextPage: repositoryStore.getState().nextPage
    };
  },
  componentDidMount: function () {
    this.refs.searchInput.getDOMNode().focus();
    repositoryStore.listen(this.update);
    accountStore.listen(this.updateAccount);
    imageStore.listen(this.updateImage);
    repositoryActions.search();
  },
  componentWillUnmount: function () {
    if (_searchPromise) {
      _searchPromise.cancel();
    }

    repositoryStore.unlisten(this.update);
    accountStore.unlisten(this.updateAccount);
  },
  update: function () {
    this.setState({
      loading: repositoryStore.loading(),
      repos: repositoryStore.all(),
      currentPage: repositoryStore.getState().currentPage,
      totalPage: repositoryStore.getState().totalPage,
      previousPage: repositoryStore.getState().previousPage,
      nextPage: repositoryStore.getState().nextPage,
      error: repositoryStore.getState().error
    });
  },
  updateImage: function (imgStore) {
    this.setState({
      images: imgStore.images,
      error: imgStore.error
    });
  },
  updateAccount: function () {
    this.setState({
      username: accountStore.getState().username,
      verified: accountStore.getState().verified,
      accountLoading: accountStore.getState().loading
    });
  },
  search: function (query, page = 1) {
    if (_searchPromise) {
      _searchPromise.cancel();
      _searchPromise = null;
    }
    let previousPage, nextPage, totalPage = null;
    // If query remains, retain pagination
    if (this.state.query === query) {
      previousPage = (page - 1 < 1) ? 1 : page - 1;
      nextPage = (page + 1 > this.state.totalPage) ? this.state.totalPage : page + 1;
      totalPage = this.state.totalPage;
    }
    this.setState({
      query: query,
      loading: true,
      currentPage: page,
      previousPage: previousPage,
      nextPage: nextPage,
      totalPage: totalPage,
      error: null
    });

    _searchPromise = Promise.delay(200).cancellable().then(() => {
      metrics.track('Searched for Images');
      _searchPromise = null;
      repositoryActions.search(query, page);
    }).catch(Promise.CancellationError, () => {});
  },
  handleChange: function (e) {
    let query = e.target.value;
    if (query === this.state.query) {
      return;
    }
    this.search(query);
  },
  handlePage: function (page) {
    let query = this.state.query;
    this.search(query, page);
  },
  handleFilter: function (filter) {

    this.setState({error: null});

    // If we're clicking on the filter again - refresh
    if (filter === 'userrepos' && this.getQuery().filter === 'userrepos') {
      repositoryActions.repos();
    }

    if (filter === 'userimages' && this.getQuery().filter === 'userimages') {
      imageActions.all();
    }

    if (filter === 'recommended' && this.getQuery().filter === 'recommended') {
      repositoryActions.recommended();
    }

    this.transitionTo('search', {}, {filter: filter});

    metrics.track('Filtered Results', {
      filter: filter
    });
  },
  handleCheckVerification: function () {
    accountActions.verify();
    metrics.track('Verified Account', {
      from: 'search'
    });
  },
  render: function () {
    let filter = this.getQuery().filter || 'all';
    let repos = _.values(this.state.repos)
        .filter(repo => {
          if (repo.is_recommended || repo.is_user_repo) {
            return repo.name.toLowerCase().indexOf(this.state.query.toLowerCase()) !== -1 || repo.namespace.toLowerCase().indexOf(this.state.query.toLowerCase()) !== -1;
          }
          return true;
        })
        .filter(repo => filter === 'all' || (filter === 'recommended' && repo.is_recommended) || (filter === 'userrepos' && repo.is_user_repo));

    let results, paginateResults;
    let previous = [];
    let next = [];
    if (this.state.previousPage) {
      let previousPage = this.state.currentPage - 7;
      if (previousPage < 1) {
        previousPage = 1;
      }
      previous.push((

            «

      ));
      for (previousPage; previousPage < this.state.currentPage; previousPage++) {
        previous.push((

{previousPage}

        ));
      }
    }
    if (this.state.nextPage) {
      let nextPage = this.state.currentPage + 1;
      for (nextPage; nextPage < this.state.totalPage; nextPage++) {
        next.push((

{nextPage}

        ));
        if (nextPage > this.state.currentPage + 7) {
          break;
        }
      }
      next.push((

            »

      ));
    }

    let current = (

        {this.state.currentPage} (current)

    );
    paginateResults = (next.length || previous.length) && (this.state.query !== '') ? (

          {previous}
          {current}
          {next}

    ) : null;
    let errorMsg = null;
    if (this.state.error === null || this.state.error.message.indexOf('getaddrinfo ENOTFOUND') !== -1) {
      errorMsg = 'There was an error contacting Docker Hub.';
    } else {
      errorMsg = this.state.error.message.replace('HTTP code is 409 which indicates error: conflict - ', '');
    }
    if (this.state.error) {
      results = (

{errorMsg}

      );
      paginateResults = null;
    } else if (filter === 'userrepos' && !accountStore.getState().username) {
      results = (

<Router.Link to="login">Log In</Router.Link> or <Router.Link to="signup">Sign Up</Router.Link> to access your Docker Hub repositories.

          <RetinaImage src="connect-art.png" checkIfRetinaImgExists={false}/>

      );
      paginateResults = null;
    } else if (filter === 'userrepos' && !accountStore.getState().verified) {
      let spinner = this.state.accountLoading ?

 : null;
      results = (

Please verify your Docker Hub account email address

            <button className="btn btn-action" onClick={this.handleCheckVerification}>{'I\'ve Verified My Email Address'}</button> {spinner}

          <RetinaImage src="inspection.png" checkIfRetinaImgExists={false}/>

      );
      paginateResults = null;
    } else if (filter === 'userimages') {
      let userImageItems = this.state.images.map((image, index) => {
        image.description = null;
        let tags = image.tags.join('-');
        image.star_count = 0;
        image.is_local = true;
        const key = `local-${image.name}-${index}`;
        let imageCard = null;
        if (image.name !== '<none>') {
          imageCard = (<ImageCard key={key + ':' + tags} image={image} chosenTag={image.tags[0]} tags={image.tags} />);
        }
        return imageCard;
      });
      let userImageResults = userImageItems.length ? (

My Images

              {userImageItems}

      ) :

Cannot find any local image.

;
      results = (
          {userImageResults}
      );
    } else if (this.state.loading) {
      results = (

Loading Images

      );
    } else if (repos.length) {
      let recommendedItems = repos.filter(repo => repo.is_recommended).map((image, index) => {
        const key = `rec-${image.name}-${index}`;
        return (<ImageCard key={key} image={image} />);
      });
      let otherItems = repos.filter(repo => !repo.is_recommended && !repo.is_user_repo).map((image, index) => {
        const key = `other-${image.name}-${index}`;
        return (<ImageCard key={key} image={image} />);
      });

      let recommendedResults = recommendedItems.length ? (

Recommended

            {recommendedItems}

      ) : null;

      let userRepoItems = repos.filter(repo => repo.is_user_repo).map((image, index) => {
        const key = `usr-${image.name}-${index}`;
        return (<ImageCard key={key} image={image} />);
      });
      let userRepoResults = userRepoItems.length ? (

My Repositories

            {userRepoItems}

      ) : null;

      let otherResults;
      if (otherItems.length) {
        otherResults = (

Other Repositories

              {otherItems}

        );
      } else {
        otherResults = null;
        paginateResults = null;
      }

      results = (

          {recommendedResults}
          {userRepoResults}
          {otherResults}

      );
    } else {
      if (this.state.query.length) {
        results = (

Cannot find a matching image.

        );
      } else {
        results = (

No Images

        );
      }
    }

    let loadingClasses = classNames({
      hidden: !this.state.loading,
      spinner: true,
      loading: true,
      'la-ball-clip-rotate': true,
      'la-dark': true,
      'la-sm': true
    });

    let magnifierClasses = classNames({
      hidden: this.state.loading,
      icon: true,
      'icon-search': true,
      'search-icon': true
    });
    let searchClasses = classNames('search-bar');
    if (filter === 'userimages') {
      searchClasses = classNames('search-bar', {
        hidden: true
      });
    }

    return (

              <input type="search" ref="searchInput" className="form-control" placeholder="Search for Docker images from Docker Hub" onChange={this.handleChange}/>

              FILTER BY
              <span className={`results-filter results-all tab ${filter === 'all' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'all')}>All
              <span className={`results-filter results-recommended tab ${filter === 'recommended' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'recommended')}>Recommended
              <span className={`results-filter results-userrepos tab ${filter === 'userrepos' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'userrepos')}>My Repos
              <span className={`results-filter results-userimages tab ${filter === 'userimages' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'userimages')}>My Images

            {results}

            {paginateResults}

    );
  }
});
<commit_msg>refactor local images view to handle dangling images (#3232)<commit_after>import _ from 'underscore';
import React from 'react/addons';
import Router from 'react-router';
import RetinaImage from 'react-retina-image';
import ImageCard from './ImageCard.react';
import Promise from 'bluebird';
import metrics from '../utils/MetricsUtil';
import classNames from 'classnames';
import repositoryActions from '../actions/RepositoryActions';
import repositoryStore from '../stores/RepositoryStore';
import accountStore from '../stores/AccountStore';
import accountActions from '../actions/AccountActions';
import imageActions from '../actions/ImageActions';
import imageStore from '../stores/ImageStore';

var _searchPromise = null;

module.exports = React.createClass({
  mixins: [Router.Navigation, Router.State],
  getInitialState: function () {
    return {
      query: '',
      loading: repositoryStore.loading(),
      repos: repositoryStore.all(),
      images: imageStore.all(),
      imagesErr: imageStore.error,
      username: accountStore.getState().username,
      verified: accountStore.getState().verified,
      accountLoading: accountStore.getState().loading,
      error: repositoryStore.getState().error,
      currentPage: repositoryStore.getState().currentPage,
      totalPage: repositoryStore.getState().totalPage,
      previousPage: repositoryStore.getState().previousPage,
      nextPage: repositoryStore.getState().nextPage
    };
  },
  componentDidMount: function () {
    this.refs.searchInput.getDOMNode().focus();
    repositoryStore.listen(this.update);
    accountStore.listen(this.updateAccount);
    imageStore.listen(this.updateImage);
    repositoryActions.search();
  },
  componentWillUnmount: function () {
    if (_searchPromise) {
      _searchPromise.cancel();
    }

    repositoryStore.unlisten(this.update);
    accountStore.unlisten(this.updateAccount);
  },
  update: function () {
    this.setState({
      loading: repositoryStore.loading(),
      repos: repositoryStore.all(),
      currentPage: repositoryStore.getState().currentPage,
      totalPage: repositoryStore.getState().totalPage,
      previousPage: repositoryStore.getState().previousPage,
      nextPage: repositoryStore.getState().nextPage,
      error: repositoryStore.getState().error
    });
  },
  updateImage: function (imgStore) {
    this.setState({
      images: imgStore.images,
      error: imgStore.error
    });
  },
  updateAccount: function () {
    this.setState({
      username: accountStore.getState().username,
      verified: accountStore.getState().verified,
      accountLoading: accountStore.getState().loading
    });
  },
  search: function (query, page = 1) {
    if (_searchPromise) {
      _searchPromise.cancel();
      _searchPromise = null;
    }
    let previousPage, nextPage, totalPage = null;
    // If query remains, retain pagination
    if (this.state.query === query) {
      previousPage = (page - 1 < 1) ? 1 : page - 1;
      nextPage = (page + 1 > this.state.totalPage) ? this.state.totalPage : page + 1;
      totalPage = this.state.totalPage;
    }
    this.setState({
      query: query,
      loading: true,
      currentPage: page,
      previousPage: previousPage,
      nextPage: nextPage,
      totalPage: totalPage,
      error: null
    });

    _searchPromise = Promise.delay(200).cancellable().then(() => {
      metrics.track('Searched for Images');
      _searchPromise = null;
      repositoryActions.search(query, page);
    }).catch(Promise.CancellationError, () => {});
  },
  handleChange: function (e) {
    let query = e.target.value;
    if (query === this.state.query) {
      return;
    }
    this.search(query);
  },
  handlePage: function (page) {
    let query = this.state.query;
    this.search(query, page);
  },
  handleFilter: function (filter) {

    this.setState({error: null});

    // If we're clicking on the filter again - refresh
    if (filter === 'userrepos' && this.getQuery().filter === 'userrepos') {
      repositoryActions.repos();
    }

    if (filter === 'userimages' && this.getQuery().filter === 'userimages') {
      imageActions.all();
    }

    if (filter === 'recommended' && this.getQuery().filter === 'recommended') {
      repositoryActions.recommended();
    }

    this.transitionTo('search', {}, {filter: filter});

    metrics.track('Filtered Results', {
      filter: filter
    });
  },
  handleCheckVerification: function () {
    accountActions.verify();
    metrics.track('Verified Account', {
      from: 'search'
    });
  },
  render: function () {
    let filter = this.getQuery().filter || 'all';
    let repos = _.values(this.state.repos)
        .filter(repo => {
          if (repo.is_recommended || repo.is_user_repo) {
            return repo.name.toLowerCase().indexOf(this.state.query.toLowerCase()) !== -1 || repo.namespace.toLowerCase().indexOf(this.state.query.toLowerCase()) !== -1;
          }
          return true;
        })
        .filter(repo => filter === 'all' || (filter === 'recommended' && repo.is_recommended) || (filter === 'userrepos' && repo.is_user_repo));

    let results, paginateResults;
    let previous = [];
    let next = [];
    if (this.state.previousPage) {
      let previousPage = this.state.currentPage - 7;
      if (previousPage < 1) {
        previousPage = 1;
      }
      previous.push((

            «

      ));
      for (previousPage; previousPage < this.state.currentPage; previousPage++) {
        previous.push((

{previousPage}

        ));
      }
    }
    if (this.state.nextPage) {
      let nextPage = this.state.currentPage + 1;
      for (nextPage; nextPage < this.state.totalPage; nextPage++) {
        next.push((

{nextPage}

        ));
        if (nextPage > this.state.currentPage + 7) {
          break;
        }
      }
      next.push((

            »

      ));
    }

    let current = (

        {this.state.currentPage} (current)

    );
    paginateResults = (next.length || previous.length) && (this.state.query !== '') ? (

          {previous}
          {current}
          {next}

    ) : null;
    let errorMsg = null;
    if (this.state.error === null || this.state.error.message.indexOf('getaddrinfo ENOTFOUND') !== -1) {
      errorMsg = 'There was an error contacting Docker Hub.';
    } else {
      errorMsg = this.state.error.message.replace('HTTP code is 409 which indicates error: conflict - ', '');
    }
    if (this.state.error) {
      results = (

{errorMsg}

      );
      paginateResults = null;
    } else if (filter === 'userrepos' && !accountStore.getState().username) {
      results = (

<Router.Link to="login">Log In</Router.Link> or <Router.Link to="signup">Sign Up</Router.Link> to access your Docker Hub repositories.

          <RetinaImage src="connect-art.png" checkIfRetinaImgExists={false}/>

      );
      paginateResults = null;
    } else if (filter === 'userrepos' && !accountStore.getState().verified) {
      let spinner = this.state.accountLoading ?

 : null;
      results = (

Please verify your Docker Hub account email address

            <button className="btn btn-action" onClick={this.handleCheckVerification}>{'I\'ve Verified My Email Address'}</button> {spinner}

          <RetinaImage src="inspection.png" checkIfRetinaImgExists={false}/>

      );
      paginateResults = null;
    } else if (filter === 'userimages') {
      // filter out dangling images (aka images with no name/tag)
      let validImages = this.state.images.filter((image) => image.name !== '<none>');
      let userImageItems = validImages.map((image, index) => {
        image.description = null;
        let tags = image.tags.join('-');
        image.star_count = 0;
        image.is_local = true;
        const key = `local-${image.name}-${index}`;
        return (
          <ImageCard key={key + ':' + tags} image={image} chosenTag={image.tags[0]} tags={image.tags} />
        );
      });
      let userImageResults = userImageItems.length ? (

My Images

              {userImageItems}

      ) : (

Cannot find any local image.

      );
      results = (
        {userImageResults}
      );
    } else if (this.state.loading) {
      results = (

Loading Images

      );
    } else if (repos.length) {
      let recommendedItems = repos.filter(repo => repo.is_recommended).map((image, index) => {
        const key = `rec-${image.name}-${index}`;
        return (<ImageCard key={key} image={image} />);
      });
      let otherItems = repos.filter(repo => !repo.is_recommended && !repo.is_user_repo).map((image, index) => {
        const key = `other-${image.name}-${index}`;
        return (<ImageCard key={key} image={image} />);
      });

      let recommendedResults = recommendedItems.length ? (

Recommended

            {recommendedItems}

      ) : null;

      let userRepoItems = repos.filter(repo => repo.is_user_repo).map((image, index) => {
        const key = `usr-${image.name}-${index}`;
        return (<ImageCard key={key} image={image} />);
      });
      let userRepoResults = userRepoItems.length ? (

My Repositories

            {userRepoItems}

      ) : null;

      let otherResults;
      if (otherItems.length) {
        otherResults = (

Other Repositories

              {otherItems}

        );
      } else {
        otherResults = null;
        paginateResults = null;
      }

      results = (

          {recommendedResults}
          {userRepoResults}
          {otherResults}

      );
    } else {
      if (this.state.query.length) {
        results = (

Cannot find a matching image.

        );
      } else {
        results = (

No Images

        );
      }
    }

    let loadingClasses = classNames({
      hidden: !this.state.loading,
      spinner: true,
      loading: true,
      'la-ball-clip-rotate': true,
      'la-dark': true,
      'la-sm': true
    });

    let magnifierClasses = classNames({
      hidden: this.state.loading,
      icon: true,
      'icon-search': true,
      'search-icon': true
    });
    let searchClasses = classNames('search-bar');
    if (filter === 'userimages') {
      searchClasses = classNames('search-bar', {
        hidden: true
      });
    }

    return (

              <input type="search" ref="searchInput" className="form-control" placeholder="Search for Docker images from Docker Hub" onChange={this.handleChange}/>

              FILTER BY
              <span className={`results-filter results-all tab ${filter === 'all' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'all')}>All
              <span className={`results-filter results-recommended tab ${filter === 'recommended' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'recommended')}>Recommended
              <span className={`results-filter results-userrepos tab ${filter === 'userrepos' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'userrepos')}>My Repos
              <span className={`results-filter results-userimages tab ${filter === 'userimages' ? 'active' : ''}`} onClick={this.handleFilter.bind(this, 'userimages')}>My Images

            {results}

            {paginateResults}

    );
  }
});
<|endoftext|>


<commit_before>var async = require('async');
var util = require('util');
var spawn = require('child_process').spawn;
var s3 = require('s3');

var syncClient = s3.createClient({
    maxAsyncS3: 20,
});

tmpDir = "/tmp/sources";
pubDir = tmpDir + "/public";

exports.handler = function(event, context) {
    // Read options from the event.
    console.log("Reading options from event:\n", util.inspect(event, {depth: 5}));
    var srcBucket = event.Records[0].s3.bucket.name;
    var srcKey    = event.Records[0].s3.object.key;
    var dstBucket = event.Records[0].s3.bucket.name.replace('input.', '');

    var isDirRe = new RegExp(/\/$/);
    var isStaticRe = new RegExp(/(^static\/|\/static\/)/);
    // don't run hugo for just static files
    if (srcKey.match(isStaticRe) !== null) {
        console.log("Key " + srcKey + " is static content, bailing out");
        context.done();
    }

    async.waterfall([
    function download(next) {
        var params = {
            localDir: tmpDir,

            s3Params: {
                Bucket: srcBucket,
            },
            getS3Params: function(localfile, s3Object, callback) {
                // skip static content
                if (s3Object.Key.match(isStaticRe) !== null) {
                    callback(null, null);
                    return;
                }
                if (s3Object.Key.match(isDirRe) !== null) {
                    callback(null, null);
                    return;
                }
                callback(null, {});
            },
        };
        var downloader = syncClient.downloadDir(params);
        downloader.on('error', function(err) {
            console.error("unable to sync down:", err.stack);
            next(err);
        });
        downloader.on('end', function() {
            console.log("done downloading");
            next(null);
        });
    },
    function runHugo(next) {
        console.log("Running hugo");
        var child = spawn("./hugo", ["-v", "--source=" + tmpDir, "--destination=" + pubDir], {});
        child.stdout.on('data', function (data) {
            console.log('hugo-stdout: ' + data);
        });
        child.stderr.on('data', function (data) {
            console.log('hugo-stderr: ' + data);
        });
        child.on('error', function(err) {
            console.log("hugo failed with error: " + err);
            next(err);
        });
        child.on('close', function(code) {
            console.log("hugo exited with code: " + code);
            next(null);
        });
    },
    function upload(next) {
        var params = {
            localDir: pubDir,
            deleteRemoved: false,
            s3Params: {
                ACL: 'public-read',
                Bucket: dstBucket,
            },
        };
        var uploader = syncClient.uploadDir(params);
        uploader.on('error', function(err) {
            console.error("unable to sync up:", err.stack);
            next(err);
        });
        uploader.on('end', function() {
            console.log("done uploading");
            next(null);
        });
    },
    ], function(err) {
        if (err) console.error("Failure because of: " + err)
        else console.log("All methods in waterfall succeeded.");

        context.done();
    });
};
<commit_msg>Reduce number of hugo runs by skipping files in .git<commit_after>var async = require('async');
var util = require('util');
var spawn = require('child_process').spawn;
var s3 = require('s3');

var syncClient = s3.createClient({
    maxAsyncS3: 20,
});

tmpDir = "/tmp/sources";
pubDir = tmpDir + "/public";

exports.handler = function(event, context) {
    // Read options from the event.
    console.log("Reading options from event:\n", util.inspect(event, {depth: 5}));
    var srcBucket = event.Records[0].s3.bucket.name;
    var srcKey    = event.Records[0].s3.object.key;
    var dstBucket = event.Records[0].s3.bucket.name.replace('input.', '');

    var isDirRe = new RegExp(/\/$/);
    var isStaticRe = new RegExp(/(^static\/|\/static\/)/);
    // don't run hugo for just static files
    if (srcKey.match(isStaticRe) !== null) {
        console.log("Key " + srcKey + " is static content, bailing out");
        context.done();
    }
    // don't run hugo for git files either
    if (srcKey.match(/^\.git\//) !== null) {
        console.log("Key " + srcKey + " is static content, bailing out");
        context.done();
    }

    async.waterfall([
    function download(next) {
        var params = {
            localDir: tmpDir,

            s3Params: {
                Bucket: srcBucket,
            },
            getS3Params: function(localfile, s3Object, callback) {
                // skip static content
                if (s3Object.Key.match(isStaticRe) !== null) {
                    callback(null, null);
                    return;
                }
                if (s3Object.Key.match(isDirRe) !== null) {
                    callback(null, null);
                    return;
                }
                callback(null, {});
            },
        };
        var downloader = syncClient.downloadDir(params);
        downloader.on('error', function(err) {
            console.error("unable to sync down:", err.stack);
            next(err);
        });
        downloader.on('end', function() {
            console.log("done downloading");
            next(null);
        });
    },
    function runHugo(next) {
        console.log("Running hugo");
        var child = spawn("./hugo", ["-v", "--source=" + tmpDir, "--destination=" + pubDir], {});
        child.stdout.on('data', function (data) {
            console.log('hugo-stdout: ' + data);
        });
        child.stderr.on('data', function (data) {
            console.log('hugo-stderr: ' + data);
        });
        child.on('error', function(err) {
            console.log("hugo failed with error: " + err);
            next(err);
        });
        child.on('close', function(code) {
            console.log("hugo exited with code: " + code);
            next(null);
        });
    },
    function upload(next) {
        var params = {
            localDir: pubDir,
            deleteRemoved: false,
            s3Params: {
                ACL: 'public-read',
                Bucket: dstBucket,
            },
        };
        var uploader = syncClient.uploadDir(params);
        uploader.on('error', function(err) {
            console.error("unable to sync up:", err.stack);
            next(err);
        });
        uploader.on('end', function() {
            console.log("done uploading");
            next(null);
        });
    },
    ], function(err) {
        if (err) console.error("Failure because of: " + err)
        else console.log("All methods in waterfall succeeded.");

        context.done();
    });
};
<|endoftext|>


<commit_before>/* FileSaver.js demo script
 * 2012-01-23
 *
 * By Eli Grey, http://eligrey.com
 * License: X11/MIT
 *   See LICENSE.md
 */

/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/demo/demo.js */

(function(view) {
"use strict";
// The canvas drawing portion of the demo is based off the demo at
// http://www.williammalone.com/articles/create-html5-canvas-javascript-drawing-app/
var
	  document = view.document
	, $ = function(id) {
		return document.getElementById(id);
	}
	, session = view.sessionStorage
	// only get URL when necessary in case Blob.js hasn't defined it yet
	, get_blob = function() {
		return view.Blob;
	}

	, canvas = $("canvas")
	, canvas_options_form = $("canvas-options")
	, canvas_filename = $("canvas-filename")
	, canvas_clear_button = $("canvas-clear")

	, text = $("text")
	, text_options_form = $("text-options")
	, text_filename = $("text-filename")

	, html = $("html")
	, html_options_form = $("html-options")
	, html_filename = $("html-filename")

	, ctx = canvas.getContext("2d")
	, drawing = false
	, x_points = session.x_points || []
	, y_points = session.y_points || []
	, drag_points = session.drag_points || []
	, add_point = function(x, y, dragging) {
		x_points.push(x);
		y_points.push(y);
		drag_points.push(dragging);
	}
	, draw = function(){
		canvas.width = canvas.width;
		ctx.lineWidth = 6;
		ctx.lineJoin = "round";
		ctx.strokeStyle = "#000000";
		var
			  i = 0
			, len = x_points.length
		;
		for(; i < len; i++) {
			ctx.beginPath();
			if (i && drag_points[i]) {
				ctx.moveTo(x_points[i-1], y_points[i-1]);
			} else {
				ctx.moveTo(x_points[i]-1, y_points[i]);
			}
			ctx.lineTo(x_points[i], y_points[i]);
			ctx.closePath();
			ctx.stroke();
		}
	}
	, stop_drawing = function() {
		drawing = false;
	}

	// Title guesser and document creator available at https://gist.github.com/1059648
	, guess_title = function(doc) {
		var
			  h = "h6 h5 h4 h3 h2 h1".split(" ")
			, i = h.length
			, headers
			, header_text
		;
		while (i--) {
			headers = doc.getElementsByTagName(h[i]);
			for (var j = 0, len = headers.length; j < len; j++) {
				header_text = headers[j].textContent.trim();
				if (header_text) {
					return header_text;
				}
			}
		}
	}
	, doc_impl = document.implementation
	, create_html_doc = function(html) {
		var
			  dt = doc_impl.createDocumentType('html', null, null)
			, doc = doc_impl.createDocument("http://www.w3.org/1999/xhtml", "html", dt)
			, doc_el = doc.documentElement
			, head = doc_el.appendChild(doc.createElement("head"))
			, charset_meta = head.appendChild(doc.createElement("meta"))
			, title = head.appendChild(doc.createElement("title"))
			, body = doc_el.appendChild(doc.createElement("body"))
			, i = 0
			, len = html.childNodes.length
		;
		charset_meta.setAttribute("charset", html.ownerDocument.characterSet);
		for (; i < len; i++) {
			body.appendChild(doc.importNode(html.childNodes.item(i), true));
		}
		var title_text = guess_title(doc);
		if (title_text) {
			title.appendChild(doc.createTextNode(title_text));
		}
		return doc;
	}
;
canvas.width = 500;
canvas.height = 300;

  if (typeof x_points === "string") {
	x_points = JSON.parse(x_points);
} if (typeof y_points === "string") {
	y_points = JSON.parse(y_points);
} if (typeof drag_points === "string") {
	drag_points = JSON.parse(drag_points);
} if (session.canvas_filename) {
	canvas_filename.value = session.canvas_filename;
} if (session.text) {
	text.value = session.text;
} if (session.text_filename) {
	text_filename.value = session.text_filename;
} if (session.html) {
	html.innerHTML = session.html;
} if (session.html_filename) {
	html_filename.value = session.html_filename;
}

drawing = true;
draw();
drawing = false;

canvas_clear_button.addEventListener("click", function() {
	canvas.width = canvas.width;
	x_points.length =
	y_points.length =
	drag_points.length =
		0;
}, false);
canvas.addEventListener("mousedown", function(event) {
	drawing = true;
	add_point(event.pageX - canvas.offsetLeft, event.pageY - canvas.offsetTop, false);
	draw();
}, false);
canvas.addEventListener("mousemove", function(event) {
	if (drawing) {
		add_point(event.pageX - canvas.offsetLeft, event.pageY - canvas.offsetTop, true);
		draw();
	}
}, false);
canvas.addEventListener("mouseup", stop_drawing, false);
canvas.addEventListener("mouseout", stop_drawing, false);

canvas_options_form.addEventListener("submit", function(event) {
	event.preventDefault();
	canvas.toBlob(function(blob) {
		saveAs(
			  blob
			, (canvas_filename.value || canvas_filename.placeholder) + ".png"
		);
	}, "image/png");
}, false);

text_options_form.addEventListener("submit", function(event) {
	event.preventDefault();
	var
		  BB = get_blob();
		, bb = new BB(text.value || text.placeholder)
	;
	saveAs(
		  bb.getBlob("text/plain;charset=" + document.characterSet)
		, (text_filename.value || text_filename.placeholder) + ".txt"
	);
}, false);

html_options_form.addEventListener("submit", function(event) {
	event.preventDefault();
	var
		  BB = get_blob()
		, bb
		, xml_serializer = new XMLSerializer
		, doc = create_html_doc(html)
	;
	bb = new BB(xml_serializer.serializeToString(doc));
	saveAs(
		  bb.getBlob("application/xhtml+xml;charset=" + document.characterSet)
		, (html_filename.value || html_filename.placeholder) + ".xhtml"
	);
}, false);

view.addEventListener("unload", function() {
	session.x_points = JSON.stringify(x_points);
	session.y_points = JSON.stringify(y_points);
	session.drag_points = JSON.stringify(drag_points);
	session.canvas_filename = canvas_filename.value;

	session.text = text.value;
	session.text_filename = text_filename.value;

	session.html = html.innerHTML;
	session.html_filename = html_filename.value;
}, false);
}(self));
<commit_msg>Removed accidental semicolon<commit_after>/* FileSaver.js demo script
 * 2012-01-23
 *
 * By Eli Grey, http://eligrey.com
 * License: X11/MIT
 *   See LICENSE.md
 */

/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/demo/demo.js */

(function(view) {
"use strict";
// The canvas drawing portion of the demo is based off the demo at
// http://www.williammalone.com/articles/create-html5-canvas-javascript-drawing-app/
var
	  document = view.document
	, $ = function(id) {
		return document.getElementById(id);
	}
	, session = view.sessionStorage
	// only get URL when necessary in case Blob.js hasn't defined it yet
	, get_blob = function() {
		return view.Blob;
	}

	, canvas = $("canvas")
	, canvas_options_form = $("canvas-options")
	, canvas_filename = $("canvas-filename")
	, canvas_clear_button = $("canvas-clear")

	, text = $("text")
	, text_options_form = $("text-options")
	, text_filename = $("text-filename")

	, html = $("html")
	, html_options_form = $("html-options")
	, html_filename = $("html-filename")

	, ctx = canvas.getContext("2d")
	, drawing = false
	, x_points = session.x_points || []
	, y_points = session.y_points || []
	, drag_points = session.drag_points || []
	, add_point = function(x, y, dragging) {
		x_points.push(x);
		y_points.push(y);
		drag_points.push(dragging);
	}
	, draw = function(){
		canvas.width = canvas.width;
		ctx.lineWidth = 6;
		ctx.lineJoin = "round";
		ctx.strokeStyle = "#000000";
		var
			  i = 0
			, len = x_points.length
		;
		for(; i < len; i++) {
			ctx.beginPath();
			if (i && drag_points[i]) {
				ctx.moveTo(x_points[i-1], y_points[i-1]);
			} else {
				ctx.moveTo(x_points[i]-1, y_points[i]);
			}
			ctx.lineTo(x_points[i], y_points[i]);
			ctx.closePath();
			ctx.stroke();
		}
	}
	, stop_drawing = function() {
		drawing = false;
	}

	// Title guesser and document creator available at https://gist.github.com/1059648
	, guess_title = function(doc) {
		var
			  h = "h6 h5 h4 h3 h2 h1".split(" ")
			, i = h.length
			, headers
			, header_text
		;
		while (i--) {
			headers = doc.getElementsByTagName(h[i]);
			for (var j = 0, len = headers.length; j < len; j++) {
				header_text = headers[j].textContent.trim();
				if (header_text) {
					return header_text;
				}
			}
		}
	}
	, doc_impl = document.implementation
	, create_html_doc = function(html) {
		var
			  dt = doc_impl.createDocumentType('html', null, null)
			, doc = doc_impl.createDocument("http://www.w3.org/1999/xhtml", "html", dt)
			, doc_el = doc.documentElement
			, head = doc_el.appendChild(doc.createElement("head"))
			, charset_meta = head.appendChild(doc.createElement("meta"))
			, title = head.appendChild(doc.createElement("title"))
			, body = doc_el.appendChild(doc.createElement("body"))
			, i = 0
			, len = html.childNodes.length
		;
		charset_meta.setAttribute("charset", html.ownerDocument.characterSet);
		for (; i < len; i++) {
			body.appendChild(doc.importNode(html.childNodes.item(i), true));
		}
		var title_text = guess_title(doc);
		if (title_text) {
			title.appendChild(doc.createTextNode(title_text));
		}
		return doc;
	}
;
canvas.width = 500;
canvas.height = 300;

  if (typeof x_points === "string") {
	x_points = JSON.parse(x_points);
} if (typeof y_points === "string") {
	y_points = JSON.parse(y_points);
} if (typeof drag_points === "string") {
	drag_points = JSON.parse(drag_points);
} if (session.canvas_filename) {
	canvas_filename.value = session.canvas_filename;
} if (session.text) {
	text.value = session.text;
} if (session.text_filename) {
	text_filename.value = session.text_filename;
} if (session.html) {
	html.innerHTML = session.html;
} if (session.html_filename) {
	html_filename.value = session.html_filename;
}

drawing = true;
draw();
drawing = false;

canvas_clear_button.addEventListener("click", function() {
	canvas.width = canvas.width;
	x_points.length =
	y_points.length =
	drag_points.length =
		0;
}, false);
canvas.addEventListener("mousedown", function(event) {
	drawing = true;
	add_point(event.pageX - canvas.offsetLeft, event.pageY - canvas.offsetTop, false);
	draw();
}, false);
canvas.addEventListener("mousemove", function(event) {
	if (drawing) {
		add_point(event.pageX - canvas.offsetLeft, event.pageY - canvas.offsetTop, true);
		draw();
	}
}, false);
canvas.addEventListener("mouseup", stop_drawing, false);
canvas.addEventListener("mouseout", stop_drawing, false);

canvas_options_form.addEventListener("submit", function(event) {
	event.preventDefault();
	canvas.toBlob(function(blob) {
		saveAs(
			  blob
			, (canvas_filename.value || canvas_filename.placeholder) + ".png"
		);
	}, "image/png");
}, false);

text_options_form.addEventListener("submit", function(event) {
	event.preventDefault();
	var
		  BB = get_blob()
		, bb = new BB(text.value || text.placeholder)
	;
	saveAs(
		  bb.getBlob("text/plain;charset=" + document.characterSet)
		, (text_filename.value || text_filename.placeholder) + ".txt"
	);
}, false);

html_options_form.addEventListener("submit", function(event) {
	event.preventDefault();
	var
		  BB = get_blob()
		, bb
		, xml_serializer = new XMLSerializer
		, doc = create_html_doc(html)
	;
	bb = new BB(xml_serializer.serializeToString(doc));
	saveAs(
		  bb.getBlob("application/xhtml+xml;charset=" + document.characterSet)
		, (html_filename.value || html_filename.placeholder) + ".xhtml"
	);
}, false);

view.addEventListener("unload", function() {
	session.x_points = JSON.stringify(x_points);
	session.y_points = JSON.stringify(y_points);
	session.drag_points = JSON.stringify(drag_points);
	session.canvas_filename = canvas_filename.value;

	session.text = text.value;
	session.text_filename = text_filename.value;

	session.html = html.innerHTML;
	session.html_filename = html_filename.value;
}, false);
}(self));
<|endoftext|>


<commit_before>"use strict";
var VFindMode = {
  isActive: false,
  query: "",
  parsedQuery: "",
  partialQuery: "",
  historyIndex: 0,
  isRegex: false,
  ignoreCase: false,
  hasResults: false,
  matchCount: 0,
  scrollX: 0,
  scrollY: 0,
  initialRange: null,
  activeRegexIndex: 0,
  regexMatches: null,
  box: null,
  input: null,
  countEl: null,
  styleIn: null,
  options: null,
  cssSel: "::selection{background:#ff9632;}",
  cssIFrame: '*{font:normal normal normal 12px "Helvetica Neue",Helvetica,Arial,sans-serif !important;\
height:14px;line-height:12px;margin:0;overflow-y:hidden;vertical-align:top;white-space:nowrap;cursor:default;}\
body{cursor:text;display:inline-block;padding:0 3px 0 1px;min-width:7px;}body *{cursor:text;display:inline;}body br{display:none;}',
  activate: function(options) {
    if (!document.body) { return false; }
    options = Object.setPrototypeOf(options || {}, null);
    var query = options.query;
    if (this.isActive) {
      if (query != null) {
        this.findAndFocus(this.query || query, {dir: options.dir || 1, count: options.count || 1});
        return;
      }
      this.box.contentWindow.focus();
      this.input.focus();
      this.box.contentWindow.getSelection().selectAllChildren(this.input);
      return;
    }

    query !== this.query && Marks.setPreviousPosition();
    this.init && this.init();
    this.options = options;
    if (query != null) {
      this.findAndFocus(query, options);
      return;
    }
    this.scrollX = window.scrollX;
    this.scrollY = window.scrollY;
    this.parsedQuery = this.query = "";
    this.regexMatches = null;
    this.activeRegexIndex = 0;
    this.getCurrentRange();

    var el, wnd, doc;
    DomUtils.UI.addElement(el = this.box = DomUtils.createElement("iframe"));
    DomUtils.UI.root.insertBefore(el, VHUD.box);
    el.className = "R HUD";
    el.style.width = "0px";
    el.onmousedown = this.OnMousedown;
    wnd = el.contentWindow;
    wnd.onmousedown = this.OnMousedown;
    wnd.onkeydown = this.onKeydown.bind(this);
    doc = wnd.document;
    el = this.input = doc.body;
    el.contentEditable = "plaintext-only";
    el.oninput = this.onInput.bind(this);
    doc.documentElement.appendChild(this.countEl = doc.createElement("span"));

    el = DomUtils.UI.createStyle(this.cssIFrame, doc);
    doc.head.appendChild(el);
    doc.documentElement.insertBefore(new wnd.Text("/"), doc.body);

    DomUtils.UI.focus(this.input);
    this.isActive = true;
  },
  init: function() {
    var ref = this.postMode, UI = DomUtils.UI;
    ref.exit = ref.exit.bind(ref);
    this.styleIn = UI.createStyle(this.cssSel);
    UI.init && UI.init();
    UI.box.appendChild(UI.createStyle(".vimiumFindMode " + this.cssSel));
    this.init = null;
  },
  findAndFocus: function(query, options) {
    if (query !== this.query) {
      this.updateQuery(query);
      if (this.isActive) {
        this.input.textContent = query.replace(/^ /, '\xa0');
        this.showCount();
      }
    }
    this.execute(null, options);
    if (!this.hasResults) {
      this.isActive || VHUD.showForDuration("No matches for '" + this.query + "'", 1000);
      return;
    }
    this.focusFoundLink(window.getSelection().anchorNode);
    this.postMode.activate();
  },
  refocus: function() {
    this.checkReturnToViewPort();
    window.focus();
    var el = DomUtils.getSelectionFocusElement();
    el && el.focus();
    this.exit();
    return el;
  },
  exit: function() { // need keep @hasResults
    this.box.remove();
    this.box = this.input = this.countEl = this.options = null;
    this.styleIn.remove();
    this.partialQuery = this.parsedQuery = this.query = "";
    this.initialRange = this.regexMatches = null;
    this.historyIndex = this.matchCount = 0;
    this.isActive = false;
  },
  OnMousedown: function(event) { if (event.target !== VFindMode.input) { event.preventDefault(); VFindMode.input.focus(); } },
  onKeydown: function(event) {
    var i = event.keyCode, n = i, el, el2;
    i = event.altKey ? 0 : i === KeyCodes.enter ? (this.saveQuery(), 2)
      : (i === KeyCodes.backspace || i === KeyCodes.deleteKey) ? +!this.query.length
      : 0;
    if (!i) {
      if (!KeyboardUtils.isPlain(event)) {
        if (event.shiftKey || !(event.ctrlKey || event.metaKey)) { return; }
        else if (n === 74 || n === 78 || n === 75 || n === 80) { this.execute(null, {dir: (n % 5) ? 1 : -1 }); }
      }
      else if (n === KeyCodes.f1) { this.box.contentDocument.execCommand("delete"); }
      else if (n === KeyCodes.f1 + 1) { window.focus(); }
      else if (n === KeyCodes.esc) { i = 3; }
      else if (n === KeyCodes.up || n === KeyCodes.down) { this.nextQuery(n === KeyCodes.up ? 1 : -1); }
      else { return; }
    }
    DomUtils.suppressEvent(event);
    if (!i) { return; }
    var hasStyle = !!this.styleIn.parentNode;
    el = this.refocus();
    if (i < 2 || !this.hasResults) { return; }
    if (i === 3 && hasStyle) {
      this.toggleStyle("remove");
      this.restoreSelection(true);
    }
    if (!el || el !== VInsertMode.lock) {
      el = window.getSelection().anchorNode;
      if (el && !this.focusFoundLink(el) && i === 3 && (el2 = document.activeElement)) {
        DomUtils.getEditableType(el2) === 3 && el.contains(el2) && DomUtils.UI.simulateSelect(el2);
      }
    }
    i === 2 && this.postMode.activate();
  },
  focusFoundLink: function(el) {
    for (; el && el !== document.body; el = el.parentElement) {
      if (el instanceof HTMLAnchorElement) {
        el.focus();
        return true;
      }
    }
  },
  nextQuery: function(dir) {
    var ind = this.historyIndex + dir, query;
    if (ind === 1 && dir > 0) { this.partialQuery = this.query; }
    else if (ind < 1) { query = ind < 0 ? (ind = 0, this.query) : this.partialQuery; }
    this.historyIndex = ind;
    if (ind < 1) { this.SetQuery(query); return; }
    MainPort.sendMessage({ handler: "findQuery", index: ind }, this.SetQuery );
  },
  SetQuery: function(query) {
    var _this = VFindMode, sel;
    if (query === _this.query) { return; }
    if (!query && _this.historyIndex > 0) { --_this.historyIndex; return; }
    _this.input.textContent = query.replace(/^ /, '\xa0');
    sel = _this.box.contentWindow.getSelection();
    sel.selectAllChildren(_this.input);
    sel.collapseToEnd();
    _this.onInput();
  },
  saveQuery: function() {
    this.query && MainPort.port.postMessage({ handler: "findQuery", query: this.query });
  },
  postMode: {
    lock: null,
    activate: function() {
      var el = VInsertMode.lock, Exit = this.exit;
      if (el === this.lock) { el && handlerStack.set(this.onKeydown, this); return; }
      Exit();
      if (!el || el === Vomnibar.input) { return; }
      this.lock = el;
      addEventListener("click", Exit, true);
      VInsertMode.setupSuppress(Exit);
      handlerStack.push(this.onKeydown, this);
      el.addEventListener("blur", Exit, true);
    },
    onKeydown: function(event) {
      var exit = event.keyCode === KeyCodes.esc && KeyboardUtils.isPlain(event);
      exit ? this.exit() : handlerStack.remove(this);
      return 2 * exit;
    },
    exit: function() {
      if (!this.lock) { return; }
      this.lock.removeEventListener("blur", this.exit, true);
      this.lock = null;
      removeEventListener("click", this.exit, true);
      handlerStack.remove(this);
      VInsertMode.exitSuppress();
    }
  },
  onInput: function() {
    var query = this.input.textContent.replace('\xa0', " "), count;
    this.checkReturnToViewPort();
    this.updateQuery(query);
    this.restoreSelection();
    this.execute(!this.isRegex ? this.parsedQuery : this.regexMatches ? this.regexMatches[0] : "");
    this.showCount();
  },
  showCount: function() {
    var count = this.matchCount;
    this.countEl.textContent = this.parsedQuery ? "(" + (count || (this.hasResults ? "Some" : "No")) + " match" + (count !== 1 ? "es)" : ")") : "";
    count = this.input.getBoundingClientRect().width + this.countEl.getBoundingClientRect().width;
    this.box.style.width = (count | 0) + 4 + "px";
  },
  checkReturnToViewPort: function() {
    this.options.returnToViewport && window.scrollTo(this.scrollX, this.scrollY);
  },
  _ctrlRe: /(\\\\?)([rRI]?)/g,
  escapeAllRe: /[\$\(\)\*\+\.\?\[\\\]\^\{\|\}]/g,
  updateQuery: function(query) {
    this.query = query;
    this.isRegex = this.options.isRegex;
    this.hasNoIgnoreCaseFlag = false;
    query = this.parsedQuery = query.replace(this._ctrlRe, this.FormatQuery);
    this.ignoreCase = !this.hasNoIgnoreCaseFlag && !/[A-Z]/.test(query);
    this.isRegex || (query = query.replace(this.escapeAllRe, "\\$&"));

    var re, matches;
    try {
      re = query && new RegExp(query, this.ignoreCase ? "gi" : "g");
    } catch (e) {}
    matches = re && (document.webkitFullscreenElement || document.documentElement).innerText.match(re);
    this.regexMatches = this.isRegex && matches || null;
    this.activeRegexIndex = 0;
    this.matchCount = matches ? matches.length : 0;
  },
  FormatQuery: function(match, slashes, flag) {
    if (!flag || slashes.length != 1) { return match; }
    if (flag === 'I') { VFindMode.hasNoIgnoreCaseFlag = true; }
    else { VFindMode.isRegex = flag === 'r'; }
    return "";
  },
  restoreSelection: function(isCur) {
    var sel = window.getSelection(), range;
    range = !isCur ? this.initialRange : sel.isCollapsed ? null : sel.getRangeAt(0);
    if (!range) { return; }
    sel.removeAllRanges()
    sel.addRange(range);
  },
  getNextQueryFromRegexMatches: function(dir) {
    if (!this.regexMatches) { return ""; }
    var count = this.matchCount;
    this.activeRegexIndex = count = (this.activeRegexIndex + dir + count) % count;
    return this.regexMatches[count];
  },
  execute: function(query, options) {
    options || (options = {});
    var el, found, count = options.count | 0, dir = options.dir || 1, q;
    options.noColor || this.toggleStyle('add');
    do {
      q = query != null ? query : this.isRegex ? this.getNextQueryFromRegexMatches(dir) : this.parsedQuery;
      found = window.find(q, options.caseSensitive || !this.ignoreCase, dir < 0, true, false, true, false);
    } while (0 < --count && found);
    options.noColor || setTimeout(this.hookSel.bind(this, "add"), 0);
    (el = VInsertMode.lock) && DomUtils.getEditableType(el) > 1 && !DomUtils.isSelected(document.activeElement) && el.blur();
    return this.hasResults = found;
  },
  RestoreHighlight: function() { VFindMode.toggleStyle('remove'); },
  hookSel: function(action) { document[action + "EventListener"]("selectionchange", this.RestoreHighlight, true); },
  toggleStyle: function(action) {
    this.hookSel("remove");
    document.documentElement.classList[action]("vimiumFindMode");
    action !== "add" ? this.styleIn.remove() : (this.isActive || DomUtils.UI.root) && DomUtils.UI.addElement(this.styleIn);
  },
  getCurrentRange: function() {
    var sel = window.getSelection(), range;
    if (sel.type == "None") {
      range = this.initialRange = document.createRange();
      range.setStart(document.body, 0);
      range.setEnd(document.body, 0);
    } else {
      sel.type == "Range" && sel.collapseToStart();
      this.initialRange = sel.getRangeAt(0);
    }
  }
};
<commit_msg>rework postFindMode.activate/exit: faster<commit_after>"use strict";
var VFindMode = {
  isActive: false,
  query: "",
  parsedQuery: "",
  partialQuery: "",
  historyIndex: 0,
  isRegex: false,
  ignoreCase: false,
  hasResults: false,
  matchCount: 0,
  scrollX: 0,
  scrollY: 0,
  initialRange: null,
  activeRegexIndex: 0,
  regexMatches: null,
  box: null,
  input: null,
  countEl: null,
  styleIn: null,
  options: null,
  cssSel: "::selection{background:#ff9632;}",
  cssIFrame: '*{font:normal normal normal 12px "Helvetica Neue",Helvetica,Arial,sans-serif !important;\
height:14px;line-height:12px;margin:0;overflow-y:hidden;vertical-align:top;white-space:nowrap;cursor:default;}\
body{cursor:text;display:inline-block;padding:0 3px 0 1px;min-width:7px;}body *{cursor:text;display:inline;}body br{display:none;}',
  activate: function(options) {
    if (!document.body) { return false; }
    options = Object.setPrototypeOf(options || {}, null);
    var query = options.query;
    if (this.isActive) {
      if (query != null) {
        this.findAndFocus(this.query || query, {dir: options.dir || 1, count: options.count || 1});
        return;
      }
      this.box.contentWindow.focus();
      this.input.focus();
      this.box.contentWindow.getSelection().selectAllChildren(this.input);
      return;
    }

    query !== this.query && Marks.setPreviousPosition();
    this.init && this.init();
    this.options = options;
    if (query != null) {
      this.findAndFocus(query, options);
      return;
    }
    this.scrollX = window.scrollX;
    this.scrollY = window.scrollY;
    this.parsedQuery = this.query = "";
    this.regexMatches = null;
    this.activeRegexIndex = 0;
    this.getCurrentRange();

    var el, wnd, doc;
    DomUtils.UI.addElement(el = this.box = DomUtils.createElement("iframe"));
    DomUtils.UI.root.insertBefore(el, VHUD.box);
    el.className = "R HUD";
    el.style.width = "0px";
    el.onmousedown = this.OnMousedown;
    wnd = el.contentWindow;
    wnd.onmousedown = this.OnMousedown;
    wnd.onkeydown = this.onKeydown.bind(this);
    doc = wnd.document;
    el = this.input = doc.body;
    el.contentEditable = "plaintext-only";
    el.oninput = this.onInput.bind(this);
    doc.documentElement.appendChild(this.countEl = doc.createElement("span"));

    el = DomUtils.UI.createStyle(this.cssIFrame, doc);
    doc.head.appendChild(el);
    doc.documentElement.insertBefore(new wnd.Text("/"), doc.body);

    DomUtils.UI.focus(this.input);
    this.isActive = true;
  },
  init: function() {
    var ref = this.postMode, UI = DomUtils.UI;
    ref.exit = ref.exit.bind(ref);
    this.styleIn = UI.createStyle(this.cssSel);
    UI.init && UI.init();
    UI.box.appendChild(UI.createStyle(".vimiumFindMode " + this.cssSel));
    this.init = null;
  },
  findAndFocus: function(query, options) {
    if (query !== this.query) {
      this.updateQuery(query);
      if (this.isActive) {
        this.input.textContent = query.replace(/^ /, '\xa0');
        this.showCount();
      }
    }
    this.execute(null, options);
    if (!this.hasResults) {
      this.isActive || VHUD.showForDuration("No matches for '" + this.query + "'", 1000);
      return;
    }
    this.focusFoundLink(window.getSelection().anchorNode);
    this.postMode.activate();
  },
  refocus: function() {
    this.checkReturnToViewPort();
    window.focus();
    var el = DomUtils.getSelectionFocusElement();
    el && el.focus();
    this.exit();
    return el;
  },
  exit: function() { // need keep @hasResults
    this.box.remove();
    this.box = this.input = this.countEl = this.options = null;
    this.styleIn.remove();
    this.partialQuery = this.parsedQuery = this.query = "";
    this.initialRange = this.regexMatches = null;
    this.historyIndex = this.matchCount = 0;
    this.isActive = false;
  },
  OnMousedown: function(event) { if (event.target !== VFindMode.input) { event.preventDefault(); VFindMode.input.focus(); } },
  onKeydown: function(event) {
    var i = event.keyCode, n = i, el, el2;
    i = event.altKey ? 0 : i === KeyCodes.enter ? (this.saveQuery(), 2)
      : (i === KeyCodes.backspace || i === KeyCodes.deleteKey) ? +!this.query.length
      : 0;
    if (!i) {
      if (!KeyboardUtils.isPlain(event)) {
        if (event.shiftKey || !(event.ctrlKey || event.metaKey)) { return; }
        else if (n === 74 || n === 78 || n === 75 || n === 80) { this.execute(null, {dir: (n % 5) ? 1 : -1 }); }
      }
      else if (n === KeyCodes.f1) { this.box.contentDocument.execCommand("delete"); }
      else if (n === KeyCodes.f1 + 1) { window.focus(); }
      else if (n === KeyCodes.esc) { i = 3; }
      else if (n === KeyCodes.up || n === KeyCodes.down) { this.nextQuery(n === KeyCodes.up ? 1 : -1); }
      else { return; }
    }
    DomUtils.suppressEvent(event);
    if (!i) { return; }
    var hasStyle = !!this.styleIn.parentNode;
    el = this.refocus();
    if (i < 2 || !this.hasResults) { return; }
    if (i === 3 && hasStyle) {
      this.toggleStyle("remove");
      this.restoreSelection(true);
    }
    if (!el || el !== VInsertMode.lock) {
      el = window.getSelection().anchorNode;
      if (el && !this.focusFoundLink(el) && i === 3 && (el2 = document.activeElement)) {
        DomUtils.getEditableType(el2) === 3 && el.contains(el2) && DomUtils.UI.simulateSelect(el2);
      }
    }
    i === 2 && this.postMode.activate();
  },
  focusFoundLink: function(el) {
    for (; el && el !== document.body; el = el.parentElement) {
      if (el instanceof HTMLAnchorElement) {
        el.focus();
        return true;
      }
    }
  },
  nextQuery: function(dir) {
    var ind = this.historyIndex + dir, query;
    if (ind === 1 && dir > 0) { this.partialQuery = this.query; }
    else if (ind < 1) { query = ind < 0 ? (ind = 0, this.query) : this.partialQuery; }
    this.historyIndex = ind;
    if (ind < 1) { this.SetQuery(query); return; }
    MainPort.sendMessage({ handler: "findQuery", index: ind }, this.SetQuery );
  },
  SetQuery: function(query) {
    var _this = VFindMode, sel;
    if (query === _this.query) { return; }
    if (!query && _this.historyIndex > 0) { --_this.historyIndex; return; }
    _this.input.textContent = query.replace(/^ /, '\xa0');
    sel = _this.box.contentWindow.getSelection();
    sel.selectAllChildren(_this.input);
    sel.collapseToEnd();
    _this.onInput();
  },
  saveQuery: function() {
    this.query && MainPort.port.postMessage({ handler: "findQuery", query: this.query });
  },
  postMode: {
    lock: null,
    activate: function() {
      var el = VInsertMode.lock, Exit = this.exit;
      if (!el || el === Vomnibar.input) { Exit(); return; }
      handlerStack.push(this.onKeydown, this);
      if (el === this.lock) { return; }
      if (!this.lock) {
        addEventListener("click", Exit, true);
        VInsertMode.setupSuppress(Exit);
      }
      this.exit(true);
      this.lock = el;
      el.addEventListener("blur", Exit, true);
    },
    onKeydown: function(event) {
      var exit = event.keyCode === KeyCodes.esc && KeyboardUtils.isPlain(event);
      exit ? this.exit() : handlerStack.remove(this);
      return 2 * exit;
    },
    exit: function(skip) {
      this.lock && this.lock.removeEventListener("blur", this.exit, true);
      if (!this.lock || skip === true) { return; }
      this.lock = null;
      removeEventListener("click", this.exit, true);
      handlerStack.remove(this);
      VInsertMode.exitSuppress();
    }
  },
  onInput: function() {
    var query = this.input.textContent.replace('\xa0', " "), count;
    this.checkReturnToViewPort();
    this.updateQuery(query);
    this.restoreSelection();
    this.execute(!this.isRegex ? this.parsedQuery : this.regexMatches ? this.regexMatches[0] : "");
    this.showCount();
  },
  showCount: function() {
    var count = this.matchCount;
    this.countEl.textContent = this.parsedQuery ? "(" + (count || (this.hasResults ? "Some" : "No")) + " match" + (count !== 1 ? "es)" : ")") : "";
    count = this.input.getBoundingClientRect().width + this.countEl.getBoundingClientRect().width;
    this.box.style.width = (count | 0) + 4 + "px";
  },
  checkReturnToViewPort: function() {
    this.options.returnToViewport && window.scrollTo(this.scrollX, this.scrollY);
  },
  _ctrlRe: /(\\\\?)([rRI]?)/g,
  escapeAllRe: /[\$\(\)\*\+\.\?\[\\\]\^\{\|\}]/g,
  updateQuery: function(query) {
    this.query = query;
    this.isRegex = this.options.isRegex;
    this.hasNoIgnoreCaseFlag = false;
    query = this.parsedQuery = query.replace(this._ctrlRe, this.FormatQuery);
    this.ignoreCase = !this.hasNoIgnoreCaseFlag && !/[A-Z]/.test(query);
    this.isRegex || (query = query.replace(this.escapeAllRe, "\\$&"));

    var re, matches;
    try {
      re = query && new RegExp(query, this.ignoreCase ? "gi" : "g");
    } catch (e) {}
    matches = re && (document.webkitFullscreenElement || document.documentElement).innerText.match(re);
    this.regexMatches = this.isRegex && matches || null;
    this.activeRegexIndex = 0;
    this.matchCount = matches ? matches.length : 0;
  },
  FormatQuery: function(match, slashes, flag) {
    if (!flag || slashes.length != 1) { return match; }
    if (flag === 'I') { VFindMode.hasNoIgnoreCaseFlag = true; }
    else { VFindMode.isRegex = flag === 'r'; }
    return "";
  },
  restoreSelection: function(isCur) {
    var sel = window.getSelection(), range;
    range = !isCur ? this.initialRange : sel.isCollapsed ? null : sel.getRangeAt(0);
    if (!range) { return; }
    sel.removeAllRanges()
    sel.addRange(range);
  },
  getNextQueryFromRegexMatches: function(dir) {
    if (!this.regexMatches) { return ""; }
    var count = this.matchCount;
    this.activeRegexIndex = count = (this.activeRegexIndex + dir + count) % count;
    return this.regexMatches[count];
  },
  execute: function(query, options) {
    options || (options = {});
    var el, found, count = options.count | 0, dir = options.dir || 1, q;
    options.noColor || this.toggleStyle('add');
    do {
      q = query != null ? query : this.isRegex ? this.getNextQueryFromRegexMatches(dir) : this.parsedQuery;
      found = window.find(q, options.caseSensitive || !this.ignoreCase, dir < 0, true, false, true, false);
    } while (0 < --count && found);
    options.noColor || setTimeout(this.hookSel.bind(this, "add"), 0);
    (el = VInsertMode.lock) && DomUtils.getEditableType(el) > 1 && !DomUtils.isSelected(document.activeElement) && el.blur();
    return this.hasResults = found;
  },
  RestoreHighlight: function() { VFindMode.toggleStyle('remove'); },
  hookSel: function(action) { document[action + "EventListener"]("selectionchange", this.RestoreHighlight, true); },
  toggleStyle: function(action) {
    this.hookSel("remove");
    document.documentElement.classList[action]("vimiumFindMode");
    action !== "add" ? this.styleIn.remove() : (this.isActive || DomUtils.UI.root) && DomUtils.UI.addElement(this.styleIn);
  },
  getCurrentRange: function() {
    var sel = window.getSelection(), range;
    if (sel.type == "None") {
      range = this.initialRange = document.createRange();
      range.setStart(document.body, 0);
      range.setEnd(document.body, 0);
    } else {
      sel.type == "Range" && sel.collapseToStart();
      this.initialRange = sel.getRangeAt(0);
    }
  }
};
<|endoftext|>


<commit_before>(function () {
  'use strict';

  angular
    .module('app')
    .controller('FormController', FormController);

  FormController.$inject = ['$document', '$ionicModal', '$ionicPopup', '$log', '$scope', 'FormFactory',
    'ProjectFactory'];

  function FormController($document, $ionicModal, $ionicPopup, $log, $scope, FormFactory, ProjectFactory) {
    var vm = this;

    vm.choices = {};
    vm.field = undefined;
    vm.showFieldInfoModal = {};
    vm.survey = {};

    vm.closeModal = closeModal;
    vm.getMax = getMax;
    vm.getMin = getMin;
    vm.getSurvey = getSurvey;
    vm.isOptionChecked = isOptionChecked;
    vm.setSelMultClass = setSelMultClass;
    vm.showField = showField;
    vm.showFieldInfo = showFieldInfo;
    vm.toggleAcknowledgeChecked = toggleAcknowledgeChecked;
    vm.toggleChecked = toggleChecked;

    activate();

    function activate() {
      vm.survey = FormFactory.getForm().survey;
      vm.choices = FormFactory.getForm().choices;
      $scope.$on('formUpdated', function (e, form) {
        //$log.log('Form updated to', form);
        vm.survey = form.survey;
        vm.choices = form.choices;
      });

      $ionicModal.fromTemplateUrl('app/form/field-info-modal.html', {
        'scope': $scope,
        'animation': 'slide-in-up',
        'backdropClickToClose': false
      }).then(function (modal) {
        vm.fieldInfoModal = modal;
      });
    }

    /**
     * Public Functions
     */

    function closeModal(modal) {
      vm[modal].hide();
    }

    function getMax(constraint) {
      return FormFactory.getMax(constraint);
    }

    function getMin(constraint) {
      return FormFactory.getMin(constraint);
    }

    function getSurvey() {
      return FormFactory.getForm().survey
    }

    function isOptionChecked(field, choice, data) {
      return _.contains(data[field], choice);
    }

    // Set the class for the select_multiple fields here because it is not working
    // to set the class in the html the same way as for the other fields
    function setSelMultClass(field, data) {
      if (field.required === 'true') {
        if (data[field.name]) {
          if (data[field.name].length > 0) {
            return 'no-errors';
          }
        }
        else {
          return 'has-errors';
        }
      }
      return 'no-errors';
    }

    // Determine if the field should be shown or not by looking at the relevant key-value pair
    function showField(field, data) {
      var show = FormFactory.isRelevant(field.relevant, data);
      if (show && field.default) {
        if (!data[field.name]) data[field.name] = field.default;
      }
      if (!show) {
        if (data[field.name]) delete data[field.name];
      }
      var project = ProjectFactory.getCurrentProject();
      if (!project.is_testing_mode) {
        if (field.name === 'group_sed_tab_control' || field.name === 'sed_lithologies' ||
          field.name === 'sed_structures' || field.name === 'sed_interpretations' ||
          field.name === 'group_other_modes' || field.name === 'strat_mode' || field.name === 'minerals' ||
          field.name === 'group_experimental_tab_control' || field.name === 'experimental' ||
          field.name === 'experimental_results' || field.name === 'experimental_set_up' ||
          field.name === 'data' || field.name === 'group_pet_tab_control' || field.name === 'pet_basics' ||
          field.name === 'pet_minerals') {
          show = false;
        }
      }
      return show;
    }

    function showFieldInfo(field) {
      vm.field = field;
      vm.fieldInfoModal.show();
    }

    function toggleAcknowledgeChecked(field, data) {
      if (data.trace_feature) {
        var confirmPopup = $ionicPopup.confirm({
          'title': 'Close Group?',
          'template': 'By closing this group you will be clearing any data in this group. Continue?'
        });
        confirmPopup.then(function (res) {
          if (res) data = FormFactory.toggleAcknowledgeChecked(data, field);
          else $document[0].getElementById(field + 'Toggle').checked = true;
        });
      }
      else data = FormFactory.toggleAcknowledgeChecked(data, field);
    }

    function toggleChecked(field, choice, data) {
      var i = -1;
      if (data[field]) i = data[field].indexOf(choice);
      else data[field] = [];

      // If choice not already selected
      if (i === -1) data[field].push(choice);
      // Choice has been unselected so remove it and delete if empty
      else {
        data[field].splice(i, 1);
        if (data[field].length === 0) delete data[field];
      }
    }
  }
}());
<commit_msg>Forms: Fix for empty field value<commit_after>(function () {
  'use strict';

  angular
    .module('app')
    .controller('FormController', FormController);

  FormController.$inject = ['$document', '$ionicModal', '$ionicPopup', '$log', '$scope', 'FormFactory',
    'ProjectFactory'];

  function FormController($document, $ionicModal, $ionicPopup, $log, $scope, FormFactory, ProjectFactory) {
    var vm = this;

    vm.choices = {};
    vm.field = undefined;
    vm.showFieldInfoModal = {};
    vm.survey = {};

    vm.closeModal = closeModal;
    vm.getMax = getMax;
    vm.getMin = getMin;
    vm.getSurvey = getSurvey;
    vm.isOptionChecked = isOptionChecked;
    vm.setSelMultClass = setSelMultClass;
    vm.showField = showField;
    vm.showFieldInfo = showFieldInfo;
    vm.toggleAcknowledgeChecked = toggleAcknowledgeChecked;
    vm.toggleChecked = toggleChecked;

    activate();

    function activate() {
      vm.survey = FormFactory.getForm().survey;
      vm.choices = FormFactory.getForm().choices;
      $scope.$on('formUpdated', function (e, form) {
        //$log.log('Form updated to', form);
        vm.survey = form.survey;
        vm.choices = form.choices;
      });

      $ionicModal.fromTemplateUrl('app/form/field-info-modal.html', {
        'scope': $scope,
        'animation': 'slide-in-up',
        'backdropClickToClose': false
      }).then(function (modal) {
        vm.fieldInfoModal = modal;
      });
    }

    /**
     * Public Functions
     */

    function closeModal(modal) {
      vm[modal].hide();
    }

    function getMax(constraint) {
      return FormFactory.getMax(constraint);
    }

    function getMin(constraint) {
      return FormFactory.getMin(constraint);
    }

    function getSurvey() {
      return FormFactory.getForm().survey
    }

    function isOptionChecked(field, choice, data) {
      return _.contains(data[field], choice);
    }

    // Set the class for the select_multiple fields here because it is not working
    // to set the class in the html the same way as for the other fields
    function setSelMultClass(field, data) {
      if (field.required === 'true') {
        if (data[field.name]) {
          if (data[field.name].length > 0) {
            return 'no-errors';
          }
        }
        else {
          return 'has-errors';
        }
      }
      return 'no-errors';
    }

    // Determine if the field should be shown or not by looking at the relevant key-value pair
    function showField(field, data) {
      if (_.isEmpty(data[field.name])) delete data[field.name];
      var show = FormFactory.isRelevant(field.relevant, data);
      if (show && field.default) {
        if (!data[field.name]) data[field.name] = field.default;
      }
      if (!show) {
        if (data[field.name]) delete data[field.name];
      }
      var project = ProjectFactory.getCurrentProject();
      if (!project.is_testing_mode) {
        if (field.name === 'group_sed_tab_control' || field.name === 'sed_lithologies' ||
          field.name === 'sed_structures' || field.name === 'sed_interpretations' ||
          field.name === 'group_other_modes' || field.name === 'strat_mode' || field.name === 'minerals' ||
          field.name === 'group_experimental_tab_control' || field.name === 'experimental' ||
          field.name === 'experimental_results' || field.name === 'experimental_set_up' ||
          field.name === 'data' || field.name === 'group_pet_tab_control' || field.name === 'pet_basics' ||
          field.name === 'pet_minerals') {
          show = false;
        }
      }
      return show;
    }

    function showFieldInfo(field) {
      vm.field = field;
      vm.fieldInfoModal.show();
    }

    function toggleAcknowledgeChecked(field, data) {
      if (data.trace_feature) {
        var confirmPopup = $ionicPopup.confirm({
          'title': 'Close Group?',
          'template': 'By closing this group you will be clearing any data in this group. Continue?'
        });
        confirmPopup.then(function (res) {
          if (res) data = FormFactory.toggleAcknowledgeChecked(data, field);
          else $document[0].getElementById(field + 'Toggle').checked = true;
        });
      }
      else data = FormFactory.toggleAcknowledgeChecked(data, field);
    }

    function toggleChecked(field, choice, data) {
      var i = -1;
      if (data[field]) i = data[field].indexOf(choice);
      else data[field] = [];

      // If choice not already selected
      if (i === -1) data[field].push(choice);
      // Choice has been unselected so remove it and delete if empty
      else {
        data[field].splice(i, 1);
        if (data[field].length === 0) delete data[field];
      }
    }
  }
}());
<|endoftext|>


<commit_before>import * as Weights from "./Weights.js";
import * as PartTime from "./PartTime.js";
import * as ValueMax from "./ValueMax.js";

export { Params, Layer, StringArrayToEntities };

/**
 * CNN separable convolution (2D) layer parameters.
 */
class Params extends Weights.Params {

  /**
   * If ( channelMultiplier != null ) and ( outChannels != null ), extract 4 parameters [ dilationHeight,
   * dilationWidth, filterHeight, filterWidth ] from inputFloat32Array or fixedWeights.
   *
   * If ( channelMultiplier == null ) and ( outChannels != null ), extract 5 parameters [ dilationHeight,
   * dilationWidth, filterHeight, filterWidth, channelMultiplier ] from inputFloat32Array or fixedWeights.
   *
   * If ( channelMultiplier != null ) and ( outChannels == null ), extract 5 parameters [ dilationHeight,
   * dilationWidth, filterHeight, filterWidth, outChannels ] from inputFloat32Array or fixedWeights.
   *
   * If ( channelMultiplier == null ) and ( outChannels == null ), extract 6 parameters [ dilationHeight,
   * dilationWidth, filterHeight, filterWidth, channelMultiplier, outChannels ] from inputFloat32Array
   * or fixedWeights.
   *
   * @return {boolean} Return false, if initialization failed.
   *
   * @override
   */
  init( inputFloat32Array, byteOffsetBegin, inChannels, channelMultiplier = null, outChannels = null, fixedWeights = null ) {

//!!! ...unfinished...
// pad mode ?
// inverted residual connection (by add or by concatenate) ? (dense net)
// squeeze-and-excitation ?
// Shuffled Grouped Pointwise Convolution ... ? (by tf.gather() ?)

    // Except channelMultiplier and outChannels, there are 4 parameters need to be extract and convert (to positive integer).
    let parameterCountAtLeast = 4;

    return super.init(
      inputFloat32Array, byteOffsetBegin, parameterCountAtLeast, inChannels, channelMultiplier, outChannels, fixedWeights );
  }

  get dilationHeight()    { return this.weightsModified[ 0 ]; }
  get dilationWidth()     { return this.weightsModified[ 1 ]; }
  get filterHeight()      { return this.weightsModified[ 2 ]; }
  get filterWidth()       { return this.weightsModified[ 3 ]; }
}

/**
 * A CNN layer contains one params (this.params) and three filters: depthwise, pointwise and bias.
 */
class Layer {

  /**
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpret as weights.
   *
   * @param {number}       byteOffsetBegin
   *   The position to start to decode from the inputFloat32Array. This is relative to the inputFloat32Array.buffer
   * (not to the inputFloat32Array.byteOffset).
   *
   * @param {number}       inChannels
   *   The input channel count.
   *
   * @param {Array} fixedParams
   *   If null, extract 6 parameters from inputFloat32Array. If not null, extract 6 parameters from it instead of
   * inputFloat32Array. If not null, it should have 6 elements: [ filterHeight, filterWidth, channelMultiplier,
   * dilationHeight, dilationWidth, outChannels ].
   */
  constructor( inputFloat32Array, byteOffsetBegin, inChannels, fixedParams = null ) {

    this.params = new Params( inputFloat32Array, byteOffsetBegin, fixedParams );
    if ( !this.params.isValid() )
      return;

    this.depthwise = new Weights.Base(
      inputFloat32Array, this.params.defaultByteOffsetEnd, null, 0,
      [this.params.filterHeight, this.params.filterWidth, inChannels, this.params.channelMultiplier] );

    if ( !this.depthwise.isValid() )
      return;

    this.depthwiseBias = new Weights.Base(
      inputFloat32Array, this.depthwise.defaultByteOffsetEnd, null, 0,
      [1, 1, inChannels, this.params.channelMultiplier] );

    if ( !this.depthwiseBias.isValid() )
      return;

    this.pointwise = new Weights.Base(
      inputFloat32Array, this.depthwiseBias.defaultByteOffsetEnd, null, 0,
      [1, 1, inChannels * this.params.channelMultiplier, this.params.outChannels] );

    if ( !this.pointwise.isValid() )
      return;

    this.pointwiseBias = new Weights.Base(
      inputFloat32Array, this.pointwise.defaultByteOffsetEnd, null, 0,
      [1, 1, this.params.outChannels] );
  }

  isValid() {
    if ( this.pointwiseBias )
      if ( this.pointwiseBias.isValid() )
        return true;
    return false;
  }

  get byteOffsetBegin() { return this.params.byteOffsetBegin; }
  get byteOffsetEnd()   { return this.pointwiseBias.byteOffsetEnd; }
}

/** Aggregate all progress about downloading, JSON parsing, characters scanning, and weights scanning.  */
class Progress extends ValueMax.Percentage.Aggregate {
  constructor() {
    let children = [
      new ValueMax.Percentage.Concrete(), // Increased when downloading from network.
      new ValueMax.Percentage.Concrete(), // Increased when parsing the downloaded data as JSON.
      new ValueMax.Percentage.Concrete(), // Increased when converting characters to weights.
      new ValueMax.Percentage.Concrete()  // Increased when converting weights to layers.
    ];

    super(children);

    [this.download, this.JSONParse, this.CharCount, this.WeightByteCount] = children;
  }
}

/**
 * Parsing and decoding string array to SeparableConv2d entities.
 *
 * @param  {string[]} encodedStringArray     Every string is an encoded entity.
 * @param  {number}   encodedWeightCharCount Every weight is encoded as string with this length. (e.g. 5 )
 * @param  {number}   encodedWeightBase      Every weight is encoded by this base number. (e.g. 2 or 10 or 16 or 36)
 * @param  {number}   weightValueOffset      The value will be subtracted from the integer weight value.
 * @param  {number}   weightValueDivisor     Divide the integer weight value by this value for converting to floating-point number.
 *
 * @param  {string}   htmlProgressTitle
 *   The title of HTMLProgressElement for reporting progress and onclick will be called when done. If null, no reporting.
 *
 * @return {Promise} A promise resolved as an Object[] which is the decoded entity for separableConv2d().
 *   The entity is an array of SeparableConv2d.Layer.
 */
function StringArrayToEntities(
  encodedStringArray, encodedWeightCharCount, encodedWeightBase, weightValueOffset, weightValueDivisor, htmlProgressTitle) {

  encodedWeightCharCount = Math.max(1, encodedWeightCharCount); /* At least one character for a weight. */

  const suspendWeightCount = 1000; /* Everytime so many weights decoded, yield for releasing CPU time. */
  const suspendLayerCount = 100;   /* Everytime so many layers created, yield for releasing CPU time. */

  /**
   * @param {integer} integerWeight The integer which will be converted to floating-point number by subtract and divide.
   * @return The floating-point number.
   */
  function integerToFloat(integerWeight) {
    return ( integerWeight - weightValueOffset ) / weightValueDivisor;
  }

  /** Input encodedString, yield progress, return integerWeights.
   *
   * @param {ValueMax.Percentage.Aggregate} progressToYield
   *   Return this when every time yield. Usually, this is the container of the progressToAdvance.
   *
   * @param {ValueMax.Percentage.Concrete}  progressToAdvance
   *   Increase this when every time advanced. It will be initialized to zero when decoder starting.
   */
  function* weightGenerator(encodedString, progressToYield, progressToAdvance) {
    let encodedWeightCount = Math.ceil(encodedString.length / encodedWeightCharCount);
    let integerWeights = new Float32Array( encodedWeightCount );

    let collectedCharCount = 0;
    let encodedWeight;
    let weightIndex = 0;
    let weightIndexAfterYield = 0;

    progressToAdvance.accumulation = 0;

    for (let encodedChar of encodedString) {
      progressToAdvance.accumulation++;

      if (0 == collectedCharCount)
        encodedWeight = encodedChar;
      else
        encodedWeight += encodedChar;  // String concatenate.

      collectedCharCount++;
      if (collectedCharCount < encodedWeightCharCount)
        continue; // Collect characters for one weight.

      collectedCharCount = 0;

     // Decode as integer, and then convert to floating-point number.
      integerWeights[ weightIndex ] = integerToFloat( parseInt(encodedWeight, encodedWeightBase) );
      weightIndex++;
      weightIndexAfterYield++;

      if (weightIndexAfterYield >= suspendWeightCount) { // Every suspendWeightCount, release CPU time.
        yield progressToYield;
        weightIndexAfterYield = 0;
      }
    }

    yield progressToYield; // After weights of one entity converted to integer, release CPU time.
    return integerWeights;
  }

  /** Input integerWeights, yield progress, return entity.
   *
   * @param {ValueMax.Percentage.Aggregate} progressToYield
   *   Return this when every time yield. Usually, this is the container of the progressToAdvance.
   *
   * @param {ValueMax.Percentage.Concrete}  progressToAdvance
   *   Increase this when every time advanced. It will be initialized to zero when decoder starting.
   */
  function* entityGenerator(integerWeights, progressToYield, progressToAdvance) {
    let layerCountAfterYield = 0;

    progressToAdvance.accumulation = 0;

    let byteOffset = integerWeights.byteOffset; // Bounded by the integerWeights.
    let entity = [], inChannels = 4;            // Suppose the first layer's input channel count is always RGBA 4 channels.

    let legalByteOffsetEnd = integerWeights.byteOffset + integerWeights.byteLength;
    while ( byteOffset < legalByteOffsetEnd ) {

      let layer = new Layer( integerWeights, byteOffset, inChannels );
      if (layer.isValid()) {	// Only collect valid layer.
        entity.push(layer);
        progressToAdvance.accumulation += layer.weightByteCount;
        inChannels = layer.params.outChannels;  // The next layer's input channel count is the previous layer's output channel count.
        byteOffset = layer.byteOffsetEnd;
      } else { // Discard invalid layer. Progress skip to the end of the weight array.
        progressToAdvance.accumulation += ( integerWeights.byteLength - byteOffset );
        byteOffset = legalByteOffsetEnd; // For stopping the loop.
      }

      layerCountAfterYield++; // Count even if layer invalid, because CPU time is still used.

      if (layerCountAfterYield >= suspendLayerCount) { // Every suspendLayerCount, release CPU time.
        yield progressToYield;
        layerCountAfterYield = 0;
      }
    }

    yield progressToYield; // After weights of one entity converted to layers, release CPU time.
    return entity;
  }

  /** Input encodedStringArray, yield progress, return entities. */
  function* entitiesGenerator() {

    let progress = new Progress();

    /* Estimate maximum volume for progress reporting. Parsing has two pass: one for converting characters to weight,
       another for converting weights to layer. So the total volume is sum of these two. */
    for (let encodedString of encodedStringArray) {
      progress.CharCount.total += encodedString.length;
      let encodedWeightByteCount = Math.ceil(encodedString.length / encodedWeightCharCount) * Float32Array.BYTES_PER_ELEMENT;
      progress.WeightByteCount.total += encodedWeightByteCount;
    }

    let entityCount = encodedStringArray.length;
    let entities =    new Array(entityCount);

    yield progress; /* Report initial progress after maximum volume calculated and first memory block allocated. */

    for (let i = 0; i < encodedStringArray.length; i++) {
      let encodedString = encodedStringArray[ i ];
      let integerWeights = yield* weightGenerator(encodedString, progress, progress.CharCount);
      let entity = yield* entityGenerator(integerWeights, progress, progress.WeightByteCount);
      entities[ i ] = entity;
    }
    return entities;
  }

  let progressReceiver = ValueMax.Receiver.HTMLProgress.createByTitle_or_getDummy(this.htmlProgressTitle);
  let theEntitiesGenerator = entitiesGenerator(encodedStringArray);
  let p = PartTime.forOf(theEntitiesGenerator, (valueMax) => {
    progressReceiver.setValueMax(valueMax); /* Report progress to UI. */
  }).then((doneValue) => {
    progressReceiver.informDone(doneValue); /* Inform UI progress done. */
    return doneValue; /* The doneValue will be the entities */
  });
  return p;
}
<commit_msg>Update SeparableConv2D.js<commit_after>import * as Weights from "./Weights.js";
import * as PartTime from "./PartTime.js";
import * as ValueMax from "./ValueMax.js";

export { Params, Layer, StringArrayToEntities };

/**
 * CNN separable convolution (2D) layer parameters.
 */
class Params extends Weights.Params {

  /**
   * If ( channelMultiplier != null ) and ( outChannels != null ), extract 4 parameters [ dilationHeight,
   * dilationWidth, filterHeight, filterWidth ] from inputFloat32Array or fixedWeights.
   *
   * If ( channelMultiplier == null ) and ( outChannels != null ), extract 5 parameters [ dilationHeight,
   * dilationWidth, filterHeight, filterWidth, channelMultiplier ] from inputFloat32Array or fixedWeights.
   *
   * If ( channelMultiplier != null ) and ( outChannels == null ), extract 5 parameters [ dilationHeight,
   * dilationWidth, filterHeight, filterWidth, outChannels ] from inputFloat32Array or fixedWeights.
   *
   * If ( channelMultiplier == null ) and ( outChannels == null ), extract 6 parameters [ dilationHeight,
   * dilationWidth, filterHeight, filterWidth, channelMultiplier, outChannels ] from inputFloat32Array
   * or fixedWeights.
   *
   * @return {boolean} Return false, if initialization failed.
   *
   * @override
   */
  init( inputFloat32Array, byteOffsetBegin, inChannels, channelMultiplier = null, outChannels = null, fixedWeights = null ) {

//!!! ...unfinished...
// pad mode ?
// inverted residual connection (by add or by concatenate) ? (dense net)
// squeeze-and-excitation ?
// Shuffled Grouped Pointwise Convolution ... ? (by tf.gather() ?)

    // Except channelMultiplier and outChannels, there are 4 parameters [ dilationHeight, dilationWidth, filterHeight,
    // filterWidth ] need to be extract and convert (to positive integer).
    let parameterCountAtLeast = 4;

    return super.init(
      inputFloat32Array, byteOffsetBegin, parameterCountAtLeast, inChannels, channelMultiplier, outChannels, fixedWeights );
  }

  get dilationHeight()    { return this.weightsModified[ 0 ]; }
  get dilationWidth()     { return this.weightsModified[ 1 ]; }
  get filterHeight()      { return this.weightsModified[ 2 ]; }
  get filterWidth()       { return this.weightsModified[ 3 ]; }
}

/**
 * A CNN layer contains one params (this.params) and three filters: depthwise, pointwise and bias.
 */
class Layer {

  /**
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpret as weights.
   *
   * @param {number}       byteOffsetBegin
   *   The position to start to decode from the inputFloat32Array. This is relative to the inputFloat32Array.buffer
   * (not to the inputFloat32Array.byteOffset).
   *
   * @param {number}       inChannels
   *   The input channel count.
   *
   * @param {Array} fixedParams
   *   If null, extract 6 parameters from inputFloat32Array. If not null, extract 6 parameters from it instead of
   * inputFloat32Array. If not null, it should have 6 elements: [ filterHeight, filterWidth, channelMultiplier,
   * dilationHeight, dilationWidth, outChannels ].
   */
  constructor( inputFloat32Array, byteOffsetBegin, inChannels, fixedParams = null ) {

    this.params = new Params( inputFloat32Array, byteOffsetBegin, fixedParams );
    if ( !this.params.isValid() )
      return;

    this.depthwise = new Weights.Base(
      inputFloat32Array, this.params.defaultByteOffsetEnd, null, 0,
      [this.params.filterHeight, this.params.filterWidth, inChannels, this.params.channelMultiplier] );

    if ( !this.depthwise.isValid() )
      return;

    this.depthwiseBias = new Weights.Base(
      inputFloat32Array, this.depthwise.defaultByteOffsetEnd, null, 0,
      [1, 1, inChannels, this.params.channelMultiplier] );

    if ( !this.depthwiseBias.isValid() )
      return;

    this.pointwise = new Weights.Base(
      inputFloat32Array, this.depthwiseBias.defaultByteOffsetEnd, null, 0,
      [1, 1, inChannels * this.params.channelMultiplier, this.params.outChannels] );

    if ( !this.pointwise.isValid() )
      return;

    this.pointwiseBias = new Weights.Base(
      inputFloat32Array, this.pointwise.defaultByteOffsetEnd, null, 0,
      [1, 1, this.params.outChannels] );
  }

  isValid() {
    if ( this.pointwiseBias )
      if ( this.pointwiseBias.isValid() )
        return true;
    return false;
  }

  get byteOffsetBegin() { return this.params.defaultByteOffsetBegin; }
  get byteOffsetEnd()   { return this.pointwiseBias.defaultByteOffsetEnd; }
}

/** Aggregate all progress about downloading, JSON parsing, characters scanning, and weights scanning.  */
class Progress extends ValueMax.Percentage.Aggregate {
  constructor() {
    let children = [
      new ValueMax.Percentage.Concrete(), // Increased when downloading from network.
      new ValueMax.Percentage.Concrete(), // Increased when parsing the downloaded data as JSON.
      new ValueMax.Percentage.Concrete(), // Increased when converting characters to weights.
      new ValueMax.Percentage.Concrete()  // Increased when converting weights to layers.
    ];

    super(children);

    [this.download, this.JSONParse, this.CharCount, this.WeightByteCount] = children;
  }
}

/**
 * Parsing and decoding string array to SeparableConv2d entities.
 *
 * @param  {string[]} encodedStringArray     Every string is an encoded entity.
 * @param  {number}   encodedWeightCharCount Every weight is encoded as string with this length. (e.g. 5 )
 * @param  {number}   encodedWeightBase      Every weight is encoded by this base number. (e.g. 2 or 10 or 16 or 36)
 * @param  {number}   weightValueOffset      The value will be subtracted from the integer weight value.
 * @param  {number}   weightValueDivisor     Divide the integer weight value by this value for converting to floating-point number.
 *
 * @param  {string}   htmlProgressTitle
 *   The title of HTMLProgressElement for reporting progress and onclick will be called when done. If null, no reporting.
 *
 * @return {Promise} A promise resolved as an Object[] which is the decoded entity for separableConv2d().
 *   The entity is an array of SeparableConv2d.Layer.
 */
function StringArrayToEntities(
  encodedStringArray, encodedWeightCharCount, encodedWeightBase, weightValueOffset, weightValueDivisor, htmlProgressTitle) {

  encodedWeightCharCount = Math.max(1, encodedWeightCharCount); /* At least one character for a weight. */

  const suspendWeightCount = 1000; /* Everytime so many weights decoded, yield for releasing CPU time. */
  const suspendLayerCount = 100;   /* Everytime so many layers created, yield for releasing CPU time. */

  /**
   * @param {integer} integerWeight The integer which will be converted to floating-point number by subtract and divide.
   * @return The floating-point number.
   */
  function integerToFloat(integerWeight) {
    return ( integerWeight - weightValueOffset ) / weightValueDivisor;
  }

  /** Input encodedString, yield progress, return integerWeights.
   *
   * @param {ValueMax.Percentage.Aggregate} progressToYield
   *   Return this when every time yield. Usually, this is the container of the progressToAdvance.
   *
   * @param {ValueMax.Percentage.Concrete}  progressToAdvance
   *   Increase this when every time advanced. It will be initialized to zero when decoder starting.
   */
  function* weightGenerator(encodedString, progressToYield, progressToAdvance) {
    let encodedWeightCount = Math.ceil(encodedString.length / encodedWeightCharCount);
    let integerWeights = new Float32Array( encodedWeightCount );

    let collectedCharCount = 0;
    let encodedWeight;
    let weightIndex = 0;
    let weightIndexAfterYield = 0;

    progressToAdvance.accumulation = 0;

    for (let encodedChar of encodedString) {
      progressToAdvance.accumulation++;

      if (0 == collectedCharCount)
        encodedWeight = encodedChar;
      else
        encodedWeight += encodedChar;  // String concatenate.

      collectedCharCount++;
      if (collectedCharCount < encodedWeightCharCount)
        continue; // Collect characters for one weight.

      collectedCharCount = 0;

     // Decode as integer, and then convert to floating-point number.
      integerWeights[ weightIndex ] = integerToFloat( parseInt(encodedWeight, encodedWeightBase) );
      weightIndex++;
      weightIndexAfterYield++;

      if (weightIndexAfterYield >= suspendWeightCount) { // Every suspendWeightCount, release CPU time.
        yield progressToYield;
        weightIndexAfterYield = 0;
      }
    }

    yield progressToYield; // After weights of one entity converted to integer, release CPU time.
    return integerWeights;
  }

  /** Input integerWeights, yield progress, return entity.
   *
   * @param {ValueMax.Percentage.Aggregate} progressToYield
   *   Return this when every time yield. Usually, this is the container of the progressToAdvance.
   *
   * @param {ValueMax.Percentage.Concrete}  progressToAdvance
   *   Increase this when every time advanced. It will be initialized to zero when decoder starting.
   */
  function* entityGenerator(integerWeights, progressToYield, progressToAdvance) {
    let layerCountAfterYield = 0;

    progressToAdvance.accumulation = 0;

    let byteOffset = integerWeights.byteOffset; // Bounded by the integerWeights.
    let entity = [], inChannels = 4;            // Suppose the first layer's input channel count is always RGBA 4 channels.

    let legalByteOffsetEnd = integerWeights.byteOffset + integerWeights.byteLength;
    while ( byteOffset < legalByteOffsetEnd ) {

      let layer = new Layer( integerWeights, byteOffset, inChannels );
      if (layer.isValid()) {	// Only collect valid layer.
        entity.push(layer);
        progressToAdvance.accumulation += layer.weightByteCount;
        inChannels = layer.params.outChannels;  // The next layer's input channel count is the previous layer's output channel count.
        byteOffset = layer.byteOffsetEnd;
      } else { // Discard invalid layer. Progress skip to the end of the weight array.
        progressToAdvance.accumulation += ( integerWeights.byteLength - byteOffset );
        byteOffset = legalByteOffsetEnd; // For stopping the loop.
      }

      layerCountAfterYield++; // Count even if layer invalid, because CPU time is still used.

      if (layerCountAfterYield >= suspendLayerCount) { // Every suspendLayerCount, release CPU time.
        yield progressToYield;
        layerCountAfterYield = 0;
      }
    }

    yield progressToYield; // After weights of one entity converted to layers, release CPU time.
    return entity;
  }

  /** Input encodedStringArray, yield progress, return entities. */
  function* entitiesGenerator() {

    let progress = new Progress();

    /* Estimate maximum volume for progress reporting. Parsing has two pass: one for converting characters to weight,
       another for converting weights to layer. So the total volume is sum of these two. */
    for (let encodedString of encodedStringArray) {
      progress.CharCount.total += encodedString.length;
      let encodedWeightByteCount = Math.ceil(encodedString.length / encodedWeightCharCount) * Float32Array.BYTES_PER_ELEMENT;
      progress.WeightByteCount.total += encodedWeightByteCount;
    }

    let entityCount = encodedStringArray.length;
    let entities =    new Array(entityCount);

    yield progress; /* Report initial progress after maximum volume calculated and first memory block allocated. */

    for (let i = 0; i < encodedStringArray.length; i++) {
      let encodedString = encodedStringArray[ i ];
      let integerWeights = yield* weightGenerator(encodedString, progress, progress.CharCount);
      let entity = yield* entityGenerator(integerWeights, progress, progress.WeightByteCount);
      entities[ i ] = entity;
    }
    return entities;
  }

  let progressReceiver = ValueMax.Receiver.HTMLProgress.createByTitle_or_getDummy(this.htmlProgressTitle);
  let theEntitiesGenerator = entitiesGenerator(encodedStringArray);
  let p = PartTime.forOf(theEntitiesGenerator, (valueMax) => {
    progressReceiver.setValueMax(valueMax); /* Report progress to UI. */
  }).then((doneValue) => {
    progressReceiver.informDone(doneValue); /* Inform UI progress done. */
    return doneValue; /* The doneValue will be the entities */
  });
  return p;
}
<|endoftext|>