<commit_before>// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

// This is the main application configuration file.  It is a Grunt
// configuration file, which you can learn more about here:
// https://github.com/cowboy/grunt/blob/master/docs/configuring.md

module.exports = function(grunt) {
  var helper = require('./tasks/helper').init(grunt),
  _ = grunt.util._,
  path = require('path');

  var couch_config = function () {

    var default_couch_config = {
      fauxton: {
        db: 'http://localhost:5984/fauxton',
        app: './couchapp.js',
        options: {
          okay_if_missing: true
        }
      }
    };

    var settings_couch_config = helper.readSettingsFile().couch_config;
    return settings_couch_config || default_couch_config;
  }();

  var cleanableAddons =  function () {
    var theListToClean = [];
    helper.processAddons(function(addon){
      // Only clean addons that are included from a local dir
      if (addon.path){
        theListToClean.push("app/addons/" + addon.name);
      }
    });

    return theListToClean;
  }();

  var cleanable = function(){
    // Whitelist files and directories to be cleaned
    // You'll always want to clean these two directories
    // Now find the external addons you have and add them for cleaning up
    return _.union(["dist/", "app/load_addons.js"], cleanableAddons);
  }();

  var assets = function(){
    // Base assets
    var theAssets = {
      less:{
        paths: ["assets/less"],
        files: {
          "dist/debug/css/fauxton.css": "assets/less/fauxton.less"
        }
      },
      img: ["assets/img/**"]
    };
    helper.processAddons(function(addon){
      // Less files from addons
      var root = addon.path || "app/addons/" + addon.name;
      var lessPath = root + "/assets/less";
      if(path.existsSync(lessPath)){
        // .less files exist for this addon
        theAssets.less.paths.push(lessPath);
        theAssets.less.files["dist/debug/css/" + addon.name + ".css"] =
          lessPath + "/" + addon.name + ".less";
      }
      // Images
      root = addon.path || "app/addons/" + addon.name;
      var imgPath = root + "/assets/img";
      if(path.existsSync(imgPath)){
        theAssets.img.push(imgPath + "/**");
      }
    });
    return theAssets;
  }();

  var templateSettings = function(){
    var defaultSettings = {
     "development": {
        "src": "assets/index.underscore",
        "dest": "dist/debug/index.html",
        "variables": {
          "requirejs": "/assets/js/libs/require.js",
          "css": "./css/index.css",
          "base": null
        }
      },
      "release": {
        "src": "assets/index.underscore",
        "dest": "dist/debug/index.html",
        "variables": {
          "requirejs": "./js/require.js",
          "css": "./css/index.css",
          "base": null
        }
      }
    };

    var settings = helper.readSettingsFile();
    return settings.template || defaultSettings;
  }();

  var couchserver_config  = function () {
    // add a "couchserver" key to settings.json with JSON that matches the
    // keys and values below (plus your customizations) to have Fauxton work
    // against a remote CouchDB-compatible server.
    var defaults = {
      dist: './dist/debug/',
      port: 8000,
      proxy: {
        target: {
          host: 'localhost',
          port: 5984,
          https: false
        },
        // This sets the Host header in the proxy so that you can use external
        // CouchDB instances and not have the Host set to 'localhost'
        changeOrigin: true
      }
    };

    return helper.readSettingsFile().couchserver || defaults;
  }();

  grunt.initConfig({

    // The clean task ensures all files are removed from the dist/ directory so
    // that no files linger from previous builds.
    clean: {
      release:  cleanable,
      watch: cleanableAddons
    },

    // The lint task will run the build configuration and the application
    // JavaScript through JSHint and report any errors.  You can change the
    // options for this task, by reading this:
    // https://github.com/cowboy/grunt/blob/master/docs/task_lint.md
    lint: {
      files: [
        "build/config.js", "app/**/*.js"
      ]
    },

    less: {
      compile: {
        options: {
          paths: assets.less.paths
        },
        files: assets.less.files
      }
    },

    // The jshint option for scripturl is set to lax, because the anchor
    // override inside main.js needs to test for them so as to not accidentally
    // route. Settings expr true so we can do `migtBeNullObject && mightBeNullObject.coolFunction()`
    jshint: {
      all: ['app/**/*.js', 'Gruntfile.js', "test/core/*.js"],
      options: {
        scripturl: true,
        evil: true,
        expr: true
      }
    },

    // The jst task compiles all application templates into JavaScript
    // functions with the underscore.js template function from 1.2.4.  You can
    // change the namespace and the template options, by reading this:
    // https://github.com/gruntjs/grunt-contrib/blob/master/docs/jst.md
    //
    // The concat task depends on this file to exist, so if you decide to
    // remove this, ensure concat is updated accordingly.
    jst: {
      compile: {
        options: {
          processContent: function(src) {
            return src.replace(/<!--[\s\S]*?-->/gm, '');
          }
        },
        files: {
          "dist/debug/templates.js": [
            "app/templates/**/*.html",
            "app/addons/**/templates/**/*.html"
          ]
        }
      }
    },

    template: templateSettings,

    // The concatenate task is used here to merge the almond require/define
    // shim and the templates into the application code.  It's named
    // dist/debug/require.js, because we want to only load one script file in
    // index.html.
    concat: {
      requirejs: {
        src: [ "assets/js/libs/require.js", "dist/debug/templates.js", "dist/debug/require.js"],
        dest: "dist/debug/js/require.js"
      },

      index_css: {
        src: ["dist/debug/css/*.css", 'assets/css/*.css'],
        dest: 'dist/debug/css/index.css'
      },

      test_config_js: {
        src: ["dist/debug/templates.js", "test/test.config.js"],
        dest: 'test/test.config.js'
      },
    },

    cssmin: {
      compress: {
        files: {
          "dist/release/css/index.css": [
            "dist/debug/css/index.css", 'assets/css/*.css',
            "app/addons/**/assets/css/*.css"
          ]
        },
        options: {
          report: 'min'
        }
      }
    },

    uglify: {
      release: {
        files: {
          "dist/release/js/require.js": [
            "dist/debug/js/require.js"
          ]
        }
      }
    },

    // Runs a proxy server for easier development, no need to keep deploying to couchdb
    couchserver: couchserver_config,

    watch: {
      js: {
        files: helper.watchFiles(['.js'], ["./app/**/*.js", '!./app/load_addons.js',"./assets/**/*.js", "./test/**/*.js"]),
        tasks: ['watchRun'],
      },
      style: {
        files: helper.watchFiles(['.less','.css'],["./app/**/*.css","./app/**/*.less","./assets/**/*.css", "./assets/**/*.less"]),
        tasks: ['clean:watch', 'dependencies','less', 'concat:index_css'],
      },
      html: {
        // the index.html is added in as a dummy file incase there is no
        // html dependancies this will break. So we need one include pattern
        files: helper.watchFiles(['.html'], ['./index.html']),
        tasks: ['clean:watch', 'dependencies']
      },
      options: {
        nospawn: true,
        debounceDelay: 500
      }
    },

    requirejs: {
      compile: {
        options: {
          baseUrl: 'app',
          // Include the main configuration file.
          mainConfigFile: "app/config.js",

          // Output file.
          out: "dist/debug/require.js",

          // Root application module.
          name: "config",

          // Do not wrap everything in an IIFE.
          wrap: false,
          optimize: "none",
          findNestedDependencies: true
        }
      }
    },

    // Copy build artifacts and library code into the distribution
    // see - http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically
    copy: {
      couchdb: {
        files: [
          // this gets built in the template task
          {src: "dist/release/index.html", dest: "../../share/www/fauxton/index.html"},
          {src: ["**"], dest: "../../share/www/fauxton/js/", cwd:'dist/release/js/',  expand: true},
          {src: ["**"], dest: "../../share/www/fauxton/img/", cwd:'dist/release/img/', expand: true},
          {src: ["**"], dest: "../../share/www/fauxton/css/", cwd:"dist/release/css/", expand: true}
        ]
      },
      couchdebug: {
        files: [
          // this gets built in the template task
          {src: "dist/debug/index.html", dest: "../../share/www/fauxton/index.html"},
          {src: ["**"], dest: "../../share/www/fauxton/js/", cwd:'dist/debug/js/',  expand: true},
          {src: ["**"], dest: "../../share/www/fauxton/img/", cwd:'dist/debug/img/', expand: true},
          {src: ["**"], dest: "../../share/www/fauxton/css/", cwd:"dist/debug/css/", expand: true}
        ]
      },
      ace: {
        files: [
          {src: "assets/js/libs/ace/worker-json.js", dest: "dist/release/js/ace/worker-json.js"},
          {src: "assets/js/libs/ace/mode-json.js", dest: "dist/release/js/ace/mode-json.js"},
          {src: "assets/js/libs/ace/theme-crimson_editor.js", dest: "dist/release/js/ace/theme-crimson_editor.js"},
          {src: "assets/js/libs/ace/mode-javascript.js", dest: "dist/release/js/ace/mode-javascript.js"},
          {src: "assets/js/libs/ace/worker-javascript.js", dest: "dist/release/js/ace/worker-javascript.js"},
        ]
      },

      dist:{
        files:[
          {src: "dist/debug/index.html", dest: "dist/release/index.html"},
          {src: assets.img, dest: "dist/release/img/", flatten: true, expand: true}
        ]
      },
      debug:{
        files:[
          {src: assets.img, dest: "dist/debug/img/", flatten: true, expand: true}
        ]
      }
    },

    get_deps: {
      "default": {
        src: "settings.json"
      }
    },

    gen_load_addons: {
      "default": {
        src: "settings.json"
      }
    },
    gen_initialize: templateSettings,
    /*gen_initialize: {
      "default": {
        src: "settings.json"
      }
    },*/

    mkcouchdb: couch_config,
    rmcouchdb: couch_config,
    couchapp: couch_config,

    mochaSetup: {
      default: {
        files: { src: helper.watchFiles(['[Ss]pec.js'], ['./test/core/**/*[Ss]pec.js', './app/**/*[Ss]pec.js'])},
        template: 'test/test.config.underscore',
        config: './app/config.js'
      }
    },

    mocha_phantomjs: {
      all: ['test/runner.html']
    }

  });

  // on watch events configure jshint:all to only run on changed file
  grunt.event.on('watch', function(action, filepath) {
    if (!!filepath.match(/.js$/) && filepath.indexOf('test.config.js') === -1) {
      grunt.config(['jshint', 'all'], filepath);
    }

    if (!!filepath.match(/[Ss]pec.js$/)) {
      //grunt.task.run(['mochaSetup','jst', 'concat:test_config_js', 'mocha_phantomjs']);
    }
  });

  /*
   * Load Grunt plugins
   */
  // Load fauxton specific tasks
  grunt.loadTasks('tasks');
  // Load the couchapp task
  grunt.loadNpmTasks('grunt-couchapp');
  // Load the copy task
  grunt.loadNpmTasks('grunt-contrib-watch');
  // Load the exec task
  grunt.loadNpmTasks('grunt-exec');
  // Load Require.js task
  grunt.loadNpmTasks('grunt-contrib-requirejs');
  // Load Copy task
  grunt.loadNpmTasks('grunt-contrib-copy');
  // Load Clean task
  grunt.loadNpmTasks('grunt-contrib-clean');
  // Load jshint task
  grunt.loadNpmTasks('grunt-contrib-jshint');
  // Load jst task
  grunt.loadNpmTasks('grunt-contrib-jst');
  // Load less task
  grunt.loadNpmTasks('grunt-contrib-less');
  // Load concat task
  grunt.loadNpmTasks('grunt-contrib-concat');
  // Load UglifyJS task
  grunt.loadNpmTasks('grunt-contrib-uglify');
  // Load CSSMin task
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-mocha-phantomjs');

  /*
   * Default task
   */
  // defult task - install minified app to local CouchDB
  grunt.registerTask('default', 'couchdb');

  /*
   * Transformation tasks
   */
  // clean out previous build artefactsa and lint
  grunt.registerTask('lint', ['clean', 'jshint']);
  grunt.registerTask('test', ['lint', 'dependencies', 'test_inline']);
  // lighter weight test task for use inside dev/watch
  grunt.registerTask('test_inline', ['mochaSetup','jst', 'concat:test_config_js', 'mocha_phantomjs']);
  // Fetch dependencies (from git or local dir), lint them and make load_addons
  grunt.registerTask('dependencies', ['get_deps', 'gen_load_addons:default']);
  // build templates, js and css
  grunt.registerTask('build', ['less', 'concat:index_css', 'jst', 'requirejs', 'concat:requirejs', 'template:release']);
  // minify code and css, ready for release.
  grunt.registerTask('minify', ['uglify', 'cssmin:compress']);

  /*
   * Build the app in either dev, debug, or release mode
   */
  // dev server
  grunt.registerTask('dev', ['debugDev', 'couchserver']);
  // build a debug release
  grunt.registerTask('debug', ['lint', 'dependencies', "gen_initialize:development", 'concat:requirejs','less', 'concat:index_css', 'template:development', 'copy:debug']);
  grunt.registerTask('debugDev', ['clean', 'dependencies', "gen_initialize:development",'jshint','less', 'concat:index_css', 'template:development', 'copy:debug']);

  grunt.registerTask('watchRun', ['clean:watch', 'dependencies', 'jshint']);
  // build a release
  grunt.registerTask('release', ['clean' ,'dependencies', "gen_initialize:release", 'jshint', 'build', 'minify', 'copy:dist', 'copy:ace']);

  /*
   * Install into CouchDB in either debug, release, or couchapp mode
   */
  // make a development install that is server by mochiweb under _utils
  grunt.registerTask('couchdebug', ['debug', 'copy:couchdebug']);
  // make a minimized install that is server by mochiweb under _utils
  grunt.registerTask('couchdb', ['release', 'copy:couchdb']);
  // make an install that can be deployed as a couchapp
  grunt.registerTask('couchapp_setup', ['release']);
  // install fauxton as couchapp
  grunt.registerTask('couchapp_install', ['rmcouchdb:fauxton', 'mkcouchdb:fauxton', 'couchapp:fauxton']);
  // setup and install fauxton as couchapp
  grunt.registerTask('couchapp_deploy', ['couchapp_setup', 'couchapp_install']);
};
<commit_msg>ignoring index.css in concat:index_css<commit_after>// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

// This is the main application configuration file.  It is a Grunt
// configuration file, which you can learn more about here:
// https://github.com/cowboy/grunt/blob/master/docs/configuring.md

module.exports = function(grunt) {
  var helper = require('./tasks/helper').init(grunt),
  _ = grunt.util._,
  path = require('path');

  var couch_config = function () {

    var default_couch_config = {
      fauxton: {
        db: 'http://localhost:5984/fauxton',
        app: './couchapp.js',
        options: {
          okay_if_missing: true
        }
      }
    };

    var settings_couch_config = helper.readSettingsFile().couch_config;
    return settings_couch_config || default_couch_config;
  }();

  var cleanableAddons =  function () {
    var theListToClean = [];
    helper.processAddons(function(addon){
      // Only clean addons that are included from a local dir
      if (addon.path){
        theListToClean.push("app/addons/" + addon.name);
      }
    });

    return theListToClean;
  }();

  var cleanable = function(){
    // Whitelist files and directories to be cleaned
    // You'll always want to clean these two directories
    // Now find the external addons you have and add them for cleaning up
    return _.union(["dist/", "app/load_addons.js"], cleanableAddons);
  }();

  var assets = function(){
    // Base assets
    var theAssets = {
      less:{
        paths: ["assets/less"],
        files: {
          "dist/debug/css/fauxton.css": "assets/less/fauxton.less"
        }
      },
      img: ["assets/img/**"]
    };
    helper.processAddons(function(addon){
      // Less files from addons
      var root = addon.path || "app/addons/" + addon.name;
      var lessPath = root + "/assets/less";
      if(path.existsSync(lessPath)){
        // .less files exist for this addon
        theAssets.less.paths.push(lessPath);
        theAssets.less.files["dist/debug/css/" + addon.name + ".css"] =
          lessPath + "/" + addon.name + ".less";
      }
      // Images
      root = addon.path || "app/addons/" + addon.name;
      var imgPath = root + "/assets/img";
      if(path.existsSync(imgPath)){
        theAssets.img.push(imgPath + "/**");
      }
    });
    return theAssets;
  }();

  var templateSettings = function(){
    var defaultSettings = {
     "development": {
        "src": "assets/index.underscore",
        "dest": "dist/debug/index.html",
        "variables": {
          "requirejs": "/assets/js/libs/require.js",
          "css": "./css/index.css",
          "base": null
        }
      },
      "release": {
        "src": "assets/index.underscore",
        "dest": "dist/debug/index.html",
        "variables": {
          "requirejs": "./js/require.js",
          "css": "./css/index.css",
          "base": null
        }
      }
    };

    var settings = helper.readSettingsFile();
    return settings.template || defaultSettings;
  }();

  var couchserver_config  = function () {
    // add a "couchserver" key to settings.json with JSON that matches the
    // keys and values below (plus your customizations) to have Fauxton work
    // against a remote CouchDB-compatible server.
    var defaults = {
      dist: './dist/debug/',
      port: 8000,
      proxy: {
        target: {
          host: 'localhost',
          port: 5984,
          https: false
        },
        // This sets the Host header in the proxy so that you can use external
        // CouchDB instances and not have the Host set to 'localhost'
        changeOrigin: true
      }
    };

    return helper.readSettingsFile().couchserver || defaults;
  }();

  grunt.initConfig({

    // The clean task ensures all files are removed from the dist/ directory so
    // that no files linger from previous builds.
    clean: {
      release:  cleanable,
      watch: cleanableAddons
    },

    // The lint task will run the build configuration and the application
    // JavaScript through JSHint and report any errors.  You can change the
    // options for this task, by reading this:
    // https://github.com/cowboy/grunt/blob/master/docs/task_lint.md
    lint: {
      files: [
        "build/config.js", "app/**/*.js"
      ]
    },

    less: {
      compile: {
        options: {
          paths: assets.less.paths
        },
        files: assets.less.files
      }
    },

    // The jshint option for scripturl is set to lax, because the anchor
    // override inside main.js needs to test for them so as to not accidentally
    // route. Settings expr true so we can do `migtBeNullObject && mightBeNullObject.coolFunction()`
    jshint: {
      all: ['app/**/*.js', 'Gruntfile.js', "test/core/*.js"],
      options: {
        scripturl: true,
        evil: true,
        expr: true
      }
    },

    // The jst task compiles all application templates into JavaScript
    // functions with the underscore.js template function from 1.2.4.  You can
    // change the namespace and the template options, by reading this:
    // https://github.com/gruntjs/grunt-contrib/blob/master/docs/jst.md
    //
    // The concat task depends on this file to exist, so if you decide to
    // remove this, ensure concat is updated accordingly.
    jst: {
      compile: {
        options: {
          processContent: function(src) {
            return src.replace(/<!--[\s\S]*?-->/gm, '');
          }
        },
        files: {
          "dist/debug/templates.js": [
            "app/templates/**/*.html",
            "app/addons/**/templates/**/*.html"
          ]
        }
      }
    },

    template: templateSettings,

    // The concatenate task is used here to merge the almond require/define
    // shim and the templates into the application code.  It's named
    // dist/debug/require.js, because we want to only load one script file in
    // index.html.
    concat: {
      requirejs: {
        src: [ "assets/js/libs/require.js", "dist/debug/templates.js", "dist/debug/require.js"],
        dest: "dist/debug/js/require.js"
      },

      index_css: {
        src: ["dist/debug/css/*.css", '!dist/debug/css/index.css', 'assets/css/*.css'],
        dest: 'dist/debug/css/index.css'
      },

      test_config_js: {
        src: ["dist/debug/templates.js", "test/test.config.js"],
        dest: 'test/test.config.js'
      },
    },

    cssmin: {
      compress: {
        files: {
          "dist/release/css/index.css": [
            "dist/debug/css/index.css", 'assets/css/*.css',
            "app/addons/**/assets/css/*.css"
          ]
        },
        options: {
          report: 'min'
        }
      }
    },

    uglify: {
      release: {
        files: {
          "dist/release/js/require.js": [
            "dist/debug/js/require.js"
          ]
        }
      }
    },

    // Runs a proxy server for easier development, no need to keep deploying to couchdb
    couchserver: couchserver_config,

    watch: {
      js: {
        files: helper.watchFiles(['.js'], ["./app/**/*.js", '!./app/load_addons.js',"./assets/**/*.js", "./test/**/*.js"]),
        tasks: ['watchRun'],
      },
      style: {
        files: helper.watchFiles(['.less','.css'],["./app/**/*.css","./app/**/*.less","./assets/**/*.css", "./assets/**/*.less"]),
        tasks: ['clean:watch', 'dependencies','less', 'concat:index_css'],
      },
      html: {
        // the index.html is added in as a dummy file incase there is no
        // html dependancies this will break. So we need one include pattern
        files: helper.watchFiles(['.html'], ['./index.html']),
        tasks: ['clean:watch', 'dependencies']
      },
      options: {
        nospawn: true,
        debounceDelay: 500
      }
    },

    requirejs: {
      compile: {
        options: {
          baseUrl: 'app',
          // Include the main configuration file.
          mainConfigFile: "app/config.js",

          // Output file.
          out: "dist/debug/require.js",

          // Root application module.
          name: "config",

          // Do not wrap everything in an IIFE.
          wrap: false,
          optimize: "none",
          findNestedDependencies: true
        }
      }
    },

    // Copy build artifacts and library code into the distribution
    // see - http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically
    copy: {
      couchdb: {
        files: [
          // this gets built in the template task
          {src: "dist/release/index.html", dest: "../../share/www/fauxton/index.html"},
          {src: ["**"], dest: "../../share/www/fauxton/js/", cwd:'dist/release/js/',  expand: true},
          {src: ["**"], dest: "../../share/www/fauxton/img/", cwd:'dist/release/img/', expand: true},
          {src: ["**"], dest: "../../share/www/fauxton/css/", cwd:"dist/release/css/", expand: true}
        ]
      },
      couchdebug: {
        files: [
          // this gets built in the template task
          {src: "dist/debug/index.html", dest: "../../share/www/fauxton/index.html"},
          {src: ["**"], dest: "../../share/www/fauxton/js/", cwd:'dist/debug/js/',  expand: true},
          {src: ["**"], dest: "../../share/www/fauxton/img/", cwd:'dist/debug/img/', expand: true},
          {src: ["**"], dest: "../../share/www/fauxton/css/", cwd:"dist/debug/css/", expand: true}
        ]
      },
      ace: {
        files: [
          {src: "assets/js/libs/ace/worker-json.js", dest: "dist/release/js/ace/worker-json.js"},
          {src: "assets/js/libs/ace/mode-json.js", dest: "dist/release/js/ace/mode-json.js"},
          {src: "assets/js/libs/ace/theme-crimson_editor.js", dest: "dist/release/js/ace/theme-crimson_editor.js"},
          {src: "assets/js/libs/ace/mode-javascript.js", dest: "dist/release/js/ace/mode-javascript.js"},
          {src: "assets/js/libs/ace/worker-javascript.js", dest: "dist/release/js/ace/worker-javascript.js"},
        ]
      },

      dist:{
        files:[
          {src: "dist/debug/index.html", dest: "dist/release/index.html"},
          {src: assets.img, dest: "dist/release/img/", flatten: true, expand: true}
        ]
      },
      debug:{
        files:[
          {src: assets.img, dest: "dist/debug/img/", flatten: true, expand: true}
        ]
      }
    },

    get_deps: {
      "default": {
        src: "settings.json"
      }
    },

    gen_load_addons: {
      "default": {
        src: "settings.json"
      }
    },
    gen_initialize: templateSettings,
    /*gen_initialize: {
      "default": {
        src: "settings.json"
      }
    },*/

    mkcouchdb: couch_config,
    rmcouchdb: couch_config,
    couchapp: couch_config,

    mochaSetup: {
      default: {
        files: { src: helper.watchFiles(['[Ss]pec.js'], ['./test/core/**/*[Ss]pec.js', './app/**/*[Ss]pec.js'])},
        template: 'test/test.config.underscore',
        config: './app/config.js'
      }
    },

    mocha_phantomjs: {
      all: ['test/runner.html']
    }

  });

  // on watch events configure jshint:all to only run on changed file
  grunt.event.on('watch', function(action, filepath) {
    if (!!filepath.match(/.js$/) && filepath.indexOf('test.config.js') === -1) {
      grunt.config(['jshint', 'all'], filepath);
    }

    if (!!filepath.match(/[Ss]pec.js$/)) {
      //grunt.task.run(['mochaSetup','jst', 'concat:test_config_js', 'mocha_phantomjs']);
    }
  });

  /*
   * Load Grunt plugins
   */
  // Load fauxton specific tasks
  grunt.loadTasks('tasks');
  // Load the couchapp task
  grunt.loadNpmTasks('grunt-couchapp');
  // Load the copy task
  grunt.loadNpmTasks('grunt-contrib-watch');
  // Load the exec task
  grunt.loadNpmTasks('grunt-exec');
  // Load Require.js task
  grunt.loadNpmTasks('grunt-contrib-requirejs');
  // Load Copy task
  grunt.loadNpmTasks('grunt-contrib-copy');
  // Load Clean task
  grunt.loadNpmTasks('grunt-contrib-clean');
  // Load jshint task
  grunt.loadNpmTasks('grunt-contrib-jshint');
  // Load jst task
  grunt.loadNpmTasks('grunt-contrib-jst');
  // Load less task
  grunt.loadNpmTasks('grunt-contrib-less');
  // Load concat task
  grunt.loadNpmTasks('grunt-contrib-concat');
  // Load UglifyJS task
  grunt.loadNpmTasks('grunt-contrib-uglify');
  // Load CSSMin task
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-mocha-phantomjs');

  /*
   * Default task
   */
  // defult task - install minified app to local CouchDB
  grunt.registerTask('default', 'couchdb');

  /*
   * Transformation tasks
   */
  // clean out previous build artefactsa and lint
  grunt.registerTask('lint', ['clean', 'jshint']);
  grunt.registerTask('test', ['lint', 'dependencies', 'test_inline']);
  // lighter weight test task for use inside dev/watch
  grunt.registerTask('test_inline', ['mochaSetup','jst', 'concat:test_config_js', 'mocha_phantomjs']);
  // Fetch dependencies (from git or local dir), lint them and make load_addons
  grunt.registerTask('dependencies', ['get_deps', 'gen_load_addons:default']);
  // build templates, js and css
  grunt.registerTask('build', ['less', 'concat:index_css', 'jst', 'requirejs', 'concat:requirejs', 'template:release']);
  // minify code and css, ready for release.
  grunt.registerTask('minify', ['uglify', 'cssmin:compress']);

  /*
   * Build the app in either dev, debug, or release mode
   */
  // dev server
  grunt.registerTask('dev', ['debugDev', 'couchserver']);
  // build a debug release
  grunt.registerTask('debug', ['lint', 'dependencies', "gen_initialize:development", 'concat:requirejs','less', 'concat:index_css', 'template:development', 'copy:debug']);
  grunt.registerTask('debugDev', ['clean', 'dependencies', "gen_initialize:development",'jshint','less', 'concat:index_css', 'template:development', 'copy:debug']);

  grunt.registerTask('watchRun', ['clean:watch', 'dependencies', 'jshint']);
  // build a release
  grunt.registerTask('release', ['clean' ,'dependencies', "gen_initialize:release", 'jshint', 'build', 'minify', 'copy:dist', 'copy:ace']);

  /*
   * Install into CouchDB in either debug, release, or couchapp mode
   */
  // make a development install that is server by mochiweb under _utils
  grunt.registerTask('couchdebug', ['debug', 'copy:couchdebug']);
  // make a minimized install that is server by mochiweb under _utils
  grunt.registerTask('couchdb', ['release', 'copy:couchdb']);
  // make an install that can be deployed as a couchapp
  grunt.registerTask('couchapp_setup', ['release']);
  // install fauxton as couchapp
  grunt.registerTask('couchapp_install', ['rmcouchdb:fauxton', 'mkcouchdb:fauxton', 'couchapp:fauxton']);
  // setup and install fauxton as couchapp
  grunt.registerTask('couchapp_deploy', ['couchapp_setup', 'couchapp_install']);
};
<|endoftext|>


<commit_before>var form = require("../form.js")

//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Creates a set of Toggle Buttons
//------------------------------------------------------------------------------
module.exports = function( vars ) {

  if ( !("buttons" in vars.container) ) {

    vars.container.buttons = form()
      .container(vars.container.ui)
      .type("button")

  }

  var dataLength  = vars.data.viz.length
    , buttonWidth = vars.width.value
                  ? vars.width.value/dataLength
                  : false

  var toggles = vars.container.ui.selectAll("div.d3plus_toggle")
    .data(vars.data.viz,function(d){
      return d[vars.id.value]
    })

  toggles.enter().append("div")
    .attr("class","d3plus_toggle")
    .style("display","inline-block")
    .style("vertical-align","top")

  toggles.order()
    .each(function(d){

      if (!("form" in d.d3plus)) {
        d.d3plus.form = form().container(d3.select(this))
      }

      var id = vars.id.nesting.length > vars.depth.value ? vars.id.nesting[vars.depth.value+1] : vars.id.value

      if (d[id] instanceof Array) {
        d.d3plus.form
          .container({"id": vars.container.id+"_"+d[vars.id.value]})
          .data(d[id])
          .id(vars.id.nesting.slice(1))
          .type("drop")
      }
      else {
        d.d3plus.form
          .data([d])
          .id(vars.id.value)
          .type("button")
      }

      d.d3plus.form
        .color(vars.color)
        .focus(vars.focus.value,function(value){

          if (value !== vars.focus.value) {
            vars.self.focus(value).draw()
          }

        })
        .hover(vars.hover.value)
        .icon({
          "select": false,
          "value": vars.icon.value
        })
        .font(vars.font)
        .format(vars.format)
        .order(vars.order)
        .text(vars.text.value)
        .ui({
          "border": vars.ui.border,
          "color": vars.ui.color,
          "display": "inline-block",
          "margin": 0,
          "padding": vars.ui.padding.css
        })
        .width(buttonWidth)
        .draw()

    })

  if (vars.data.element.value) {
    vars.data.element.value
      .on("focus."+vars.container.id, function(){
        vars.self.focus(this.value).hover(this.value).draw();
      })
      .on("blur."+vars.container.id, function(){
        vars.self.hover(false).draw();
      })
  }

}
<commit_msg>added exit behavior for toggle buttons<commit_after>var form = require("../form.js")

//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Creates a set of Toggle Buttons
//------------------------------------------------------------------------------
module.exports = function( vars ) {

  if ( !("buttons" in vars.container) ) {

    vars.container.buttons = form()
      .container(vars.container.ui)
      .type("button")

  }

  var dataLength  = vars.data.viz.length
    , buttonWidth = vars.width.value
                  ? vars.width.value/dataLength
                  : false

  var toggles = vars.container.ui.selectAll("div.d3plus_toggle")
    .data(vars.data.viz,function(d){
      return d[vars.id.value];
    })

  toggles.exit().remove();

  toggles.enter().append("div")
    .attr("class","d3plus_toggle")
    .style("display","inline-block")
    .style("vertical-align","top")

  toggles.order()
    .each(function(d){

      if (!("form" in d.d3plus)) {
        d.d3plus.form = form().container(d3.select(this))
      }

      var id = vars.id.nesting.length > vars.depth.value ? vars.id.nesting[vars.depth.value+1] : vars.id.value

      if (d[id] instanceof Array) {
        d.d3plus.form
          .container({"id": vars.container.id+"_"+d[vars.id.value]})
          .data(d[id])
          .id(vars.id.nesting.slice(1))
          .type("drop")
      }
      else {
        d.d3plus.form
          .data([d])
          .id(vars.id.value)
          .type("button")
      }

      d.d3plus.form
        .color(vars.color)
        .focus(vars.focus.value,function(value){

          if (value !== vars.focus.value) {
            vars.self.focus(value).draw()
          }

        })
        .hover(vars.hover.value)
        .icon({
          "select": false,
          "value": vars.icon.value
        })
        .font(vars.font)
        .format(vars.format)
        .order(vars.order)
        .text(vars.text.value)
        .ui({
          "border": vars.ui.border,
          "color": vars.ui.color,
          "display": "inline-block",
          "margin": 0,
          "padding": vars.ui.padding.css
        })
        .width(buttonWidth)
        .draw()

    })

  if (vars.data.element.value) {
    vars.data.element.value
      .on("focus."+vars.container.id, function(){
        vars.self.focus(this.value).hover(this.value).draw();
      })
      .on("blur."+vars.container.id, function(){
        vars.self.hover(false).draw();
      })
  }

}
<|endoftext|>


<commit_before>var editorDic = {};

function ExpandDirectoryTree(obj)
{
    if (!obj.dirName || obj.dirName == '.git')
        return "";
    var ret = '
';
    for (var i = 0; i < obj.files.length; i++)
    {
        if (obj.files[i].path)
            ret += '

 ' + obj.files[i].name + '

';
        else if (obj.files[i].dirName != '.git')
            ret += '

 ' + obj.files[i].dirName + '

' + ExpandDirectoryTree(obj.files[i]);
    }
    return ret + '
';
}

router.get('/work/index', function (req, res, next) {
    showMsg("Loading project file structures...");
    node.invoke('ListFolder', req.query.project, '')
        .done(function (data) {
            $('.work .sidebar .sidebar-directory-tree').html('');
            $('.sidebar-working').html('');
            var tree = ExpandDirectoryTree(data.msg);
            $('.work .sidebar .sidebar-directory-tree').html(tree);
            $('.sidebar-directory-tree div.folder').click(function () {
                $('.sidebar-directory-tree div.folder').removeClass('active');
                $('.sidebar-directory-tree div.file').removeClass('active');
                $(this).addClass('active');
            });
            $('.sidebar-directory-tree div.file').click(function () {
                $('.sidebar-directory-tree div.folder').removeClass('active');
                $('.sidebar-directory-tree div.file').removeClass('active');
                var file = $(this);
                file.addClass('active');

                // Add to working list
                $('.sidebar-working-item').removeClass('active');
                if ($('.sidebar-working-item div[data-path="' + file.attr('data-path') + '"]').length > 0) {
                    $('.sidebar-working-item div[data-path="' + file.attr('data-path') + '"]').parent('.sidebar-working-item').prependTo('.sidebar-working');
                    $('.sidebar-working-item div[data-path="' + file.attr('data-path') + '"]').parent('.sidebar-working-item').addClass('active');
                } else {
                    var working_item = $('

' + file.text() + '

');
                    working_item.click(function () {
                        file.click();
                    });
                    working_item.prependTo('.sidebar-working');
                }

                $('#tabWorking').click();
                // 判断文件是否已经打开
                if (editorDic[file.attr('data-path')]) {
                    $('.body.coding pre').hide();
                    $('pre#' + editorDic[file.attr('data-path')].id).show();
                } else {
                    showMsg('Loading file content...');
                    node.invoke('ReadFile', req.query.project, file.attr('data-path'))
                        .done(function (data) {
                            if (data.isSucceeded) {
                                var id = jFlick.GenerateRandomString();
                                $('.body.coding').append('
' + data.msg + '
');
                                var editor = ace.edit(id);
                                editor.setTheme("ace/theme/twilight");
                                editor.session.setMode("ace/mode/csharp");
                                editorDic[file.attr('data-path')] = { editor: editor, id: id };
                                $('.body.coding pre').hide();
                                $('#' + editorDic[file.attr('data-path')].id).show();
                                hideMsg();
                            } else {
                                $('.sidebar-working-item div[data-path="' + file.attr('data-path') + '"]').parent('.sidebar-working-item').remove();
                                showMsg('An error occurred while reading file.
' + data.msg, 3000);
                            }
                        });
                }
            });
            hideMsg();
        });

    // Navigator bar click events
    $('.work .header-center-item.coding').click(function () {
        $('.work .body').addClass('hidden');
        $('.work .sidebar').addClass('hidden');
        $('.work .header-center-item').removeClass('active');
        $('.work .header-center-item.coding').addClass('active');
        $('.work .sidebar.coding').removeClass('hidden');
        $('.work .body.coding').removeClass('hidden');
    });

    $('.work .header-center-item.git').click(function () {
        $('.work .body').addClass('hidden');
        $('.work .sidebar').addClass('hidden');
        $('.work .header-center-item').removeClass('active');
        $('.work .header-center-item.git').addClass('active');
        $('.work .sidebar.git').removeClass('hidden');
        $('.work .body.git').removeClass('hidden');
    });

    $('.work .header-center-item.browser').click(function () {
        $('.work .body').addClass('hidden');
        $('.work .sidebar').addClass('hidden');
        $('.work .header-center-item').removeClass('active');
        $('.work .header-center-item.browser').addClass('active');
        $('.work .sidebar.browser').removeClass('hidden');
        $('.work .body.browser').removeClass('hidden');
    });

    $('.work .header-center-item.console').click(function () {
        $('.work .body').addClass('hidden');
        $('.work .sidebar').addClass('hidden');
        $('.work .header-center-item').removeClass('active');
        $('.work .header-center-item.console').addClass('active');
        $('.work .sidebar.console').removeClass('hidden');
        $('.work .body.console').removeClass('hidden');

        // Addition events
        $('#txtConsole').focus();
    });

    $('.work .header-center-item.ssh').click(function () {
        $('.work .body').addClass('hidden');
        $('.work .sidebar').addClass('hidden');
        $('.work .header-center-item').removeClass('active');
        $('.work .header-center-item.ssh').addClass('active');
        $('.work .body.ssh').removeClass('hidden');

        // Addition events
        $('#txtSsh').focus();
    });

    // Side bar tab click events
    $('#tabWorking').click(function () {
        $('#tabWorking').addClass('active');
        $('#tabDirectory').removeClass('active');
        $('.work .sidebar-directory').hide();
        $('.work .sidebar-directory').addClass('hidden');
        $('.work .sidebar-working').show();
        $('.work .sidebar-working').removeClass('hidden');
    });

    $('#tabDirectory').click(function () {
        $('#tabDirectory').addClass('active');
        $('#tabWorking').removeClass('active');
        $('.work .sidebar-directory').show();
        $('.work .sidebar-directory').removeClass('hidden');
        $('.work .sidebar-working').hide();
        $('.work .sidebar-working').addClass('hidden');
    });

    $('#tabChanges').click(function () {
        $('#tabChanges').addClass('active');
        $('#tabHistories').removeClass('active');
        $('.work .sidebar-histories').hide();
        $('.work .sidebar-histories').addClass('hidden');
        $('.work .sidebar-changes').show();
        $('.work .sidebar-changes').removeClass('hidden');
        $('.work .body.git .changes').show();
        $('.work .body.git .changes').removeClass('hidden');
        $('.work .body.git .histories').hide();
        $('.work .body.git .histories').addClass('hidden');
    });

    $('#tabHistories').click(function () {
        $('#tabHistories').addClass('active');
        $('#tabChanges').removeClass('active');
        $('.work .sidebar-histories').show();
        $('.work .sidebar-histories').removeClass('hidden');
        $('.work .sidebar-changes').hide();
        $('.work .sidebar-changes').addClass('hidden');
        $('.work .body.git .histories').show();
        $('.work .body.git .histories').removeClass('hidden');
        $('.work .body.git .changes').hide();
        $('.work .body.git .changes').addClass('hidden');
    });
});<commit_msg>Implement change detector<commit_after>var editorDic = {};

function ExpandDirectoryTree(obj)
{
    if (!obj.dirName || obj.dirName == '.git')
        return "";
    var ret = '
';
    for (var i = 0; i < obj.files.length; i++)
    {
        if (obj.files[i].path)
            ret += '

 ' + obj.files[i].name + '

';
        else if (obj.files[i].dirName != '.git')
            ret += '

 ' + obj.files[i].dirName + '

' + ExpandDirectoryTree(obj.files[i]);
    }
    return ret + '
';
}

router.get('/work/index', function (req, res, next) {
    showMsg("Loading project file structures...");
    node.invoke('ListFolder', req.query.project, '')
        .done(function (data) {
            $('.work .sidebar .sidebar-directory-tree').html('');
            $('.sidebar-working').html('');
            var tree = ExpandDirectoryTree(data.msg);
            $('.work .sidebar .sidebar-directory-tree').html(tree);
            $('.sidebar-directory-tree div.folder').click(function () {
                $('.sidebar-directory-tree div.folder').removeClass('active');
                $('.sidebar-directory-tree div.file').removeClass('active');
                $(this).addClass('active');
            });
            $('.sidebar-directory-tree div.file').click(function () {
                $('.sidebar-directory-tree div.folder').removeClass('active');
                $('.sidebar-directory-tree div.file').removeClass('active');
                var file = $(this);
                file.addClass('active');

                // Add to working list
                $('.sidebar-working-item').removeClass('active');
                if ($('.sidebar-working-item div[data-path="' + file.attr('data-path') + '"]').length > 0) {
                    $('.sidebar-working-item div[data-path="' + file.attr('data-path') + '"]').parent('.sidebar-working-item').prependTo('.sidebar-working');
                    $('.sidebar-working-item div[data-path="' + file.attr('data-path') + '"]').parent('.sidebar-working-item').addClass('active');
                } else {
                    var working_item = $('

' + file.text() + '

');
                    working_item.click(function () {
                        file.click();
                    });
                    working_item.prependTo('.sidebar-working');
                }

                $('#tabWorking').click();
                // 判断文件是否已经打开
                if (editorDic[file.attr('data-path')]) {
                    $('.body.coding pre').hide();
                    $('pre#' + editorDic[file.attr('data-path')].id).show();
                } else {
                    showMsg('Loading file content...');
                    node.invoke('ReadFile', req.query.project, file.attr('data-path'))
                        .done(function (data) {
                            if (data.isSucceeded) {
                                var id = jFlick.GenerateRandomString();
                                $('.body.coding').append('
' + data.msg + '
');
                                var editor = ace.edit(id);
                                editor.pomelo = { };
                                editor.pomelo.path = file.attr('data-path');
                                editor.setTheme("ace/theme/twilight");
                                editor.session.setMode("ace/mode/csharp");
                                // Editor events
                                editor.getSession().on('change', function () {
                                    $('.sidebar-working-item div[data-path="' + file.attr('data-path') + '"]').text('*' + file.text());
                                });
                                editorDic[file.attr('data-path')] = { editor: editor, id: id };
                                $('.body.coding pre').hide();
                                $('#' + editorDic[file.attr('data-path')].id).show();
                                hideMsg();
                            } else {
                                $('.sidebar-working-item div[data-path="' + file.attr('data-path') + '"]').parent('.sidebar-working-item').remove();
                                showMsg('An error occurred while reading file.
' + data.msg, 3000);
                            }
                        });
                }
            });
            hideMsg();
        });

    // Navigator bar click events
    $('.work .header-center-item.coding').click(function () {
        $('.work .body').addClass('hidden');
        $('.work .sidebar').addClass('hidden');
        $('.work .header-center-item').removeClass('active');
        $('.work .header-center-item.coding').addClass('active');
        $('.work .sidebar.coding').removeClass('hidden');
        $('.work .body.coding').removeClass('hidden');
    });

    $('.work .header-center-item.git').click(function () {
        $('.work .body').addClass('hidden');
        $('.work .sidebar').addClass('hidden');
        $('.work .header-center-item').removeClass('active');
        $('.work .header-center-item.git').addClass('active');
        $('.work .sidebar.git').removeClass('hidden');
        $('.work .body.git').removeClass('hidden');
    });

    $('.work .header-center-item.browser').click(function () {
        $('.work .body').addClass('hidden');
        $('.work .sidebar').addClass('hidden');
        $('.work .header-center-item').removeClass('active');
        $('.work .header-center-item.browser').addClass('active');
        $('.work .sidebar.browser').removeClass('hidden');
        $('.work .body.browser').removeClass('hidden');
    });

    $('.work .header-center-item.console').click(function () {
        $('.work .body').addClass('hidden');
        $('.work .sidebar').addClass('hidden');
        $('.work .header-center-item').removeClass('active');
        $('.work .header-center-item.console').addClass('active');
        $('.work .sidebar.console').removeClass('hidden');
        $('.work .body.console').removeClass('hidden');

        // Addition events
        $('#txtConsole').focus();
    });

    $('.work .header-center-item.ssh').click(function () {
        $('.work .body').addClass('hidden');
        $('.work .sidebar').addClass('hidden');
        $('.work .header-center-item').removeClass('active');
        $('.work .header-center-item.ssh').addClass('active');
        $('.work .body.ssh').removeClass('hidden');

        // Addition events
        $('#txtSsh').focus();
    });

    // Side bar tab click events
    $('#tabWorking').click(function () {
        $('#tabWorking').addClass('active');
        $('#tabDirectory').removeClass('active');
        $('.work .sidebar-directory').hide();
        $('.work .sidebar-directory').addClass('hidden');
        $('.work .sidebar-working').show();
        $('.work .sidebar-working').removeClass('hidden');
    });

    $('#tabDirectory').click(function () {
        $('#tabDirectory').addClass('active');
        $('#tabWorking').removeClass('active');
        $('.work .sidebar-directory').show();
        $('.work .sidebar-directory').removeClass('hidden');
        $('.work .sidebar-working').hide();
        $('.work .sidebar-working').addClass('hidden');
    });

    $('#tabChanges').click(function () {
        $('#tabChanges').addClass('active');
        $('#tabHistories').removeClass('active');
        $('.work .sidebar-histories').hide();
        $('.work .sidebar-histories').addClass('hidden');
        $('.work .sidebar-changes').show();
        $('.work .sidebar-changes').removeClass('hidden');
        $('.work .body.git .changes').show();
        $('.work .body.git .changes').removeClass('hidden');
        $('.work .body.git .histories').hide();
        $('.work .body.git .histories').addClass('hidden');
    });

    $('#tabHistories').click(function () {
        $('#tabHistories').addClass('active');
        $('#tabChanges').removeClass('active');
        $('.work .sidebar-histories').show();
        $('.work .sidebar-histories').removeClass('hidden');
        $('.work .sidebar-changes').hide();
        $('.work .sidebar-changes').addClass('hidden');
        $('.work .body.git .histories').show();
        $('.work .body.git .histories').removeClass('hidden');
        $('.work .body.git .changes').hide();
        $('.work .body.git .changes').addClass('hidden');
    });
});<|endoftext|>


<commit_before>// @flow

import type { OverlayRouterNavigationProp } from './overlay-router';
import type { RootNavigationProp } from './root-navigator.react';
import type { BottomTabNavigationProp } from '@react-navigation/bottom-tabs';

import * as React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { PersistGate } from 'redux-persist/integration/react';
import * as SplashScreen from 'expo-splash-screen';
import Icon from 'react-native-vector-icons/FontAwesome';

import {
  CalendarRouteName,
  ChatRouteName,
  MoreRouteName,
  TabNavigatorRouteName,
  MultimediaModalRouteName,
  MultimediaTooltipModalRouteName,
  ActionResultModalRouteName,
  TextMessageTooltipModalRouteName,
  ThreadSettingsMemberTooltipModalRouteName,
  CameraModalRouteName,
  type ScreenParamList,
  type TabParamList,
  type OverlayParamList,
} from './route-names';
import Calendar from '../calendar/calendar.react';
import Chat from '../chat/chat.react';
import More from '../more/more.react';
import { tabBar } from './tab-bar.react';
import { createOverlayNavigator } from './overlay-navigator.react';
import MultimediaModal from '../media/multimedia-modal.react';
import { MultimediaTooltipModal } from '../chat/multimedia-tooltip-modal.react';
import ActionResultModal from './action-result-modal.react';
import { TextMessageTooltipModal } from '../chat/text-message-tooltip-modal.react';
import ThreadSettingsMemberTooltipModal from '../chat/settings/thread-settings-member-tooltip-modal.react';
import CameraModal from '../media/camera-modal.react';
import KeyboardStateContainer from '../keyboard/keyboard-state-container.react';
import PushHandler from '../push/push-handler.react';
import { getPersistor } from '../redux/persist';
import { RootContext } from '../root-context';
import { waitForInteractions } from '../utils/interactions';
import ChatIcon from '../chat/chat-icon.react';

let splashScreenHasHidden = false;

const calendarTabOptions = {
  tabBarLabel: 'Calendar',
  // eslint-disable-next-line react/display-name
  tabBarIcon: ({ color }) => (
    <Icon name="calendar" style={[styles.icon, { color }]} />
  ),
};
const chatTabOptions = {
  tabBarLabel: 'Chat',
  // eslint-disable-next-line react/display-name
  tabBarIcon: ({ color }) => <ChatIcon color={color} />,
};
const moreTabOptions = {
  tabBarLabel: 'More',
  // eslint-disable-next-line react/display-name
  tabBarIcon: ({ color }) => (
    <Icon name="bars" style={[styles.icon, { color }]} />
  ),
};

export type TabNavigationProp<
  RouteName: $Keys<TabParamList> = $Keys<TabParamList>,
> = BottomTabNavigationProp<ScreenParamList, RouteName>;

const Tab = createBottomTabNavigator<
  ScreenParamList,
  TabParamList,
  TabNavigationProp<>,
>();
const tabBarOptions = { keyboardHidesTabBar: false };
function TabNavigator() {
  return (
    <Tab.Navigator
      initialRouteName={ChatRouteName}
      lazy={false}
      tabBar={tabBar}
      backBehavior="none"
      tabBarOptions={tabBarOptions}
    >
      <Tab.Screen
        name={CalendarRouteName}
        component={Calendar}
        options={calendarTabOptions}
      />
      <Tab.Screen
        name={ChatRouteName}
        component={Chat}
        options={chatTabOptions}
      />
      <Tab.Screen
        name={MoreRouteName}
        component={More}
        options={moreTabOptions}
      />
    </Tab.Navigator>
  );
}

export type AppNavigationProp<
  RouteName: $Keys<OverlayParamList> = $Keys<OverlayParamList>,
> = OverlayRouterNavigationProp<ScreenParamList, RouteName>;

const App = createOverlayNavigator<
  ScreenParamList,
  OverlayParamList,
  AppNavigationProp<>,
>();
type AppNavigatorProps = {
  navigation: RootNavigationProp<'App'>,
};
function AppNavigator(props: AppNavigatorProps) {
  const { navigation } = props;

  const rootContext = React.useContext(RootContext);
  const setNavStateInitialized =
    rootContext && rootContext.setNavStateInitialized;
  React.useEffect(() => {
    setNavStateInitialized && setNavStateInitialized();
  }, [setNavStateInitialized]);

  React.useEffect(() => {
    if (splashScreenHasHidden) {
      return;
    }
    splashScreenHasHidden = true;
    (async () => {
      await waitForInteractions();
      try {
        await SplashScreen.hideAsync();
      } catch {}
    })();
  }, []);

  return (
    <KeyboardStateContainer>
      <App.Navigator>
        <App.Screen name={TabNavigatorRouteName} component={TabNavigator} />
        <App.Screen
          name={MultimediaModalRouteName}
          component={MultimediaModal}
        />
        <App.Screen
          name={MultimediaTooltipModalRouteName}
          component={MultimediaTooltipModal}
        />
        <App.Screen
          name={ActionResultModalRouteName}
          component={ActionResultModal}
        />
        <App.Screen
          name={TextMessageTooltipModalRouteName}
          component={TextMessageTooltipModal}
        />
        <App.Screen
          name={ThreadSettingsMemberTooltipModalRouteName}
          component={ThreadSettingsMemberTooltipModal}
        />
        <App.Screen name={CameraModalRouteName} component={CameraModal} />
      </App.Navigator>
      <PersistGate persistor={getPersistor()}>
        <PushHandler navigation={navigation} />
      </PersistGate>
    </KeyboardStateContainer>
  );
}

const styles = {
  icon: {
    fontSize: 28,
  },
};

export default AppNavigator;
<commit_msg>[native] Don't render PushHandler until splash screen is hidden<commit_after>// @flow

import type { OverlayRouterNavigationProp } from './overlay-router';
import type { RootNavigationProp } from './root-navigator.react';
import type { BottomTabNavigationProp } from '@react-navigation/bottom-tabs';

import * as React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { PersistGate } from 'redux-persist/integration/react';
import * as SplashScreen from 'expo-splash-screen';
import Icon from 'react-native-vector-icons/FontAwesome';

import {
  CalendarRouteName,
  ChatRouteName,
  MoreRouteName,
  TabNavigatorRouteName,
  MultimediaModalRouteName,
  MultimediaTooltipModalRouteName,
  ActionResultModalRouteName,
  TextMessageTooltipModalRouteName,
  ThreadSettingsMemberTooltipModalRouteName,
  CameraModalRouteName,
  type ScreenParamList,
  type TabParamList,
  type OverlayParamList,
} from './route-names';
import Calendar from '../calendar/calendar.react';
import Chat from '../chat/chat.react';
import More from '../more/more.react';
import { tabBar } from './tab-bar.react';
import { createOverlayNavigator } from './overlay-navigator.react';
import MultimediaModal from '../media/multimedia-modal.react';
import { MultimediaTooltipModal } from '../chat/multimedia-tooltip-modal.react';
import ActionResultModal from './action-result-modal.react';
import { TextMessageTooltipModal } from '../chat/text-message-tooltip-modal.react';
import ThreadSettingsMemberTooltipModal from '../chat/settings/thread-settings-member-tooltip-modal.react';
import CameraModal from '../media/camera-modal.react';
import KeyboardStateContainer from '../keyboard/keyboard-state-container.react';
import PushHandler from '../push/push-handler.react';
import { getPersistor } from '../redux/persist';
import { RootContext } from '../root-context';
import { waitForInteractions } from '../utils/interactions';
import ChatIcon from '../chat/chat-icon.react';

let splashScreenHasHidden = false;

const calendarTabOptions = {
  tabBarLabel: 'Calendar',
  // eslint-disable-next-line react/display-name
  tabBarIcon: ({ color }) => (
    <Icon name="calendar" style={[styles.icon, { color }]} />
  ),
};
const chatTabOptions = {
  tabBarLabel: 'Chat',
  // eslint-disable-next-line react/display-name
  tabBarIcon: ({ color }) => <ChatIcon color={color} />,
};
const moreTabOptions = {
  tabBarLabel: 'More',
  // eslint-disable-next-line react/display-name
  tabBarIcon: ({ color }) => (
    <Icon name="bars" style={[styles.icon, { color }]} />
  ),
};

export type TabNavigationProp<
  RouteName: $Keys<TabParamList> = $Keys<TabParamList>,
> = BottomTabNavigationProp<ScreenParamList, RouteName>;

const Tab = createBottomTabNavigator<
  ScreenParamList,
  TabParamList,
  TabNavigationProp<>,
>();
const tabBarOptions = { keyboardHidesTabBar: false };
function TabNavigator() {
  return (
    <Tab.Navigator
      initialRouteName={ChatRouteName}
      lazy={false}
      tabBar={tabBar}
      backBehavior="none"
      tabBarOptions={tabBarOptions}
    >
      <Tab.Screen
        name={CalendarRouteName}
        component={Calendar}
        options={calendarTabOptions}
      />
      <Tab.Screen
        name={ChatRouteName}
        component={Chat}
        options={chatTabOptions}
      />
      <Tab.Screen
        name={MoreRouteName}
        component={More}
        options={moreTabOptions}
      />
    </Tab.Navigator>
  );
}

export type AppNavigationProp<
  RouteName: $Keys<OverlayParamList> = $Keys<OverlayParamList>,
> = OverlayRouterNavigationProp<ScreenParamList, RouteName>;

const App = createOverlayNavigator<
  ScreenParamList,
  OverlayParamList,
  AppNavigationProp<>,
>();
type AppNavigatorProps = {
  navigation: RootNavigationProp<'App'>,
};
function AppNavigator(props: AppNavigatorProps) {
  const { navigation } = props;

  const rootContext = React.useContext(RootContext);
  const setNavStateInitialized =
    rootContext && rootContext.setNavStateInitialized;
  React.useEffect(() => {
    setNavStateInitialized && setNavStateInitialized();
  }, [setNavStateInitialized]);

  const [
    localSplashScreenHasHidden,
    setLocalSplashScreenHasHidden,
  ] = React.useState(splashScreenHasHidden);

  React.useEffect(() => {
    if (localSplashScreenHasHidden) {
      return;
    }
    splashScreenHasHidden = true;
    (async () => {
      await waitForInteractions();
      try {
        await SplashScreen.hideAsync();
        setLocalSplashScreenHasHidden(true);
      } catch {}
    })();
  }, [localSplashScreenHasHidden]);

  let pushHandler;
  if (localSplashScreenHasHidden) {
    pushHandler = (
      <PersistGate persistor={getPersistor()}>
        <PushHandler navigation={navigation} />
      </PersistGate>
    );
  }

  return (
    <KeyboardStateContainer>
      <App.Navigator>
        <App.Screen name={TabNavigatorRouteName} component={TabNavigator} />
        <App.Screen
          name={MultimediaModalRouteName}
          component={MultimediaModal}
        />
        <App.Screen
          name={MultimediaTooltipModalRouteName}
          component={MultimediaTooltipModal}
        />
        <App.Screen
          name={ActionResultModalRouteName}
          component={ActionResultModal}
        />
        <App.Screen
          name={TextMessageTooltipModalRouteName}
          component={TextMessageTooltipModal}
        />
        <App.Screen
          name={ThreadSettingsMemberTooltipModalRouteName}
          component={ThreadSettingsMemberTooltipModal}
        />
        <App.Screen name={CameraModalRouteName} component={CameraModal} />
      </App.Navigator>
      {pushHandler}
    </KeyboardStateContainer>
  );
}

const styles = {
  icon: {
    fontSize: 28,
  },
};

export default AppNavigator;
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react'
import serializeError from 'serialize-error'
import Button from 'modules/web/components/button'
import {
	getStudentInfo,
	checkIfLoggedIn,
	ExtensionNotLoadedError,
	ExtensionTooOldError,
	convertStudent,
	semesterName,
} from 'modules/schools/stolaf'
import {getCourse} from 'modules/web/helpers/get-courses'
import StudentSummary from 'modules/web/routes/student/components/student-summary'
import map from 'lodash/map'
import groupBy from 'lodash/groupBy'
import sortBy from 'lodash/sortBy'
import { RadioGroup, Radio } from 'react-radio-group'
import { initStudent } from 'modules/web/redux/students/actions/init-student'
import { connect } from 'react-redux'
import withRouter from 'react-router/lib/withRouter'
import './sis-import.scss'

class SISImportScreen extends Component {
	static propTypes = {
		dispatch: PropTypes.func.isRequired, // redux
		router: PropTypes.object.isRequired,
	};

	state = {
		loggedIn: null,
		checkingLogin: true,
		error: null,
		ids: [],
		selectedId: null,
		student: null,
	};

	componentWillMount() {
		this.checkLoginState()
	}

	checkLoginState = () => {
		checkIfLoggedIn()
			.then(ids => {
				if (ids.length === 1) {
					this.setState({loggedIn: true, checkingLogin: false, selectedId: ids[0]})
				}
				else {
					this.setState({loggedIn: true, checkingLogin: false, ids})
				}
			})
			.catch(err => {
				this.setState({loggedIn: false, checkingLogin: false})
				if (err instanceof ExtensionNotLoadedError) {
					this.setState({error: 'The extension is not loaded properly.'})
				}
				else if (err instanceof ExtensionTooOldError) {
					this.setState({error: 'The extension is too old.'})
				}
				else {
					this.setState({error: serializeError(err)})
				}
			})
	};

	handleImportData = () => {
		getStudentInfo(this.state.selectedId)
			.then(info => convertStudent(info, getCourse))
			.then(student => this.setState({student}))
			.catch(err => {
				console.error(err)
				this.setState({error: serializeError(err)})
			})
	};

	handleCreateStudent = () => {
		let action = initStudent(this.state.student)
		this.props.dispatch(action)
		this.props.dispatch(this.props.router.push(`/s/${action.payload.id}`))
	};

	handleSelectId = value => {
		this.setState({selectedId: value})
		this.handleImportData()
	};

	render() {
		let {
			student,
			checkingLogin,
			loggedIn,
			error,
			ids,
		} = this.state

		return (

Import from the SIS

					{checkingLogin
						? 'Checking login…'
						: loggedIn
							? "Great! You're logged in."
							: 'Not logged in. Please log in to the SIS in another tab.'}

				{error
					?

							{error.name}: {error.message}

							{error.stack}

					: null}

				{!loggedIn ? <Button disabled={checkingLogin} onClick={this.checkLoginState}>Check Again</Button> : null}

				{ids.length > 1 ?

Hang on one second… we found multiple student IDs. Which one is yours?

					<RadioGroup name='student-id' selectedValue={this.state.selectedId} onChange={this.handleSelectId}>
						{map(ids, id => <Radio value={id} /> {id})}
					</RadioGroup>

 : null}

				{student
					?

						<StudentSummary student={student} showMessage={false} />

							{map(groupBy(student.schedules, 'year'), (schedules, year) =>

{year}:

										{map(sortBy(schedules, 'semester'), schedule =>

{semesterName(schedule.semester)}:

													{map(schedule.courses, course =>

{course.deptnum} – {course.name}

													)}

										)}

							)}

					: null}

					{loggedIn ? student
						? <Button onClick={this.handleCreateStudent}>Import Student</Button>
						: <Button disabled={!loggedIn} onClick={this.handleImportData}>Fetch Student</Button>
					: null}

		)
	}
}

let mapDispatch = dispatch => ({dispatch})

export default connect(undefined, mapDispatch)(withRouter(SISImportScreen))
<commit_msg>fix another "push undefined type" bug<commit_after>import React, {Component, PropTypes} from 'react'
import serializeError from 'serialize-error'
import Button from 'modules/web/components/button'
import {
	getStudentInfo,
	checkIfLoggedIn,
	ExtensionNotLoadedError,
	ExtensionTooOldError,
	convertStudent,
	semesterName,
} from 'modules/schools/stolaf'
import {getCourse} from 'modules/web/helpers/get-courses'
import StudentSummary from 'modules/web/routes/student/components/student-summary'
import map from 'lodash/map'
import groupBy from 'lodash/groupBy'
import sortBy from 'lodash/sortBy'
import { RadioGroup, Radio } from 'react-radio-group'
import { initStudent } from 'modules/web/redux/students/actions/init-student'
import { connect } from 'react-redux'
import withRouter from 'react-router/lib/withRouter'
import './sis-import.scss'

class SISImportScreen extends Component {
	static propTypes = {
		dispatch: PropTypes.func.isRequired, // redux
		router: PropTypes.object.isRequired,
	};

	state = {
		loggedIn: null,
		checkingLogin: true,
		error: null,
		ids: [],
		selectedId: null,
		student: null,
	};

	componentWillMount() {
		this.checkLoginState()
	}

	checkLoginState = () => {
		checkIfLoggedIn()
			.then(ids => {
				if (ids.length === 1) {
					this.setState({loggedIn: true, checkingLogin: false, selectedId: ids[0]})
				}
				else {
					this.setState({loggedIn: true, checkingLogin: false, ids})
				}
			})
			.catch(err => {
				this.setState({loggedIn: false, checkingLogin: false})
				if (err instanceof ExtensionNotLoadedError) {
					this.setState({error: 'The extension is not loaded properly.'})
				}
				else if (err instanceof ExtensionTooOldError) {
					this.setState({error: 'The extension is too old.'})
				}
				else {
					this.setState({error: serializeError(err)})
				}
			})
	};

	handleImportData = () => {
		getStudentInfo(this.state.selectedId)
			.then(info => convertStudent(info, getCourse))
			.then(student => this.setState({student}))
			.catch(err => {
				console.error(err)
				this.setState({error: serializeError(err)})
			})
	};

	handleCreateStudent = () => {
		let action = initStudent(this.state.student)
		this.props.dispatch(action)
		this.props.router.push(`/s/${action.payload.id}`)
	};

	handleSelectId = value => {
		this.setState({selectedId: value})
		this.handleImportData()
	};

	render() {
		let {
			student,
			checkingLogin,
			loggedIn,
			error,
			ids,
		} = this.state

		return (

Import from the SIS

					{checkingLogin
						? 'Checking login…'
						: loggedIn
							? "Great! You're logged in."
							: 'Not logged in. Please log in to the SIS in another tab.'}

				{error
					?

							{error.name}: {error.message}

							{error.stack}

					: null}

				{!loggedIn ? <Button disabled={checkingLogin} onClick={this.checkLoginState}>Check Again</Button> : null}

				{ids.length > 1 ?

Hang on one second… we found multiple student IDs. Which one is yours?

					<RadioGroup name='student-id' selectedValue={this.state.selectedId} onChange={this.handleSelectId}>
						{map(ids, id => <Radio value={id} /> {id})}
					</RadioGroup>

 : null}

				{student
					?

						<StudentSummary student={student} showMessage={false} />

							{map(groupBy(student.schedules, 'year'), (schedules, year) =>

{year}:

										{map(sortBy(schedules, 'semester'), schedule =>

{semesterName(schedule.semester)}:

													{map(schedule.courses, course =>

{course.deptnum} – {course.name}

													)}

										)}

							)}

					: null}

					{loggedIn ? student
						? <Button onClick={this.handleCreateStudent}>Import Student</Button>
						: <Button disabled={!loggedIn} onClick={this.handleImportData}>Fetch Student</Button>
					: null}

		)
	}
}

let mapDispatch = dispatch => ({dispatch})

export default connect(undefined, mapDispatch)(withRouter(SISImportScreen))
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Include test fixture.
GEN_INCLUDE(['net_internals_test.js']);

// Anonymous namespace
(function() {

/**
 * Phases of a PrerenderTask.
 * @enum
 */
var STATE = {
  // The task has been created, but not yet started.
  NONE: -1,

  // We've switched to the prerender tab, but have yet to receive the
  // resulting onPrerenderInfoChanged event with no prerenders active or in
  // the history.
  START_PRERENDERING: 0,
  // We're waiting for the prerender loader to start in a background tab,
  // as well as the prerendered view to be created. Only visit this state if
  // |shouldSucceed| is true.
  NEED_NAVIGATE: 1,
  // The prerendered view has been created, and we've started the navigation
  // to it.  We're waiting for it to move to the history.  We may see the
  // prerender one or more times in the active list, or it may move straight
  // to the history.  We will not receive any event with both history and
  // active prerenders empty while in this state, as we only send
  // notifications when the values change.
  HISTORY_WAIT: 2
};

/**
 * Task that tries to prerender a page.  The URL is received from the previous
 * Task.  A loader page is opened in a background tab, which triggers the
 * prerender.  |shouldSucceed| indicates whether the prerender is expected to
 * succeed or not.  If it's false, we just wait for the page to fail, possibly
 * seeing it as active first.  If it's true, we navigate to the URL in the
 * background tab.
 *
 * Checks that we see all relevant events, and update the corresponding tables.
 * In both cases, we exit the test once we see the prerender in the history.
 * |finalStatus| is the expected status value when the page reaches the
 * history.
 *
 * @param {bool} shouldSucceed Whether or not the prerender should succeed.
 * @param {string} finalStatus The expected value of |final_status|.
 * @extends {NetInternalsTest.Task}
 * @constructor
 */
function PrerenderTask(shouldSucceed, finalStatus) {
  NetInternalsTest.Task.call(this);

  this.startedSuccessfulPrerender_ = false;
  this.url_ = null;
  this.shouldSucceed_ = shouldSucceed;
  this.finalStatus_ = finalStatus;
  this.state_ = STATE.NONE;
}

PrerenderTask.prototype = {
  __proto__: NetInternalsTest.Task.prototype,

  /**
   * Switches to prerender tab and starts waiting until we receive prerender
   * info (With no pages prerendering) before starting to prerender.
   * @param {string} url URL to be prerendered.
   */
  start: function(url) {
    assertEquals('string', typeof url);
    this.state_ = STATE.START_PRERENDERING;
    this.url_ = url;
    g_browser.addPrerenderInfoObserver(this, true);
    NetInternalsTest.switchToView('prerender');
  },

  /**
   * PrerenderInfoObserver function.  Tracks state transitions, checks the
   * table sizes, and does some sanity checking on received data.
   * @param {object} prerenderInfo State of prerendering pages.
   */
  onPrerenderInfoChanged: function(prerenderInfo) {
    // Verify that prerendering is enabled.
    assertTrue(prerenderInfo.enabled, 'Prerendering not enabled.');

    // Check number of rows in both tables.
    NetInternalsTest.checkStyledTableRows(PrerenderView.HISTORY_DIV_ID,
                                          prerenderInfo.history.length);
    NetInternalsTest.checkStyledTableRows(PrerenderView.ACTIVE_DIV_ID,
                                          prerenderInfo.active.length);

    if (this.state_ == STATE.START_PRERENDERING) {
      this.startPrerendering_(prerenderInfo);
    } else if (this.state_ == STATE.NEED_NAVIGATE) {
      this.navigate_(prerenderInfo);
    } else if (this.state_ == STATE.HISTORY_WAIT) {
      this.checkDone_(prerenderInfo);
    } else {
      assertNotReached();
    }
  },

  /**
   * Start by triggering a prerender of |url_| in a background tab.
   * At this point, we expect no active or historical prerender entries.
   * @param {Object} prerenderInfo State of prerendering pages.
   */
  startPrerendering_: function(prerenderInfo) {
    expectEquals(0, prerenderInfo.active.length);
    expectEquals(0, prerenderInfo.history.length);
    chrome.send('prerenderPage', [this.url_]);
    if (this.shouldSucceed_) {
      this.state_ = STATE.NEED_NAVIGATE;
    } else {
      this.state_ = STATE.HISTORY_WAIT;
    }
  },

  /**
   * Navigate to the prerendered page in the background tab.
   * @param {Object} prerenderInfo State of prerendering pages.
   */
  navigate_: function(prerenderInfo) {
    expectEquals(0, prerenderInfo.history.length);
    assertEquals(1, prerenderInfo.active.length);
    expectEquals(this.url_, prerenderInfo.active[0].url);
    expectTrue(this.shouldSucceed_);
    chrome.send('navigateToPrerender');
    this.state_ = STATE.HISTORY_WAIT;
  },

  /**
   * We expect to either see the failure url as an active entry, or see it
   * in the history.  In the latter case, the test completes.
   * @param {Object} prerenderInfo State of prerendering pages.
   */
  checkDone_: function(prerenderInfo) {
    // If we see the url as active, continue running the test.
    if (prerenderInfo.active.length == 1) {
      expectEquals(this.url_, prerenderInfo.active[0].url);
      expectEquals(0, prerenderInfo.history.length);
      return;
    }

    // The prerender of |url_| is now in the history.
    this.checkHistory_(prerenderInfo);
  },

  /**
   * Check if the history is consistent with expectations, and end the test.
   * @param {Object} prerenderInfo State of prerendering pages.
   */
  checkHistory_: function(prerenderInfo) {
    expectEquals(0, prerenderInfo.active.length);
    assertEquals(1, prerenderInfo.history.length);
    expectEquals(this.url_, prerenderInfo.history[0].url);
    expectEquals(this.finalStatus_, prerenderInfo.history[0].final_status);

    testDone();
  }
};

/**
 * Prerender a page and navigate to it, once prerendering starts.
 */
TEST_F('NetInternalsTest', 'netInternalsPrerenderViewSucceed', function() {
  var taskQueue = new NetInternalsTest.TaskQueue(true);
  taskQueue.addTask(
      new NetInternalsTest.GetTestServerURLTask('files/title1.html'));
  taskQueue.addTask(new PrerenderTask(true, 'Used'));
  taskQueue.run();
});

/**
 * Prerender a page that is expected to fail.
 */
TEST_F('NetInternalsTest', 'netInternalsPrerenderViewFail', function() {
  var taskQueue = new NetInternalsTest.TaskQueue(true);
  taskQueue.addTask(
      new NetInternalsTest.GetTestServerURLTask('files/download-test1.lib'));
  taskQueue.addTask(new PrerenderTask(false, 'Download'));
  taskQueue.run();
});

})();  // Anonymous namespace
<commit_msg>Mark NetInternalsTest.NetInternalsPrerenderViewFail as flaky<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Include test fixture.
GEN_INCLUDE(['net_internals_test.js']);

// Anonymous namespace
(function() {

/**
 * Phases of a PrerenderTask.
 * @enum
 */
var STATE = {
  // The task has been created, but not yet started.
  NONE: -1,

  // We've switched to the prerender tab, but have yet to receive the
  // resulting onPrerenderInfoChanged event with no prerenders active or in
  // the history.
  START_PRERENDERING: 0,
  // We're waiting for the prerender loader to start in a background tab,
  // as well as the prerendered view to be created. Only visit this state if
  // |shouldSucceed| is true.
  NEED_NAVIGATE: 1,
  // The prerendered view has been created, and we've started the navigation
  // to it.  We're waiting for it to move to the history.  We may see the
  // prerender one or more times in the active list, or it may move straight
  // to the history.  We will not receive any event with both history and
  // active prerenders empty while in this state, as we only send
  // notifications when the values change.
  HISTORY_WAIT: 2
};

/**
 * Task that tries to prerender a page.  The URL is received from the previous
 * Task.  A loader page is opened in a background tab, which triggers the
 * prerender.  |shouldSucceed| indicates whether the prerender is expected to
 * succeed or not.  If it's false, we just wait for the page to fail, possibly
 * seeing it as active first.  If it's true, we navigate to the URL in the
 * background tab.
 *
 * Checks that we see all relevant events, and update the corresponding tables.
 * In both cases, we exit the test once we see the prerender in the history.
 * |finalStatus| is the expected status value when the page reaches the
 * history.
 *
 * @param {bool} shouldSucceed Whether or not the prerender should succeed.
 * @param {string} finalStatus The expected value of |final_status|.
 * @extends {NetInternalsTest.Task}
 * @constructor
 */
function PrerenderTask(shouldSucceed, finalStatus) {
  NetInternalsTest.Task.call(this);

  this.startedSuccessfulPrerender_ = false;
  this.url_ = null;
  this.shouldSucceed_ = shouldSucceed;
  this.finalStatus_ = finalStatus;
  this.state_ = STATE.NONE;
}

PrerenderTask.prototype = {
  __proto__: NetInternalsTest.Task.prototype,

  /**
   * Switches to prerender tab and starts waiting until we receive prerender
   * info (With no pages prerendering) before starting to prerender.
   * @param {string} url URL to be prerendered.
   */
  start: function(url) {
    assertEquals('string', typeof url);
    this.state_ = STATE.START_PRERENDERING;
    this.url_ = url;
    g_browser.addPrerenderInfoObserver(this, true);
    NetInternalsTest.switchToView('prerender');
  },

  /**
   * PrerenderInfoObserver function.  Tracks state transitions, checks the
   * table sizes, and does some sanity checking on received data.
   * @param {object} prerenderInfo State of prerendering pages.
   */
  onPrerenderInfoChanged: function(prerenderInfo) {
    // Verify that prerendering is enabled.
    assertTrue(prerenderInfo.enabled, 'Prerendering not enabled.');

    // Check number of rows in both tables.
    NetInternalsTest.checkStyledTableRows(PrerenderView.HISTORY_DIV_ID,
                                          prerenderInfo.history.length);
    NetInternalsTest.checkStyledTableRows(PrerenderView.ACTIVE_DIV_ID,
                                          prerenderInfo.active.length);

    if (this.state_ == STATE.START_PRERENDERING) {
      this.startPrerendering_(prerenderInfo);
    } else if (this.state_ == STATE.NEED_NAVIGATE) {
      this.navigate_(prerenderInfo);
    } else if (this.state_ == STATE.HISTORY_WAIT) {
      this.checkDone_(prerenderInfo);
    } else {
      assertNotReached();
    }
  },

  /**
   * Start by triggering a prerender of |url_| in a background tab.
   * At this point, we expect no active or historical prerender entries.
   * @param {Object} prerenderInfo State of prerendering pages.
   */
  startPrerendering_: function(prerenderInfo) {
    expectEquals(0, prerenderInfo.active.length);
    expectEquals(0, prerenderInfo.history.length);
    chrome.send('prerenderPage', [this.url_]);
    if (this.shouldSucceed_) {
      this.state_ = STATE.NEED_NAVIGATE;
    } else {
      this.state_ = STATE.HISTORY_WAIT;
    }
  },

  /**
   * Navigate to the prerendered page in the background tab.
   * @param {Object} prerenderInfo State of prerendering pages.
   */
  navigate_: function(prerenderInfo) {
    expectEquals(0, prerenderInfo.history.length);
    assertEquals(1, prerenderInfo.active.length);
    expectEquals(this.url_, prerenderInfo.active[0].url);
    expectTrue(this.shouldSucceed_);
    chrome.send('navigateToPrerender');
    this.state_ = STATE.HISTORY_WAIT;
  },

  /**
   * We expect to either see the failure url as an active entry, or see it
   * in the history.  In the latter case, the test completes.
   * @param {Object} prerenderInfo State of prerendering pages.
   */
  checkDone_: function(prerenderInfo) {
    // If we see the url as active, continue running the test.
    if (prerenderInfo.active.length == 1) {
      expectEquals(this.url_, prerenderInfo.active[0].url);
      expectEquals(0, prerenderInfo.history.length);
      return;
    }

    // The prerender of |url_| is now in the history.
    this.checkHistory_(prerenderInfo);
  },

  /**
   * Check if the history is consistent with expectations, and end the test.
   * @param {Object} prerenderInfo State of prerendering pages.
   */
  checkHistory_: function(prerenderInfo) {
    expectEquals(0, prerenderInfo.active.length);
    assertEquals(1, prerenderInfo.history.length);
    expectEquals(this.url_, prerenderInfo.history[0].url);
    expectEquals(this.finalStatus_, prerenderInfo.history[0].final_status);

    testDone();
  }
};

/**
 * Prerender a page and navigate to it, once prerendering starts.
 */
TEST_F('NetInternalsTest', 'netInternalsPrerenderViewSucceed', function() {
  var taskQueue = new NetInternalsTest.TaskQueue(true);
  taskQueue.addTask(
      new NetInternalsTest.GetTestServerURLTask('files/title1.html'));
  taskQueue.addTask(new PrerenderTask(true, 'Used'));
  taskQueue.run();
});

/**
 * Prerender a page that is expected to fail.
 * Flaky: http://code.google.com/p/chromium/issues/detail?id=112829.
 */
TEST_F('NetInternalsTest', 'FLAKY_netInternalsPrerenderViewFail', function() {
  var taskQueue = new NetInternalsTest.TaskQueue(true);
  taskQueue.addTask(
      new NetInternalsTest.GetTestServerURLTask('files/download-test1.lib'));
  taskQueue.addTask(new PrerenderTask(false, 'Download'));
  taskQueue.run();
});

})();  // Anonymous namespace
<|endoftext|>


<commit_before>// Invoked by: SNS Subscription
// Returns: Error or status message
//
// Recieves messages from an SNS topic subscription that contain preformed Slack
// message payloads (as JSON data). Makes a request to a Slack Incoming Webhook
// URL (also provided by the SNS message) with the payload.
//
// This is a very simple function, and should not make any decisions about where
// to send messages. It is just a relay. The `Message` value of the Sns object
// should not be parsed or processed.

const url = require('url');
const https = require('https');

exports.handler = (event, context, callback) => {
    try {
        const sns = event.Records[0].Sns;
        console.log(`SNS MessageId: ${sns.MessageId}`);
        console.log(`Channel: ${JSON.parse(sns.Message).channel}`);

        const attrs = sns.MessageAttributes;

        let webhookUrl = process.env.DEFAULT_WEBHOOK_URL;
        if (attrs.WebhookURL) {
            webhookUrl = attrs.WebhookURL.Value;
        }

        // Setup request options
        const options = url.parse(webhookUrl);
        options.method = 'POST';
        options.headers = {
            'Content-Type': 'application/json',
            'Content-Length': Buffer.byteLength(sns.Message),
        };

        const req = https.request(options, (res) => {
            res.setEncoding('utf8');

            res.on('end', () => {
                if (res.statusCode < 400) {
                    callback(null, 'Slack message sent');
                } else if (res.statusCode < 500) {
                    callback(new Error('Slack client Error'));
                } else {
                    callback(new Error('Slack server Error'));
                }
            });
        });

        // Generic request error handling
        req.on('error', e => callback(e));

        req.write(sns.Message);
        req.end();
    } catch (e) {
        callback(e);
    }
};
<commit_msg>Clean up Slack relay Lambda<commit_after>// Invoked by: SNS Subscription
// Returns: Error or status message
//
// Recieves messages from an SNS topic subscription that contain preformed Slack
// message payloads (as JSON data). Makes a request to a Slack Incoming Webhook
// URL (also provided by the SNS message) with the payload.
//
// This is a very simple function, and should not make any decisions about where
// to send messages. It is just a relay. The `Message` value of the Sns object
// should not be parsed or processed.

const https = require('https');

exports.handler = (event, context, callback) => {
    try {
        const sns = event.Records[0].Sns;
        console.log(JSON.stringify({
            "SNS MessageId": sns.MessageId,
            "Slack channel": JSON.parse(sns.Message).channel
        }));

        const attrs = sns.MessageAttributes;

        let webhookUrl = process.env.DEFAULT_WEBHOOK_URL;
        if (attrs.WebhookURL) {
            webhookUrl = attrs.WebhookURL.Value;
        }

        const q = new URL(webhookUrl);

        // Setup request options
        const options = {
            host: q.host,
            port: q.port,
            path: `${q.pathname || ''}${q.search || ''}`,
            headers: {
                'Content-Type': 'application/json',
                'Content-Length': Buffer.byteLength(sns.Message),
            },
            method: 'POST',
        };

        const req = https.request(options, (res) => {
            res.setEncoding('utf8');

            res.on('end', () => {
                if (res.statusCode < 400) {
                    callback(null, 'Slack message sent');
                } else if (res.statusCode < 500) {
                    callback(new Error('Slack client Error'));
                } else {
                    callback(new Error('Slack server Error'));
                }
            });
        });

        // Generic request error handling
        req.on('error', e => callback(e));

        req.write(sns.Message);
        req.end();
    } catch (e) {
        callback(e);
    }
};
<|endoftext|>