<commit_before>export default Ember.Controller.extend({
  isMultiple: Ember.computed.equal("signup.type", "multiple"),
  isNumber: Ember.computed.equal("signup.type", "number"),
  isRandom : Ember.computed.equal("signup.order", "random"),
  isClosed: Ember.computed.equal("signup.status", "closed"),

  // shows the results when
  //   - signup is closed
  //   - topic is archived/closed
  //   - user wants to see the results
  showingResults: Em.computed.or("isClosed", "post.topic.closed", "post.topic.archived", "showResults"),

  showResultsDisabled: Em.computed.equal("signup.voters", 0),
  hideResultsDisabled: Em.computed.alias("isClosed"),

  signup: function() {
    const signup = this.get("model"),
          vote = this.get("vote");

    if (signup) {
      const options = _.map(signup.get("options"), o => Em.Object.create(o));

      if (vote) {
        options.forEach(o => o.set("selected", vote.indexOf(o.get("id")) >= 0));
      }

      signup.set("options", options);
    }

    return signup;
  }.property("model"),

  selectedOptions: function() {
    return _.map(this.get("signup.options").filterBy("selected"), o => o.get("id"));
  }.property("signup.options.@each.selected"),

  min: function() {
    let min = parseInt(this.get("signup.min"), 10);
    if (isNaN(min) || min < 1) { min = 1; }
    return min;
  }.property("signup.min"),

  max: function() {
    let options = this.get("signup.options.length"),
        max = parseInt(this.get("signup.max"), 10);
    if (isNaN(max) || max > options) { max = options; }
    return max;
  }.property("signup.max", "signup.options.length"),

  votersText: function() {
    return I18n.t("signup.voters", { count: this.get("signup.voters") });
  }.property("signup.voters"),

  totalVotes: function() {
    return _.reduce(this.get("signup.options"), function(total, o) {
      return total + parseInt(o.get("votes"), 10);
    }, 0);
  }.property("signup.options.@each.votes"),

  totalVotesText: function() {
    return I18n.t("signup.total_votes", { count: this.get("totalVotes") });
  }.property("totalVotes"),

  multipleHelpText: function() {
    const options = this.get("signup.options.length"),
          min = this.get("min"),
          max = this.get("max");

    if (max > 0) {
      if (min === max) {
        if (min > 1) {
          return I18n.t("signup.multiple.help.x_options", { count: min });
        }
      } else if (min > 1) {
        if (max < options) {
          return I18n.t("signup.multiple.help.between_min_and_max_options", { min: min, max: max });
        } else {
          return I18n.t("signup.multiple.help.at_least_min_options", { count: min });
        }
      } else if (max <= options) {
        return I18n.t("signup.multiple.help.up_to_max_options", { count: max });
      }
    }
  }.property("min", "max", "signup.options.length"),

  canCastVotes: function() {
    if (this.get("isClosed") || this.get("loading")) {
      return false;
    }

    const selectedOptionCount = this.get("selectedOptions.length");

    if (this.get("isMultiple")) {
      return (selectedOptionCount >= this.get("min") && selectedOptionCount <= this.get("max")) || selectedOptionCount == 0;
    } else {
      return selectedOptionCount <= 1;
    }
  }.property("isClosed", "showingResults", "loading",
             "selectedOptions.length",
             "isMultiple", "min", "max"),

  castVotesDisabled: Em.computed.not("canCastVotes"),

  canToggleStatus: function() {
    return this.currentUser &&
           (this.currentUser.get("id") === this.get("post.user_id") || this.currentUser.get("staff")) &&
           !this.get("loading") &&
           !this.get("post.topic.closed") &&
           !this.get("post.topic.archived");
  }.property("loading", "post.user_id", "post.topic.{closed,archived}"),

  actions: {

    toggleOption(option) {
      if (this.get("isClosed")) { return; }
      if (!this.currentUser) { return this.send("showLogin"); }

      const wasSelected = option.get("selected");

      if (!this.get("isMultiple")) {
        this.get("signup.options").forEach(o => o.set("selected", false));
        // The line above just toggled the clicked option
        // If we don't toggle the option again, then the toggle below will force
        // the clicked option to stay permanently on for single-choice signups
        option.toggleProperty("selected");
      }

      option.toggleProperty("selected");

      //if (!this.get("isMultiple") && !wasSelected) { this.send("castVotes"); }
    },

    castVotes() {
      if (!this.get("canCastVotes")) { return; }
      if (!this.currentUser) { return this.send("showLogin"); }

      const self = this;

      this.set("loading", true);

      Discourse.ajax("/signups/vote", {
        type: "PUT",
        data: {
          post_id: this.get("post.id"),
          signup_name: this.get("signup.name"),
          options: this.get("selectedOptions"),
        }
      }).then(function(results) {
        self.setProperties({ vote: results.vote });
        self.set("model", Em.Object.create(results.signup));
      }).catch(function() {
        bootbox.alert(I18n.t("signup.error_while_casting_votes"));
      }).finally(function() {
        self.set("loading", false);
      });
    },

    toggleResults() {
      this.toggleProperty("showResults");
    },

    toggleStatus() {
      if (!this.get("canToggleStatus")) { return; }

      const self = this,
            confirm = this.get("isClosed") ? "signup.open.confirm" : "signup.close.confirm";

      bootbox.confirm(
        I18n.t(confirm),
        I18n.t("no_value"),
        I18n.t("yes_value"),
        function(confirmed) {
          if (confirmed) {
            self.set("loading", true);

            Discourse.ajax("/signups/toggle_status", {
              type: "PUT",
              data: {
                post_id: self.get("post.id"),
                signup_name: self.get("signup.name"),
                status: self.get("isClosed") ? "open" : "closed",
              }
            }).then(function(results) {
              self.set("model", Em.Object.create(results.signup));
            }).catch(function() {
              bootbox.alert(I18n.t("signup.error_while_toggling_status"));
            }).finally(function() {
              self.set("loading", false);
            });
          }
        }
      );

    },
  }

});
<commit_msg>I misread the loop and thought it toggled all options, but it sets them to false. So previous code couldn't turn a false option true.<commit_after>export default Ember.Controller.extend({
  isMultiple: Ember.computed.equal("signup.type", "multiple"),
  isNumber: Ember.computed.equal("signup.type", "number"),
  isRandom : Ember.computed.equal("signup.order", "random"),
  isClosed: Ember.computed.equal("signup.status", "closed"),

  // shows the results when
  //   - signup is closed
  //   - topic is archived/closed
  //   - user wants to see the results
  showingResults: Em.computed.or("isClosed", "post.topic.closed", "post.topic.archived", "showResults"),

  showResultsDisabled: Em.computed.equal("signup.voters", 0),
  hideResultsDisabled: Em.computed.alias("isClosed"),

  signup: function() {
    const signup = this.get("model"),
          vote = this.get("vote");

    if (signup) {
      const options = _.map(signup.get("options"), o => Em.Object.create(o));

      if (vote) {
        options.forEach(o => o.set("selected", vote.indexOf(o.get("id")) >= 0));
      }

      signup.set("options", options);
    }

    return signup;
  }.property("model"),

  selectedOptions: function() {
    return _.map(this.get("signup.options").filterBy("selected"), o => o.get("id"));
  }.property("signup.options.@each.selected"),

  min: function() {
    let min = parseInt(this.get("signup.min"), 10);
    if (isNaN(min) || min < 1) { min = 1; }
    return min;
  }.property("signup.min"),

  max: function() {
    let options = this.get("signup.options.length"),
        max = parseInt(this.get("signup.max"), 10);
    if (isNaN(max) || max > options) { max = options; }
    return max;
  }.property("signup.max", "signup.options.length"),

  votersText: function() {
    return I18n.t("signup.voters", { count: this.get("signup.voters") });
  }.property("signup.voters"),

  totalVotes: function() {
    return _.reduce(this.get("signup.options"), function(total, o) {
      return total + parseInt(o.get("votes"), 10);
    }, 0);
  }.property("signup.options.@each.votes"),

  totalVotesText: function() {
    return I18n.t("signup.total_votes", { count: this.get("totalVotes") });
  }.property("totalVotes"),

  multipleHelpText: function() {
    const options = this.get("signup.options.length"),
          min = this.get("min"),
          max = this.get("max");

    if (max > 0) {
      if (min === max) {
        if (min > 1) {
          return I18n.t("signup.multiple.help.x_options", { count: min });
        }
      } else if (min > 1) {
        if (max < options) {
          return I18n.t("signup.multiple.help.between_min_and_max_options", { min: min, max: max });
        } else {
          return I18n.t("signup.multiple.help.at_least_min_options", { count: min });
        }
      } else if (max <= options) {
        return I18n.t("signup.multiple.help.up_to_max_options", { count: max });
      }
    }
  }.property("min", "max", "signup.options.length"),

  canCastVotes: function() {
    if (this.get("isClosed") || this.get("loading")) {
      return false;
    }

    const selectedOptionCount = this.get("selectedOptions.length");

    if (this.get("isMultiple")) {
      return (selectedOptionCount >= this.get("min") && selectedOptionCount <= this.get("max")) || selectedOptionCount == 0;
    } else {
      return selectedOptionCount <= 1;
    }
  }.property("isClosed", "showingResults", "loading",
             "selectedOptions.length",
             "isMultiple", "min", "max"),

  castVotesDisabled: Em.computed.not("canCastVotes"),

  canToggleStatus: function() {
    return this.currentUser &&
           (this.currentUser.get("id") === this.get("post.user_id") || this.currentUser.get("staff")) &&
           !this.get("loading") &&
           !this.get("post.topic.closed") &&
           !this.get("post.topic.archived");
  }.property("loading", "post.user_id", "post.topic.{closed,archived}"),

  actions: {

    toggleOption(option) {
      if (this.get("isClosed")) { return; }
      if (!this.currentUser) { return this.send("showLogin"); }

      const wasSelected = option.get("selected");

      if (!this.get("isMultiple")) {
        this.get("signup.options").forEach(o => o.set("selected", false));
      }

      option.set("selected", !wasSelected);

      //if (!this.get("isMultiple") && !wasSelected) { this.send("castVotes"); }
    },

    castVotes() {
      if (!this.get("canCastVotes")) { return; }
      if (!this.currentUser) { return this.send("showLogin"); }

      const self = this;

      this.set("loading", true);

      Discourse.ajax("/signups/vote", {
        type: "PUT",
        data: {
          post_id: this.get("post.id"),
          signup_name: this.get("signup.name"),
          options: this.get("selectedOptions"),
        }
      }).then(function(results) {
        self.setProperties({ vote: results.vote });
        self.set("model", Em.Object.create(results.signup));
      }).catch(function() {
        bootbox.alert(I18n.t("signup.error_while_casting_votes"));
      }).finally(function() {
        self.set("loading", false);
      });
    },

    toggleResults() {
      this.toggleProperty("showResults");
    },

    toggleStatus() {
      if (!this.get("canToggleStatus")) { return; }

      const self = this,
            confirm = this.get("isClosed") ? "signup.open.confirm" : "signup.close.confirm";

      bootbox.confirm(
        I18n.t(confirm),
        I18n.t("no_value"),
        I18n.t("yes_value"),
        function(confirmed) {
          if (confirmed) {
            self.set("loading", true);

            Discourse.ajax("/signups/toggle_status", {
              type: "PUT",
              data: {
                post_id: self.get("post.id"),
                signup_name: self.get("signup.name"),
                status: self.get("isClosed") ? "open" : "closed",
              }
            }).then(function(results) {
              self.set("model", Em.Object.create(results.signup));
            }).catch(function() {
              bootbox.alert(I18n.t("signup.error_while_toggling_status"));
            }).finally(function() {
              self.set("loading", false);
            });
          }
        }
      );

    },
  }

});
<|endoftext|>


<commit_before>module.exports = function (grunt) {
	'use strict';

	/**
	 * Project configuration
	 */
	grunt.initConfig({
		pkg: require('./package'), // <%%= pkg.name %>

		/**
		 * Config - Edit this section
		 * ==========================
		 * Choose javascript dist filename
		 * Choose javascript dist location
		 * Choose javascript files to be uglified
		 */
		config : {
			js : {
				// <%%= config.js.distDir %>
				distDir  : 'js/dist/',

				// <%%= config.js.distFile %>
				distFile : 'app.min.js',

				// <%%= config.js.fileList %>
				fileList : [
					'js/helpers/helpers.js',
					'js/helpers/console.js',
					<% if (jsLibs == 'jquery') {%>'js/libs/jquery/jquery-1.10.2.js',<% } %>
					<% if (jsMobileDev == true) {%>'/bower_components/swiftclick/js/dist/swiftclick.min.js',<% } %>
					'js/script.js',
				]
			}
		},

		/**
		 * JSHint
		 * https://github.com/gruntjs/grunt-contrib-jshint
		 * Manage the options inside .jshintrc file
		 */
		jshint: {
			all: jsFileList,
			options: {
				jshintrc: '.jshintrc'
			}
		},

		/**
		 * Sass compilation
		 * https://github.com/gruntjs/grunt-contrib-sass
		 * Separate options for dev and production environments
		 * Includes kickoff.scss and kickoff-old-ie.scss by default
		 * Also creates source maps
		 */
		sass: {
			dev: {
				options: {
					unixNewlines: true,
					style: 'expanded',
					lineNumbers: false,
					debugInfo : false,
					precision : 8,
					sourcemap : true
				},
				files: {
					'css/<%= _.slugify(projectName) %>.css': 'scss/kickoff.scss',
					'css/<%= _.slugify(projectName) %>-old-ie.css': 'scss/kickoff-old-ie.scss'
				}
			},
			production: {
				options: {
					style: 'compressed',
					precision : 8,
					sourcemap : true
				},
				files: {
					'css/<%= _.slugify(projectName) %>.css': 'scss/kickoff.scss',
					'css/<%= _.slugify(projectName) %>-old-ie.css': 'scss/kickoff-old-ie.scss'
				}
			},
			styleguide: {
				options: {
					unixNewlines: true,
					style: 'expanded',
					precision : 8,
					sourcemap : true
				},
				files: {
					'css/styleguide.css': 'scss/styleguide.scss'
				}
			}
		},

		/**
		 * Uglify
		 * https://github.com/gruntjs/grunt-contrib-uglify
		 * Minifies and concatinates your JS
		 * Also creates source maps
		 */
		uglify: {
			options: {
				// mangle: Turn on or off mangling
				mangle: true,

				// beautify: beautify your code for debugging/troubleshooting purposes
				beautify: false,

				// report: Show file size report
				report: 'gzip',

				// sourceMap: @string. The location of the source map, relative to the project
				sourceMap: distDir + jsFile + '.map',

				// sourceMappingURL: @string. The string that is printed to the final file
				sourceMappingURL: jsFile +'.map',

				// sourceMapRoot: @string. The location where your source files can be found. This sets the sourceRoot field in the source map.
				sourceMapRoot: '../../'

			},

			/**
			 * Use the array at the top of this file to specify which js files you include
			 */
			js: {
				src: jsFileList,
				dest: distDir + jsFile
			}
		},

		/**
		 * Watch
		 * https://github.com/gruntjs/grunt-contrib-watch
		 * Watches your scss, js etc for changes and compiles them
		 */
		watch: {
			scss: {
				files: ['scss/**/*.scss'],
				tasks: ['sass:dev', 'sass:styleguide']
				// tasks: ['sass:dev', 'autoprefixer:dist', 'csso']
			},

			js: {
				files: [
					'Gruntfile.js',
					'js/*.js',
					'js/libs/**/*.js'
				],
				tasks: ['uglify']
			},

			livereload: {
				options: { livereload: true },
				files: [
					'css/*.css'
				]
			}
		},

		/**
		 * Connect
		 * https://github.com/gruntjs/grunt-contrib-connect
		 * Start a static web server
		 */
		connect: {
			server: {
				options: {
					// port: 9001,
					open: true,
					livereload: true
				}
			}
		},

		/**
		 * Autoprefixer
		 * https://github.com/ai/autoprefixer
		 * Auto prefixes your CSS using caniuse data
		 */
		autoprefixer: {
			dist : {
				options: {
					// Task-specific options go here - we are supporting
					// the last 2 browsers, any browsers with >1% market share,
					// and ensuring we support IE7 + 8 with prefixes
					browsers: ['last 2 versions', '> 1%', 'ie 8', 'ie 7']
				},
				files: {
					'css/<%= _.slugify(projectName) %>.prefixed.css': 'css/<%= _.slugify(projectName) %>.css',
					'css/<%= _.slugify(projectName) %>-old-ie.prefixed.css': 'css/<%= _.slugify(projectName) %>-old-ie.css'
				}
			}
		},

		/**
		 * CSSO
		 * https://github.com/t32k/grunt-csso
		 * Minify CSS files with CSSO
		 */
		csso: {
			dist: {
				files: {
					'css/<%= _.slugify(projectName) %>.min.css': ['css/<%= _.slugify(projectName) %>.prefixed.css'],
					'css/<%= _.slugify(projectName) %>-old-ie.min.css': ['css/<%= _.slugify(projectName) %>-old-ie.prefixed.css']
				},

			}
		}

		<% if (jsLibs == 'jquery') {%>,

		/**
		 * Custom jQuery builder
		 * Check build numbers at jquery.com
		 */
		jquery: {
			build: {
				options: {
					prefix: "jquery-",
					minify: true
				},
				output: "js/libs/jquery",
				versions: {
					// Add items to the below arrays to remove them from the build
					// Remove everything we don't need from 2.x versions
					"2.0.3": [ "deprecated", "dimensions", "offset", "wrap"],

					// We can't remove sizzle from 1.x versions, so let's not specify it
					"1.10.2": [ "deprecated", "dimensions", "offset", "wrap"]
				}
			}
		}
		<% } %>
	});

	// Load all the grunt tasks
	require('load-grunt-tasks')(grunt);

	/**
	 * Available tasks:
	   * grunt        : run jshint, uglify and sass:dev
	   * grunt watch  : run sass:dev, uglify and livereload
	   * grunt dev    : run jshint, uglify and sass:dev
	   * grunt deploy : run jshint, uglify and sass:production
	   * grunt jquery : build custom version of jquery
	 */

	/**
	 * Default task
	 * run jshint, uglify and sass:dev
	 */
	// Default task
	grunt.registerTask('default', ['jshint', 'uglify', 'sass:dev']);

	/**
	 * A task for development
	 * run jshint, uglify and sass:dev
	 */
	grunt.registerTask('dev', ['jshint', 'uglify', 'sass:dev']);

	/**
	 * A task for your production environment
	 * run jshint, uglify and sass:production
	 */
	grunt.registerTask('deploy', ['jshint', 'uglify', 'sass:production']);
	// grunt.registerTask('production', ['jshint', 'uglify', 'sass:production', 'autoprefixer', 'csso']);

	/**
	 * A task for for a static server with a watch
	 * run connect and watch
	 */
	grunt.registerTask("serve", ["connect", "watch"]);

};
<commit_msg>- reorganise and update entire Gruntfile - added grunticon, svgmin, csso, autoprefixer by default<commit_after>module.exports = function (grunt) {
	'use strict';

	/**
	 * Project configuration
	 */
	grunt.initConfig({
		pkg: require('./package'), // <%%= pkg.name %>

		/**
		 * Config - Edit this section
		 * ==========================
		 * Choose javascript dist filename
		 * Choose javascript dist location
		 * Choose javascript files to be uglified
		 */
		config : {
			js : {
				// <%%= config.js.distDir %>
				distDir  : 'js/dist/',

				// <%%= config.js.distFile %>
				distFile : 'app.min.js',

				// <%%= config.js.fileList %>
				fileList : [
					'js/helpers/helpers.js',
					'js/helpers/console.js',
					<% if (jsLibs == 'jquery') {%>'js/libs/jquery/jquery-1.10.2.js',<% } %>
					<% if (jsMobileDev == true) {%>'/bower_components/swiftclick/js/dist/swiftclick.min.js',<% } %>
					'js/script.js',
				]
			}
		},

		/**
		 * Watch
		 * https://github.com/gruntjs/grunt-contrib-watch
		 * Watches your scss, js etc for changes and compiles them
		 */
		watch: {
			scss: {
				files: ['scss/**/*.scss'],
				tasks: ['sass:kickoff', 'sass:styleguide', 'autoprefixer:dist']
			},

			js: {
				files: ['<%%= config.js.fileList %>', 'Gruntfile.js'],
				tasks: ['uglify']
			},

			livereload: {
				options: { livereload: true },
				files: [
					'css/*.css'
				]
			},

			grunticon : {
				files: ['img/src/*.svg', 'img/src/*.png'],
				tasks: ['svgmin', 'grunticon']
			}
		},

		/**
		 * Sass compilation
		 * https://github.com/gruntjs/grunt-contrib-sass
		 * Includes kickoff.scss and kickoff-old-ie.scss by default
		 * Also creates source maps
		 */
		sass: {
			kickoff: {
				options: {
					unixNewlines: true,
					style: 'expanded',
					lineNumbers: false,
					debugInfo : false,
					precision : 8,
					sourcemap: true
				},
				files: {
					'css/<%= _.slugify(projectName) %>.css'       : 'scss/kickoff.scss',
					'css/<%= _.slugify(projectName) %>-old-ie.css': 'scss/kickoff-old-ie.scss'
				}
			},
			styleguide: {
				options: {
					unixNewlines: true,
					style: 'expanded',
					precision : 8,
					sourcemap: true
				},
				files: {
					'css/styleguide.css': 'scss/styleguide.scss'
				}
			}
		},

		/**
		 * Autoprefixer
		 * https://github.com/nDmitry/grunt-autoprefixer
		 * https://github.com/ai/autoprefixer
		 * Auto prefixes your CSS using caniuse data
		 */
		autoprefixer: {
			dist : {
				options: {
					// Task-specific options go here - we are supporting
					// the last 2 browsers, any browsers with >1% market share,
					// and ensuring we support IE7 + 8 with prefixes
					browsers: ['> 5%', 'last 4 versions', 'firefox > 3.6', 'ie > 6'],
					map: true
				},
				files: {
					'css/<%= _.slugify(projectName) %>.css'       : 'css/kickoff.css',
					'css/<%= _.slugify(projectName) %>-old-ie.css': 'css/kickoff-old-ie.css',
					'css/styleguide.css'    : 'css/styleguide.css'
				}
			}
		},

		/**
		 * Uglify
		 * https://github.com/gruntjs/grunt-contrib-uglify
		 * Minifies and concatinates your JS
		 * Also creates source maps
		 */
		uglify: {
			options: {

				mangle: true, // mangle: Turn on or off mangling
				beautify: false, // beautify: beautify your code for debugging/troubleshooting purposes
				compress: false,
				// report: 'gzip', // report: Show file size report
				sourceMap: '<%%= config.js.distDir %><%%= config.js.distFile %>.map',
				sourceMappingURL: '/<%%= config.js.distFile %>.map',
			},
			js: {
				src: '<%%= config.js.fileList %>',
				dest: '<%%= config.js.distDir %><%%= config.js.distFile %>'
			}
		},

		/**
		 * Grunticon
		 * https://github.com/filamentgroup/grunticon
		 */
		grunticon: {
			myIcons: {
				files: [{
					expand: true,
					cwd   : 'img/src-min',
					src   : ['*.svg', '*.png'],
					dest  : 'img/icons'
				}],
				options: {
					// customselectors: {
					// 	"*": [".icon-$1:before"]
					// }
				}
			}
		},

		/**
		 * SVGmin
		 * https://github.com/sindresorhus/grunt-svgmin
		 */
		svgmin: {
			options: {
				plugins: [
					{ removeViewBox: false },
					{ removeUselessStrokeAndFill: false }
				]
			},
			dist: {                     // Target
				files: [{               // Dictionary of files
					expand: true,       // Enable dynamic expansion.
					cwd: 'img/src',     // Src matches are relative to this path.
					src: ['**/*.svg'],  // Actual pattern(s) to match.
					dest: 'img/src-min',       // Destination path prefix.
					ext: '.svg'     // Dest filepaths will have this extension.
					// ie: optimise img/src/branding/logo.svg and store it in img/branding/logo.min.svg
				}]
			}
		},

		/**
		 * CSSO
		 * https://github.com/t32k/grunt-csso
		 * Minify CSS files with CSSO
		 */
		csso: {
			dist: {
				files: {
					'css/<%= _.slugify(projectName) %>.css'       : 'css/kickoff.css',
					'css/<%= _.slugify(projectName) %>-old-ie.css': 'css/kickoff-old-ie.css'
				},

			}
		},

		/**
		 * Connect
		 * https://github.com/gruntjs/grunt-contrib-connect
		 * Start a static web server
		 */
		connect: {
			server: {
				options: {
					// port: 9001,
					// hostname: 'mysite.local',
					open: true,
					livereload: true
				}
			}
		},

		/**
		 * JSHint
		 * https://github.com/gruntjs/grunt-contrib-jshint
		 * Manage the options inside .jshintrc file
		 */
		jshint: {
			all: '<%%= config.js.fileList %>',
			options: {
				jshintrc: '.jshintrc'
			}
		},

		/**
		 * JSCS
		 * https://github.com/dsheiko/grunt-jscs
		 * Manage the options inside .jscs.json file
		 */
		jscs: {
			src: '<%%= config.js.fileList %>',
			options: {
				config: ".jscs.json"
			}
		},

		<% if (jsLibs == 'jquery') {%>,
		/**
		 * Custom jQuery builder
		 * Check build numbers at jquery.com
		 */
		jquery: {
			build: {
				options: {
					prefix: "jquery-",
					minify: true
				},
				output: "js/libs/jquery",
				versions: {
					// Add items to the below arrays to remove them from the build
					// Remove everything we don't need from 2.x versions
					//"2.0.3": [ "deprecated", "dimensions", "offset", "wrap"],

					// We can't remove sizzle from 1.x versions, so let's not specify it
					"1.10.2": [ "deprecated"]
				}
			}
		}
		<% } %>
	});

	// Load all the grunt tasks
	require('load-grunt-tasks')(grunt);

	/* ==========================================================================
		Available tasks:
		* grunt        : run jshint, uglify and sass:kickoff
		* grunt watch  : run sass:kickoff, uglify and livereload
		* grunt dev    : run jshint, uglify and sass:kickoff
		* grunt deploy : run jshint, uglify, sass:kickoff and csso
		* grunt jquery : build custom version of jquery
		* grunt serve  : watch js & scss and run a local server
		* grunt availabletasks : view all available tasks
		 ========================================================================== */

	/**
	 * GRUNT * Default task
	 * run jshint, uglify and sass:kickoff
	 */
	// Default task
	grunt.registerTask('default', [
		'jshint',
		'uglify',
		'sass:kickoff',
		'autoprefixer:dist'
	]);

	/**
	 * GRUNT DEV * A task for development
	 * run jshint, uglify and sass:kickoff
	 */
	grunt.registerTask('dev', [
		'uglify',
		'sass:kickoff',
		'autoprefixer:dist'
	]);

	/**
	 * GRUNT DEPLOY * A task for your production environment
	 * run jshint, uglify and sass:production
	 */
	grunt.registerTask('deploy', [
		'uglify',
		'sass:kickoff',
		'autoprefixer:dist',
		'csso'
	]);

	/**
	 * GRUNT SERVE * A task for for a static server with a watch
	 * run connect and watch
	 */
	grunt.registerTask("serve", [
		'uglify',
		'sass:kickoff',
		'sass:styleguide',
		'autoprefixer:dist',
		'connect',
		'watch'
	]);

	/**
	 * TODO:
	 * Need task to update all grunt dependencies
	 * Need task to download all bower dependencies
	 */

};
<|endoftext|>


<commit_before>'use strict';

// External deps
const escapeHTML = require('escape-html');

// Internal deps
const languages = require('../../locales/languages');
const { validateFiles, cleanupFiles, getFileRevs } = require('../uploads');
const ReportedError = require('../../util/reported-error');
const File = require('../../models/file');
const api = require('../helpers/api');

/**
 * The main handler for processing upload attempts via the API. Kicks in after
 * the basic MIME check within the Multer middleware. Handles metadata
 * validation & creation of "File" revisions.
 *
 * @param {IncomingMessage} req
 *  Express request
 * @param {ServerResponse} res
 *  Express response
 * @returns {Function}
 *  callback invoked by the Multer middleware
 */
module.exports = function apiUploadHandler(req, res) {
  return fileFilterError => {

    // Status code will be used for known errors from the app, not for errors
    // from multer or unknown errors
    const abortUpload = (errors = []) => {
      cleanupFiles(req);
      const errorMessages = errors.map(error => error instanceof ReportedError ?
        req.__(...error.getEscapedUserMessageArray()) :
        error.message
      );
      api.error(req, res, errorMessages);
    };

    if (fileFilterError)
      return abortUpload([fileFilterError]);

    if (!req.files.length)
      return abortUpload([new Error('No files received.')]);

    const validationErrors = validateAllMetadata(req.files, req.body);
    if (validationErrors.length)
      return abortUpload(validationErrors);

    validateFiles(req.files)
      .then(fileTypes => getFileRevs(req.files, fileTypes, req.user, ['upload', 'upload-via-api']))
      .then(fileRevs => addMetadata(req.files, fileRevs, req.body))
      .then(fileRevs => Promise.all(fileRevs.map(fileRev => fileRev.save())))
      .then(() => reportUploadSuccess(req, res))
      .catch(error => abortUpload([error]));
  };
};

/**
 * Ensure that required fields have been submitted for each upload.
 *
 * @param {Object[]} files
 *  Files to validate
 * @param {Object} data
 *  Request data
 * @returns {Error[]}
 *  Validation errors, if any.
 */
function validateAllMetadata(files, data) {
  const errors = [],
    processedFields = ['multiple'],
    multiple = Boolean(data.multiple);

  if (files.length > 1 && !multiple)
    errors.push(new Error(`Received more than one file, but 'multiple' flag is not set.`));

  for (let file of files) {
    const validationResult = validateMetadata(file, data, { addSuffix: multiple });
    errors.push(...validationResult.errors);
    processedFields.push(...validationResult.processedFields);
  }
  const remainingFields = Object.keys(data).filter(key => !processedFields.includes(key));
  if (remainingFields.length > 0)
    errors.push(new Error(`Unknown or conflicting parameter(s): ${remainingFields.join(', ')}`));

  return errors;
}

/**
 * Check that required metadata fields are present for a given upload. Also
 * ensures that language is valid, and that license is one of the accepted
 * licenses.
 *
 * @param {Object} file
 *  File received from the upload middleware
 * @param {Object} data
 *  Request data that should contain the metadata we need
 * @param {Object} [options]
 *  Validation options
 * @param {Boolean} options.addSuffix=false
 *  Add a filename suffix to each field (used for requests with multiple files)
 * @returns {Error[]}
 *  Validation errors for this field, if any
 */
function validateMetadata(file, data, { addSuffix = false } = {}) {
  const validLicenses = File.getValidLicenses();
  const errors = [],
    processedFields = [];
  // For multiple uploads, we use the filename as a suffix for each file
  const field = key => addSuffix ? `${key}-${file.originalname}` : key;

  const requiredFields = data[field('ownwork')] ?
    ['description', 'license', 'ownwork', 'language'].map(field) :
    ['description', 'author', 'source', 'license', 'language'].map(field);

  errors.push(...checkRequired(data, requiredFields));
  processedFields.push(...requiredFields);

  const language = data[field('language')];

  if (language && !languages.isValid(language))
    errors.push(new Error(`Language ${language} is not valid or recognized.`));

  const license = data[field('license')];
  if (license && !validLicenses.includes(license))
    errors.push(new Error(`License ${license} is not one of: ` +
      validLicenses.join(', ')));

  return { errors, processedFields };
}

/**
 * Check if we have "truthy" values for all required fields in a given object
 * (typically an API request body)
 *
 * @param {Object} obj
 *  any object whose keys we want to validate
 * @param {String[]} [required=[]]
 *  keys which must access a truthy value
 * @returns {Error[]}
 *  errors for each validation issue or an empty array
 */
function checkRequired(obj, required = []) {
  // Make a copy since we modify it below
  required = required.slice();

  const errors = [];
  for (let key in obj) {
    if (required.includes(key)) {
      if (!obj[key])
        errors.push(new Error(`Parameter must not be empty: ${key}`));
      required.splice(required.indexOf(key), 1);
    }
  }
  if (required.length)
    errors.push(new Error(`Missing the following parameter(s): ${required.join(', ')}`));

  return errors;
}

/**
 * Add all metadata to each file revision (does not save)
 *
 * @param {Object[]} files
 *  file objects received from the middleware
 * @param {File[]} fileRevs
 *  initial revisions of the File model, containing only the data that comes
 *  with the file itself (MIME type, filename, etc.)
 * @param {Ojbect} data
 *  request data
 * @returns {File[]}
 *  the revisions for further processing
 */
function addMetadata(files, fileRevs, data) {
  const multiple = Boolean(data.multiple);
  fileRevs.forEach((fileRev, index) =>
    addMetadataToFileRev(files[index], fileRevs[index], data, { addSuffix: multiple })
  );
  return fileRevs;
}

/**
 * Add all relevant metadata to an individual file revision
 *
 * @param {Object} file
 *  file object from the middleware
 * @param {File} fileRev
 *  initial revision of the File model
 * @param {Object} data
 *  request data
 * @param {Object} [options]
 *  Validation options
 * @param {Boolean} options.addSuffix=false
 *  Add a filename suffix to each field (used for requests with multiple files)
 */
function addMetadataToFileRev(file, fileRev, data, { addSuffix = false } = {}) {
  const field = key => addSuffix ? `${key}-${file.originalname}` : key;
  const addMlStr = (keys, rev) => {
    for (let key of keys)
      if (data[field(key)])
        rev[key] = {
          [data[field('language')]]: escapeHTML(data[field(key)])
        };
  };
  addMlStr(['description', 'creator', 'source'], fileRev);
  fileRev.license = data[field('license')];
}

/**
 * Send a success response to the API request that contains the newly assigned
 * filenames, so they can be used, e.g. in the editor.
 *
 * @param {IncomingMessage} req
 *  Express request
 * @param {ServerResponse} res
 *  Express response
 */
function reportUploadSuccess(req, res) {
  const uploads = req.files.map(file => ({
    originalName: file.originalname,
    uploadedFileName: file.filename
  }));
  res.status(200);
  res.send(JSON.stringify({
    message: 'Upload successful.',
    uploads,
    errors: []
  }, null, 2));
}
<commit_msg>Upload API: ignore conflicting parameters if "falsy"; return file ID<commit_after>'use strict';

// External deps
const escapeHTML = require('escape-html');

// Internal deps
const languages = require('../../locales/languages');
const { validateFiles, cleanupFiles, getFileRevs } = require('../uploads');
const ReportedError = require('../../util/reported-error');
const File = require('../../models/file');
const api = require('../helpers/api');

/**
 * The main handler for processing upload attempts via the API. Kicks in after
 * the basic MIME check within the Multer middleware. Handles metadata
 * validation & creation of "File" revisions.
 *
 * @param {IncomingMessage} req
 *  Express request
 * @param {ServerResponse} res
 *  Express response
 * @returns {Function}
 *  callback invoked by the Multer middleware
 */
module.exports = function apiUploadHandler(req, res) {
  return fileFilterError => {

    // Status code will be used for known errors from the app, not for errors
    // from multer or unknown errors
    const abortUpload = (errors = []) => {
      cleanupFiles(req);
      const errorMessages = errors.map(error => error instanceof ReportedError ?
        req.__(...error.getEscapedUserMessageArray()) :
        error.message
      );
      api.error(req, res, errorMessages);
    };

    if (fileFilterError)
      return abortUpload([fileFilterError]);

    if (!req.files.length)
      return abortUpload([new Error('No files received.')]);

    const validationErrors = validateAllMetadata(req.files, req.body);
    if (validationErrors.length)
      return abortUpload(validationErrors);

    validateFiles(req.files)
      .then(fileTypes => getFileRevs(req.files, fileTypes, req.user, ['upload', 'upload-via-api']))
      .then(fileRevs => addMetadata(req.files, fileRevs, req.body))
      .then(fileRevs => Promise.all(fileRevs.map(fileRev => fileRev.save())))
      .then(fileRevs => reportUploadSuccess(req, res, fileRevs))
      .catch(error => abortUpload([error]));
  };
};

/**
 * Ensure that required fields have been submitted for each upload.
 *
 * @param {Object[]} files
 *  Files to validate
 * @param {Object} data
 *  Request data
 * @returns {Error[]}
 *  Validation errors, if any.
 */
function validateAllMetadata(files, data) {
  const errors = [],
    processedFields = ['multiple'],
    multiple = Boolean(data.multiple);

  if (files.length > 1 && !multiple)
    errors.push(new Error(`Received more than one file, but 'multiple' flag is not set.`));

  for (let file of files) {
    const validationResult = validateMetadata(file, data, { addSuffix: multiple });
    errors.push(...validationResult.errors);
    processedFields.push(...validationResult.processedFields);
  }
  const remainingFields = Object.keys(data).filter(key => !processedFields.includes(key));
  if (remainingFields.length > 0)
    errors.push(new Error(`Unknown parameter(s): ${remainingFields.join(', ')}`));

  return errors;
}

/**
 * Check that required metadata fields are present for a given upload. Also
 * ensures that language is valid, and that license is one of the accepted
 * licenses.
 *
 * @param {Object} file
 *  File received from the upload middleware
 * @param {Object} data
 *  Request data that should contain the metadata we need
 * @param {Object} [options]
 *  Validation options
 * @param {Boolean} options.addSuffix=false
 *  Add a filename suffix to each field (used for requests with multiple files)
 * @returns {Error[]}
 *  Validation errors for this field, if any
 */
function validateMetadata(file, data, { addSuffix = false } = {}) {
  const validLicenses = File.getValidLicenses();
  const errors = [],
    processedFields = [];
  // For multiple uploads, we use the filename as a suffix for each file
  const field = key => addSuffix ? `${key}-${file.originalname}` : key;
  const ownWork = Boolean(data[field('ownwork')]);

  const required = ownWork ?
    ['description', 'license', 'ownwork', 'language'].map(field) :
    ['description', 'author', 'source', 'license', 'language'].map(field);

  // We ignore presence/content of these conflicting fields if they are "falsy",
  // otherwise we report an error
  const conditionallyIgnored = ownWork ?
    ['author', 'source'].map(field) :
    ['ownwork'].map(field);

  errors.push(...checkRequired(data, required, conditionallyIgnored));
  processedFields.push(...required, ...conditionallyIgnored);

  const language = data[field('language')];

  if (language && !languages.isValid(language))
    errors.push(new Error(`Language ${language} is not valid or recognized.`));

  const license = data[field('license')];
  if (license && !validLicenses.includes(license))
    errors.push(new Error(`License ${license} is not one of: ` +
      validLicenses.join(', ')));

  return { errors, processedFields };
}

/**
 * Check if we have "truthy" values for all required fields in a given object
 * (typically an API request body). Also throws errors if given fields are
 * present with a "truthy" value, which is useful for conflicting parameters
 * that may be submitted with empty values.
 *
 * @param {Object} obj
 *  any object whose keys we want to validate
 * @param {String[]} [required=[]]
 *  keys which must access a truthy value
 * @param {String[]} [conditionallyIgnored=[]]
 *  keys which will be ignored _unless_ they access a truthy value

 * @returns {Error[]}
 *  errors for each validation issue or an empty array
 */
function checkRequired(obj, required = [], conditionallyIgnored = []) {
  // Make a copy since we modify it below
  required = required.slice();

  const errors = [];
  for (let key in obj) {
    if (required.includes(key)) {
      if (!obj[key])
        errors.push(new Error(`Parameter must not be empty: ${key}`));
      required.splice(required.indexOf(key), 1);
    }
    if (conditionallyIgnored.includes(key) && Boolean(obj[key]))
      errors.push(new Error(`Parameter must be skipped, be empty, or evaluate to false: ${key}`));

  }
  if (required.length)
    errors.push(new Error(`Missing the following parameter(s): ${required.join(', ')}`));

  return errors;
}

/**
 * Add all metadata to each file revision (does not save)
 *
 * @param {Object[]} files
 *  file objects received from the middleware
 * @param {File[]} fileRevs
 *  initial revisions of the File model, containing only the data that comes
 *  with the file itself (MIME type, filename, etc.)
 * @param {Ojbect} data
 *  request data
 * @returns {File[]}
 *  the revisions for further processing
 */
function addMetadata(files, fileRevs, data) {
  const multiple = Boolean(data.multiple);
  fileRevs.forEach((fileRev, index) =>
    addMetadataToFileRev(files[index], fileRevs[index], data, { addSuffix: multiple })
  );
  return fileRevs;
}

/**
 * Add all relevant metadata to an individual file revision
 *
 * @param {Object} file
 *  file object from the middleware
 * @param {File} fileRev
 *  initial revision of the File model
 * @param {Object} data
 *  request data
 * @param {Object} [options]
 *  Validation options
 * @param {Boolean} options.addSuffix=false
 *  Add a filename suffix to each field (used for requests with multiple files)
 */
function addMetadataToFileRev(file, fileRev, data, { addSuffix = false } = {}) {
  const field = key => addSuffix ? `${key}-${file.originalname}` : key;
  const addMlStr = (keys, rev) => {
    for (let key of keys)
      if (data[field(key)])
        rev[key] = {
          [data[field('language')]]: escapeHTML(data[field(key)])
        };
  };
  addMlStr(['description', 'creator', 'source'], fileRev);
  fileRev.license = data[field('license')];
}

/**
 * Send a success response to the API request that contains the newly assigned
 * filenames, so they can be used, e.g. in the editor.
 *
 * @param {IncomingMessage} req
 *  Express request
 * @param {ServerResponse} res
 *  Express response
 * @param {File[]} fileRevs
 *  the saved file metadata revisions
 */
function reportUploadSuccess(req, res, fileRevs) {
  const uploads = req.files.map((file, index) => ({
    originalName: file.originalname,
    uploadedFileName: file.filename,
    fileID: fileRevs[index].id
  }));
  res.status(200);
  res.send(JSON.stringify({
    message: 'Upload successful.',
    uploads,
    errors: []
  }, null, 2));
}
<|endoftext|>


<commit_before>//a workaround for wrapper not finished loading before the content displays, the finish loading of wrapper hides the loader
DELAY_LOADING = true;

function AdminTool (userId) {
  this.requestsCounter = 0;
  this.userId = userId;
  this.userOrgs = [];
  this.ajaxRequests = [];
  this.ajaxAborted = false;
  this.arrData = {};
  this.patientsIdList = [];
  OrgTool.call(this);
};

/*
* extends OrgTool class
* see OT class definition in main.js should modularize it in the future so it can be instantiated/called safely
*
*/
AdminTool.prototype = Object.create(OrgTool.prototype);
AdminTool.prototype.fadeLoader = function() {
  DELAY_LOADING = false;
  setTimeout(function() { $("#loadingIndicator").fadeOut(); }, 1000);
};
AdminTool.prototype.setLoadingMessageVis = function(vis) {
  switch(vis) {
    case "hide":
      $("#adminTable .field-loading-indicator").fadeOut();
      break;
    case "show":
      $("#adminTable .field-loading-indicator").fadeIn();
      break;
  };
};
AdminTool.prototype.setStatusLoadingVis = function(vis) {
  switch(vis) {
    case "hide":
      $("#adminTable th.status-field .loading-message-indicator").fadeOut();
      $("#adminTable .bootstrap-table-filter-control-status").css("opacity", 1);
      break;
    case "show":
      $("#adminTable th.status-field .loading-message-indicator").fadeIn();
      break;
  };
};
AdminTool.prototype.getPatientsIdList = function() {
  var self = this;
  if (self.patientsIdList.length == 0) {
    var all = $("#adminTable").bootstrapTable("getData");
    if (all) {
      all.forEach(function(o) {
        self.patientsIdList.push(o.id);
      });
    };
  };
  return self.patientsIdList;
};
AdminTool.prototype.getData = function(requests, callback) {
    var self = this;
    if (self.ajaxAborted) {
        return false;
    };
    if (!requests || requests.length == 0) {
        return false;
    };
    var userString = requests.shift();
    if (!hasValue(userString)) {
        return false;
    };
    /*
     *  load the data sequentially
     *  Note, NO concurrent ajax calls here,
     *  one request will wait after the previous one has finished
     */
    var ajaxRequest = $.ajax ({
                              type: "GET",
                              url: '/api/consent-assessment-status',
                              contentType: "application/json; charset=utf-8",
                              data: userString,
                              cache: false,
                              timeout: 5000,
                              beforeSend: function() {
                                self.setStatusLoadingVis("show");
                                /*
                                 * disable export functionality while status is being populated
                                 */
                                $("div.export button").attr("disabled", true);
                              },
                              dataType: 'json'
                          }).done(function(data) {
                                if (data && data.status) {
                                  data.status.forEach(function(status) {
                                      var c = status.consents;
                                      var a = "", s = "", prevItem = {};
                                      if (c) {
                                      c.forEach(function(item) {
                                          if (!item.consent.deleted && (!prevItem.consent_signed || (prevItem.assessment_status != item.assessment_status)
                                              || (String(prevItem.consent_signed).substring(0, 10) != String(item.consent.signed).substring(0, 10)))) {
                                              if (!(/null/.test(item.consent.agreement_url))) {
                                                var cl = "";
                                                var sd = tnthDates.formatDateString(item.consent.signed);
                                                var status = item.assessment_status;
                                                if (!item.consent.send_reminders) status = "withdrawn";
                                                switch(String(status).toLowerCase()) {
                                                    case "completed":
                                                      cl = "text-success";
                                                      break;
                                                    case "withdrawn":
                                                      cl = "text-muted";
                                                      break;
                                                    case "due":
                                                      cl = "text-warning";
                                                      break;
                                                    case "overdue":
                                                      cl = "text-danger";
                                                      break;
                                                };
                                                a += (a != "" ? "
" : "") + "" + status + "";
                                                s += (s != "" ? "
" : "") + sd;
                                                prevItem.assessment_status = item.assessment_status;
                                                prevItem.consent_signed = item.consent.signed;
                                              };
                                            };
                                        });
                                    };
                                    if (hasValue(status.user_id)) {
                                      /*
                                       * NOTE, need to get the row data here
                                       * data for all the fields are required for the method, updateByUniqueId
                                       */
                                      var rowData = $("#adminTable").bootstrapTable('getRowByUniqueId', status.user_id);
                                      rowData = rowData || {};
                                      /* update row data with updated assessment status */
                                      rowData["status"] = a;
                                      /* persist data here, help with debugging */
                                      self.arrData[status.user_id] = { id: status.user_id, row: rowData};
                                      $("#adminTable").bootstrapTable('updateByUniqueId', self.arrData[status.user_id]);

                                    };
                              });
                            };
                            if (requests.length > 0) {
                              self.getData(requests, callback);
                            }
                            else {
                              if (callback) setTimeout(function() { callback.call(self);}, 300);
                              self.setStatusLoadingVis("hide");
                              $("div.export button").attr("disabled", false);
                            };
                        }).fail(function(xhr) {
                            self.setStatusLoadingVis("hide");
                            $("div.export button").attr("disabled", false);
                            $("#admin-table-error-message").text("Server error occurred updating row data.  Server error code: " + xhr.status);
                        });
        self.ajaxRequests.push(ajaxRequest);
        return ajaxRequest;
};
AdminTool.prototype.updateData = function() {
  /* compile user IDs array from patients list ID */
  var arrUsers = this.getUserIdArray();
  var self = this;
  if (arrUsers.length > 0) {
     self.getData(arrUsers);
  };
};
AdminTool.prototype.abortRequests = function(callback, showLoader) {
    var self = this;
   //NEED TO ABORT THE AJAX REQUESTS OTHERWISE CLICK EVENT IS DELAYED DUE TO NETWORK TIE-UP
   if (self.ajaxRequests.length > 0) {
      self.ajaxAborted = true;
      self.ajaxRequests.forEach(function(request, index, array) {

          try {
            if (parseInt(request.readyState) != 4) {
                /*
                 * aborting the request here to quit immediately, instead of waiting for the
                 * maximum timeout specified
                 */
                request.timeout = 100;
                request.abort();
            }
          } catch(e) {
          };
          if (index == array.length - 1) {
            $("#admin-table-error-message").text("");
            if (callback) {
              setTimeout(function() { callback();}, 100);
              if (showLoader) loader(true)
            };
          };
      });
    } else {
      if (callback) setTimeout(function() { callback();}, 100);
    };

};
AdminTool.prototype.getUserIdArray = function(_userIds) {
  var us = "", ct = 0, arrUsers = [];
  if (!_userIds) {
     _userIds = this.getPatientsIdList();
  };
  var max_ct = Math.max(_userIds.length/10, 10);

  for (var index = 0; index < _userIds.length; index++, ct++) {
     us += (us != ""?"&":"") + "user_id=" + _userIds[index];
     if (index == (_userIds.length - 1)) {
       arrUsers.push(us);
     } else if (ct >= max_ct) {
        arrUsers.push(us);
        us = "";
        ct = 0;
     };
  };
  return arrUsers;
};
AdminTool.prototype.setUserOrgs = function() {
  var self = this;
  if (!hasValue(this.userId)) return false;
  $.ajax ({
          type: "GET",
          async: false,
          url: '/api/demographics/'+this.userId
  }).done(function(data) {
    if (data && data.careProvider) {
      $.each(data.careProvider,function(i,val){
          var orgID = val.reference.split("/").pop();
          self.userOrgs.push(orgID);
          if (orgID == "0") $("#createUserLink").attr("disabled", true);
      });
    if (self.userOrgs.length == 0) $("#createUserLink").attr("disabled", true);
    };
  }).fail(function() {

  });
};
AdminTool.prototype.getUserOrgs = function() {
  if (this.userOrgs.length == 0) this.setUserOrgs(this.userId);
  return this.userOrgs;
};
AdminTool.prototype.initOrgsList = function(request_org_list, context) {
    //set user orgs
    var self = this;
    self.setUserOrgs();

    //check if the location contains filtered orgs list
    var iterated = /org_list/.test(location.href);

    var noPatientData = $("#admin-table-body").find("tr.no-records-found").length > 0;

    $.ajax ({
        type: "GET",
        url: '/api/organization'
    }).done(function(data) {

        /*
         * building an orgs array object for reference later
         */
        self.populateOrgsList(data.entry);
        /*
         * populate orgs dropdown UI
         */
        self.populateUI();

        /*
         * filter orgs UI based on user's orgs
         */
        if (!noPatientData) {
            var hbOrgs = self.getHereBelowOrgs(self.getUserOrgs());
	          self.filterOrgs(hbOrgs);
        } else {
          $("div.fixed-table-toolbar").hide();
        };

        var ofields = $("#userOrgs input[name='organization']");
        if (ofields.length > 0) {
          /* attach orgs related events to UI components */
          ofields.each(function() {
            if ((self.getHereBelowOrgs(self.getUserOrgs())).length == 1 ||
                (iterated && request_org_list && request_org_list[$(this).val()])) {
                $(this).prop("checked", true);
            }
            $(this).on("click touchstart", function(e) {
                e.stopPropagation();
                AT.abortRequests();
                var orgsList = [];
                $("#userOrgs input[name='organization']").each(function() {
                   if ($(this).is(":checked")) orgsList.push($(this).val());
                });
               if (orgsList.length > 0) {
                  location.replace("/" + context + "?org_list=" + orgsList.join(","));
               } else location.replace("/" + context);
            });
          });

          $("#org-menu").append("

<input type='checkbox' id='orglist-selectall-ckbox'> Select All   <input type='checkbox' id='orglist-clearall-ckbox'> Clear All   <input type='checkbox' id='orglist-close-ckbox'> Close
");
          $("#orglist-selectall-ckbox").on("click touchstart", function(e) {
              e.stopPropagation();
              var orgsList = [];
              $("#userOrgs input[name='organization']").each(function() {
                  if ($(this).is(":visible")) {
                    $(this).prop("checked", true);
                    orgsList.push($(this).val());
                  };
              });
              $("#orglist-clearall-ckbox").prop("checked", false);
              if (orgsList.length > 0) location.replace("/" + context + "?org_list=" + orgsList.join(","));
          });
          $("#orglist-clearall-ckbox").on("click touchstart", function(e) {
              e.stopPropagation();
              $("#userOrgs input[name='organization']").each(function() {
                  $(this).prop("checked", false);
              });
          });
          $("#orglist-close-ckbox").on("click touchstart", function(e) {
              e.stopPropagation();
              $("#orglistSelector").trigger("click");
              return false;
          });
        };

    }).fail(function() {
        //console.log("Problem retrieving data from server.");
        $("#org-menu").append("Error occurred retrieving data from server.");
    });

    //orglist-dropdown
    $('#orglist-dropdown').on('click touchstart', function () {
        setTimeout('AT.__setOrgsMenuHeight(100); AT.__clearFilterButtons();', 10);
    });

    if (noPatientData) $("#patientListExportDataContainer").hide();

    /*
     * private functions used only within the context of the class
     */
    this.__setOrgsMenuHeight = function(padding) {
      if (!padding) padding = 100;
      var h = parseInt($("#fillOrgs").height());
      if (!isNaN(h) && h > 0) {
        $("#org-menu").height(h + padding);
        if ($("div.admin-table").height() < $("#org-menu").height()) {
            setTimeout('$("div.admin-table").height($("#org-menu").height() + ' + padding + ');', 0);
        };
      };
    };
    this.__clearFilterButtons = function() {
      $("#orglist-close-ckbox, #orglist-clearall-ckbox, #orglist-selectall-ckbox").prop("checked", false);
    };
};
AdminTool.prototype.getInstrumentList = function() {
  return {
    //CRV
    '10000': ['epic26', 'eproms_add', 'comorb'],
    //IRONMAN
    '20000': ['eortc', 'ironmisc', 'factfpsi', 'epic26', 'prems', 'irondemog']
  };
};

AdminTool.prototype.handleDownloadModal = function() {

    var self = this;
     /*
      *populate instruments list based on user's parent org
      */
    $("#dataDownloadModal").on('shown.bs.modal', function () {
          var parentOrgList = AT.getUserTopLevelParentOrgs(AT.getUserOrgs());
          console.dir(parentOrgList)
          if (parentOrgList && parentOrgList.length > 0) {
             var instrumentList = self.getInstrumentList();
             var instrumentItems = [];
             parentOrgList.forEach(function(o) {
                var il = instrumentList[o];
                if (il) {
                  il.forEach(function(n) {
                    instrumentItems.push(n);
                  });
                };
             });
             if (instrumentItems.length > 0) {
                $(".instrument-container").hide();
                instrumentItems.forEach(function(item) {
                  $("#" + item + "_container").show();
                });
             };
          };
          $("#patientsInstrumentList").addClass("ready");
    });
    /*
     * attach on click event to submit button in the download modal
     */
    $(document).delegate("#patientsDownloadButton", "click", function(event){
      var instruments = "", dataType = "";
      $("input[name='instrument'").each(function() {
          if ($(this).is(":checked")) {
            instruments += (instruments != "" ? "&": "") + "instrument_id="+$(this).val();
          };
      });
      $("input[name='downloadType']").each(function() {
          if ($(this).is(":checked")) dataType = $(this).val();
      });
      if (instruments != "" && dataType != "") {
          //alert(instruments)
          $("#_downloadMessage").text("");
          $("#_downloadLink").attr("href", "/api/patient/assessment?" + instruments + "&format=" + dataType);
          $("#_downloadLink")[0].click();
      } else {
          message = (instruments == "" ? "Please choose at least one instrument.": "");
          if (dataType == "") message += (message != "" ? "
": "") + "Please choose one download type.";
          $("#_downloadMessage").html(message);
      };
    });

    /*
     * attach event to each checkbox in the download instruments modal
     */
    $("input[name='instrument'], input[name='downloadType']").on("click", function() {
        if ($(this).is(":checked")) $("#_downloadMessage").text("");
    });
}

<commit_msg>removing console.dir call<commit_after>//a workaround for wrapper not finished loading before the content displays, the finish loading of wrapper hides the loader
DELAY_LOADING = true;

function AdminTool (userId) {
  this.requestsCounter = 0;
  this.userId = userId;
  this.userOrgs = [];
  this.ajaxRequests = [];
  this.ajaxAborted = false;
  this.arrData = {};
  this.patientsIdList = [];
  OrgTool.call(this);
};

/*
* extends OrgTool class
* see OT class definition in main.js should modularize it in the future so it can be instantiated/called safely
*
*/
AdminTool.prototype = Object.create(OrgTool.prototype);
AdminTool.prototype.fadeLoader = function() {
  DELAY_LOADING = false;
  setTimeout(function() { $("#loadingIndicator").fadeOut(); }, 1000);
};
AdminTool.prototype.setLoadingMessageVis = function(vis) {
  switch(vis) {
    case "hide":
      $("#adminTable .field-loading-indicator").fadeOut();
      break;
    case "show":
      $("#adminTable .field-loading-indicator").fadeIn();
      break;
  };
};
AdminTool.prototype.setStatusLoadingVis = function(vis) {
  switch(vis) {
    case "hide":
      $("#adminTable th.status-field .loading-message-indicator").fadeOut();
      $("#adminTable .bootstrap-table-filter-control-status").css("opacity", 1);
      break;
    case "show":
      $("#adminTable th.status-field .loading-message-indicator").fadeIn();
      break;
  };
};
AdminTool.prototype.getPatientsIdList = function() {
  var self = this;
  if (self.patientsIdList.length == 0) {
    var all = $("#adminTable").bootstrapTable("getData");
    if (all) {
      all.forEach(function(o) {
        self.patientsIdList.push(o.id);
      });
    };
  };
  return self.patientsIdList;
};
AdminTool.prototype.getData = function(requests, callback) {
    var self = this;
    if (self.ajaxAborted) {
        return false;
    };
    if (!requests || requests.length == 0) {
        return false;
    };
    var userString = requests.shift();
    if (!hasValue(userString)) {
        return false;
    };
    /*
     *  load the data sequentially
     *  Note, NO concurrent ajax calls here,
     *  one request will wait after the previous one has finished
     */
    var ajaxRequest = $.ajax ({
                              type: "GET",
                              url: '/api/consent-assessment-status',
                              contentType: "application/json; charset=utf-8",
                              data: userString,
                              cache: false,
                              timeout: 5000,
                              beforeSend: function() {
                                self.setStatusLoadingVis("show");
                                /*
                                 * disable export functionality while status is being populated
                                 */
                                $("div.export button").attr("disabled", true);
                              },
                              dataType: 'json'
                          }).done(function(data) {
                                if (data && data.status) {
                                  data.status.forEach(function(status) {
                                      var c = status.consents;
                                      var a = "", s = "", prevItem = {};
                                      if (c) {
                                      c.forEach(function(item) {
                                          if (!item.consent.deleted && (!prevItem.consent_signed || (prevItem.assessment_status != item.assessment_status)
                                              || (String(prevItem.consent_signed).substring(0, 10) != String(item.consent.signed).substring(0, 10)))) {
                                              if (!(/null/.test(item.consent.agreement_url))) {
                                                var cl = "";
                                                var sd = tnthDates.formatDateString(item.consent.signed);
                                                var status = item.assessment_status;
                                                if (!item.consent.send_reminders) status = "withdrawn";
                                                switch(String(status).toLowerCase()) {
                                                    case "completed":
                                                      cl = "text-success";
                                                      break;
                                                    case "withdrawn":
                                                      cl = "text-muted";
                                                      break;
                                                    case "due":
                                                      cl = "text-warning";
                                                      break;
                                                    case "overdue":
                                                      cl = "text-danger";
                                                      break;
                                                };
                                                a += (a != "" ? "
" : "") + "" + status + "";
                                                s += (s != "" ? "
" : "") + sd;
                                                prevItem.assessment_status = item.assessment_status;
                                                prevItem.consent_signed = item.consent.signed;
                                              };
                                            };
                                        });
                                    };
                                    if (hasValue(status.user_id)) {
                                      /*
                                       * NOTE, need to get the row data here
                                       * data for all the fields are required for the method, updateByUniqueId
                                       */
                                      var rowData = $("#adminTable").bootstrapTable('getRowByUniqueId', status.user_id);
                                      rowData = rowData || {};
                                      /* update row data with updated assessment status */
                                      rowData["status"] = a;
                                      /* persist data here, help with debugging */
                                      self.arrData[status.user_id] = { id: status.user_id, row: rowData};
                                      $("#adminTable").bootstrapTable('updateByUniqueId', self.arrData[status.user_id]);

                                    };
                              });
                            };
                            if (requests.length > 0) {
                              self.getData(requests, callback);
                            }
                            else {
                              if (callback) setTimeout(function() { callback.call(self);}, 300);
                              self.setStatusLoadingVis("hide");
                              $("div.export button").attr("disabled", false);
                            };
                        }).fail(function(xhr) {
                            self.setStatusLoadingVis("hide");
                            $("div.export button").attr("disabled", false);
                            $("#admin-table-error-message").text("Server error occurred updating row data.  Server error code: " + xhr.status);
                        });
        self.ajaxRequests.push(ajaxRequest);
        return ajaxRequest;
};
AdminTool.prototype.updateData = function() {
  /* compile user IDs array from patients list ID */
  var arrUsers = this.getUserIdArray();
  var self = this;
  if (arrUsers.length > 0) {
     self.getData(arrUsers);
  };
};
AdminTool.prototype.abortRequests = function(callback, showLoader) {
    var self = this;
   //NEED TO ABORT THE AJAX REQUESTS OTHERWISE CLICK EVENT IS DELAYED DUE TO NETWORK TIE-UP
   if (self.ajaxRequests.length > 0) {
      self.ajaxAborted = true;
      self.ajaxRequests.forEach(function(request, index, array) {

          try {
            if (parseInt(request.readyState) != 4) {
                /*
                 * aborting the request here to quit immediately, instead of waiting for the
                 * maximum timeout specified
                 */
                request.timeout = 100;
                request.abort();
            }
          } catch(e) {
          };
          if (index == array.length - 1) {
            $("#admin-table-error-message").text("");
            if (callback) {
              setTimeout(function() { callback();}, 100);
              if (showLoader) loader(true)
            };
          };
      });
    } else {
      if (callback) setTimeout(function() { callback();}, 100);
    };

};
AdminTool.prototype.getUserIdArray = function(_userIds) {
  var us = "", ct = 0, arrUsers = [];
  if (!_userIds) {
     _userIds = this.getPatientsIdList();
  };
  var max_ct = Math.max(_userIds.length/10, 10);

  for (var index = 0; index < _userIds.length; index++, ct++) {
     us += (us != ""?"&":"") + "user_id=" + _userIds[index];
     if (index == (_userIds.length - 1)) {
       arrUsers.push(us);
     } else if (ct >= max_ct) {
        arrUsers.push(us);
        us = "";
        ct = 0;
     };
  };
  return arrUsers;
};
AdminTool.prototype.setUserOrgs = function() {
  var self = this;
  if (!hasValue(this.userId)) return false;
  $.ajax ({
          type: "GET",
          async: false,
          url: '/api/demographics/'+this.userId
  }).done(function(data) {
    if (data && data.careProvider) {
      $.each(data.careProvider,function(i,val){
          var orgID = val.reference.split("/").pop();
          self.userOrgs.push(orgID);
          if (orgID == "0") $("#createUserLink").attr("disabled", true);
      });
    if (self.userOrgs.length == 0) $("#createUserLink").attr("disabled", true);
    };
  }).fail(function() {

  });
};
AdminTool.prototype.getUserOrgs = function() {
  if (this.userOrgs.length == 0) this.setUserOrgs(this.userId);
  return this.userOrgs;
};
AdminTool.prototype.initOrgsList = function(request_org_list, context) {
    //set user orgs
    var self = this;
    self.setUserOrgs();

    //check if the location contains filtered orgs list
    var iterated = /org_list/.test(location.href);

    var noPatientData = $("#admin-table-body").find("tr.no-records-found").length > 0;

    $.ajax ({
        type: "GET",
        url: '/api/organization'
    }).done(function(data) {

        /*
         * building an orgs array object for reference later
         */
        self.populateOrgsList(data.entry);
        /*
         * populate orgs dropdown UI
         */
        self.populateUI();

        /*
         * filter orgs UI based on user's orgs
         */
        if (!noPatientData) {
            var hbOrgs = self.getHereBelowOrgs(self.getUserOrgs());
	          self.filterOrgs(hbOrgs);
        } else {
          $("div.fixed-table-toolbar").hide();
        };

        var ofields = $("#userOrgs input[name='organization']");
        if (ofields.length > 0) {
          /* attach orgs related events to UI components */
          ofields.each(function() {
            if ((self.getHereBelowOrgs(self.getUserOrgs())).length == 1 ||
                (iterated && request_org_list && request_org_list[$(this).val()])) {
                $(this).prop("checked", true);
            }
            $(this).on("click touchstart", function(e) {
                e.stopPropagation();
                AT.abortRequests();
                var orgsList = [];
                $("#userOrgs input[name='organization']").each(function() {
                   if ($(this).is(":checked")) orgsList.push($(this).val());
                });
               if (orgsList.length > 0) {
                  location.replace("/" + context + "?org_list=" + orgsList.join(","));
               } else location.replace("/" + context);
            });
          });

          $("#org-menu").append("

<input type='checkbox' id='orglist-selectall-ckbox'> Select All   <input type='checkbox' id='orglist-clearall-ckbox'> Clear All   <input type='checkbox' id='orglist-close-ckbox'> Close
");
          $("#orglist-selectall-ckbox").on("click touchstart", function(e) {
              e.stopPropagation();
              var orgsList = [];
              $("#userOrgs input[name='organization']").each(function() {
                  if ($(this).is(":visible")) {
                    $(this).prop("checked", true);
                    orgsList.push($(this).val());
                  };
              });
              $("#orglist-clearall-ckbox").prop("checked", false);
              if (orgsList.length > 0) location.replace("/" + context + "?org_list=" + orgsList.join(","));
          });
          $("#orglist-clearall-ckbox").on("click touchstart", function(e) {
              e.stopPropagation();
              $("#userOrgs input[name='organization']").each(function() {
                  $(this).prop("checked", false);
              });
          });
          $("#orglist-close-ckbox").on("click touchstart", function(e) {
              e.stopPropagation();
              $("#orglistSelector").trigger("click");
              return false;
          });
        };

    }).fail(function() {
        //console.log("Problem retrieving data from server.");
        $("#org-menu").append("Error occurred retrieving data from server.");
    });

    //orglist-dropdown
    $('#orglist-dropdown').on('click touchstart', function () {
        setTimeout('AT.__setOrgsMenuHeight(100); AT.__clearFilterButtons();', 10);
    });

    if (noPatientData) $("#patientListExportDataContainer").hide();

    /*
     * private functions used only within the context of the class
     */
    this.__setOrgsMenuHeight = function(padding) {
      if (!padding) padding = 100;
      var h = parseInt($("#fillOrgs").height());
      if (!isNaN(h) && h > 0) {
        $("#org-menu").height(h + padding);
        if ($("div.admin-table").height() < $("#org-menu").height()) {
            setTimeout('$("div.admin-table").height($("#org-menu").height() + ' + padding + ');', 0);
        };
      };
    };
    this.__clearFilterButtons = function() {
      $("#orglist-close-ckbox, #orglist-clearall-ckbox, #orglist-selectall-ckbox").prop("checked", false);
    };
};
AdminTool.prototype.getInstrumentList = function() {
  return {
    //CRV
    '10000': ['epic26', 'eproms_add', 'comorb'],
    //IRONMAN
    '20000': ['eortc', 'ironmisc', 'factfpsi', 'epic26', 'prems', 'irondemog']
  };
};

AdminTool.prototype.handleDownloadModal = function() {

    var self = this;
     /*
      *populate instruments list based on user's parent org
      */
    $("#dataDownloadModal").on('shown.bs.modal', function () {
          var parentOrgList = AT.getUserTopLevelParentOrgs(AT.getUserOrgs());
          if (parentOrgList && parentOrgList.length > 0) {
             var instrumentList = self.getInstrumentList();
             var instrumentItems = [];
             parentOrgList.forEach(function(o) {
                var il = instrumentList[o];
                if (il) {
                  il.forEach(function(n) {
                    instrumentItems.push(n);
                  });
                };
             });
             if (instrumentItems.length > 0) {
                $(".instrument-container").hide();
                instrumentItems.forEach(function(item) {
                  $("#" + item + "_container").show();
                });
             };
          };
          $("#patientsInstrumentList").addClass("ready");
    });
    /*
     * attach on click event to submit button in the download modal
     */
    $(document).delegate("#patientsDownloadButton", "click", function(event){
      var instruments = "", dataType = "";
      $("input[name='instrument'").each(function() {
          if ($(this).is(":checked")) {
            instruments += (instruments != "" ? "&": "") + "instrument_id="+$(this).val();
          };
      });
      $("input[name='downloadType']").each(function() {
          if ($(this).is(":checked")) dataType = $(this).val();
      });
      if (instruments != "" && dataType != "") {
          //alert(instruments)
          $("#_downloadMessage").text("");
          $("#_downloadLink").attr("href", "/api/patient/assessment?" + instruments + "&format=" + dataType);
          $("#_downloadLink")[0].click();
      } else {
          message = (instruments == "" ? "Please choose at least one instrument.": "");
          if (dataType == "") message += (message != "" ? "
": "") + "Please choose one download type.";
          $("#_downloadMessage").html(message);
      };
    });

    /*
     * attach event to each checkbox in the download instruments modal
     */
    $("input[name='instrument'], input[name='downloadType']").on("click", function() {
        if ($(this).is(":checked")) $("#_downloadMessage").text("");
    });
}

<|endoftext|>


<commit_before>import del from 'rollup-plugin-delete';
import rollupCalf from './rollupCalf';
import serve from 'rollup-plugin-serve';

const fs = require('fs');
const port = require('./config.json').port;
const outPath = 'dist/watch/';

const options = rollupCalf(
  outPath,
  '[name].js',
  {
    _BETA: true,
    _CSSPATH: `https://localhost:${port}/${outPath}`,
    _DEV: true
  });
options.treeshake = false;
options.watch = {include: 'src/**'};

options.plugins.push(serve({
  contentBase: '',
  headers: {'Access-Control-Allow-Origin': '*'},
  https: {
    key: fs.readFileSync('key.pem'),
    cert: fs.readFileSync('cert.crt')
  },
  port
}));

options.plugins.push(del({
  targets: [
    `${outPath}*`,
    `!${outPath}fallenswordhelper.user.js`
  ]
}));

export default options;
<commit_msg>fix missing dataTables.css in watch<commit_after>import del from 'rollup-plugin-delete';
import rollupCalf from './rollupCalf';
import serve from 'rollup-plugin-serve';

const fs = require('fs');
const port = require('./config.json').port;
const outPath = 'dist/watch/';

const options = rollupCalf(
  outPath,
  '[name].js',
  {
    _BETA: true,
    _CSSPATH: `https://localhost:${port}/${outPath}`,
    _DEV: true
  });
options.treeshake = false;
options.watch = {include: 'src/**'};

options.plugins.push(serve({
  contentBase: '',
  headers: {'Access-Control-Allow-Origin': '*'},
  https: {
    key: fs.readFileSync('key.pem'),
    cert: fs.readFileSync('cert.crt')
  },
  port
}));

options.plugins.push(del({
  targets: [
    `${outPath}*`,
    `!${outPath}dataTables.css`,
    `!${outPath}fallenswordhelper.user.js`
  ]
}));

export default options;
<|endoftext|>


<commit_before>      default:

       if(typeof vars.default_params[vars.type] === "undefined") {
          console.log("No params for chart " + vars.type);
       }

        // TODO
        // Contain the parameters in something different than global variable
        // Use the utils.create_chart()

        if(vars.dev) {
          console.log("[init.vars.default]", vars);
        }

        var scope = {};

        // Sparkline is currenlty the only chart that can have a scope as parameter
        if(vars.type == "sparkline" || vars.type == "dotplot" || vars.type == "barchart" || vars.type == "linechart" || vars.type == "scatterplot" || vars.type == "grid" || vars.type == "stacked" || vars.type == "piechart" || vars.type == "slopegraph" || vars.type == "productspace" || vars.type == "treemap") {
          //scope = {};
          //scope = utils.merge(scope, vars)
          scope = vars.default_params[vars.type](vars);
          vars = vistk.utils.merge(vars, scope);
          vars.items = vistk.utils.merge(vars.items, vars.user_vars.items);

          console.log("VAR", vars)

        } else {

          // LOAD CHART PARAMS
          vars = vistk.utils.merge(vars, vars.default_params[vars.type]);

          // LOAD USER PARAMS
          vars.items = vistk.utils.merge(vars.items, vars.user_vars.items);

        }

        // CREATE AXIS
        if(vars.x_axis_show)
          vars.svg.call(utils.x_axis);

       if(vars.y_axis_show)
          vars.svg.call(utils.y_axis);

        if(vars.x_grid_show) {
          vars.svg.selectAll(".x.grid").data([vars.new_data])
            .enter()
              .append("g")
              .attr("class", "x grid")
              .style("display", function() { return vars.x_grid_show ? "block": "none"; })
              .attr("transform", "translate(0," + (vars.height-vars.margin.top-vars.margin.bottom) + ")");

          vars.svg.selectAll(".x.grid").transition()
              .duration(vars.duration)
              .call(utils.make_x_axis()
              .tickSize(-vars.height+vars.margin.top+vars.margin.bottom, 0, 0)
              .tickFormat(""));
        }

        if(vars.y_grid_show) {
          vars.svg.selectAll(".y.grid").data([vars.new_data])
            .enter()
              .append("g")
              .attr("class", "y grid")
              .style("display", function() { return vars.y_axis_show ? "block": "none"; })
              .attr("transform", "translate(" + vars.margin.left + ", 0)");

          vars.svg.selectAll(".y.grid").transition()
              .duration(vars.duration)
              .call(utils.make_y_axis()
              .tickSize(-vars.width+vars.margin.left+vars.margin.right, 0, 0)
              .tickFormat(""));
        }

        if(typeof vars.connect !== "undefined" && typeof vars.connect[0] !== "undefined") {

          // 1/ Between different items at a given time for one dimension
          // 2/ Between same items at a given time points
          // 2/ Between same items at multiple given times

          // By default, connecting time points
          var connect_data = vars.new_data;

          // Connecting items
          if(vars.connect[0].type == "items" && vars.type == "productspace") {
            connect_data = vars.links;

          // Connecting dimension
          } else if(vars.connect[0].type == "dimension") {

          }

          // PRE-UPDATE CONNECT
          // TOOD: find a common join to al types of connections
          var gConnect = vars.svg.selectAll(".connect__group")
                          .data(connect_data);

          // ENTER CONNECT
          var gConnect_enter = gConnect.enter()
                          .append("g")
                          .attr("class", "connect__group");

          // APPEND AND UPDATE CONNECT MARK
          vars.connect.forEach(function(connect) {

              connect.marks.forEach(function(params) {

                if(typeof params.filter == "undefined")
                  params.filter = function() { return true; };

                gConnect_enter.filter(params.filter).call(utils.draw_mark, params);
                gConnect.filter(params.filter).call(utils.draw_mark, params);
              });

          });

          // EXIT
          var gConnect_exit = gConnect.exit().remove();

        }

        if(typeof vars.items !== "undefined" && vars.items[0] !== "undefined" && vars.type !== "stacked") {

          // PRE-UPDATE ITEMS
          var gItems = vars.svg.selectAll(".mark__group")
                          .data(vars.new_data, function(d, i) { return vars.accessor_data(d)[vars.var_id]; });

          // ENTER ITEMS
          var gItems_enter = gItems.enter()
                          .insert("g", ":first-child")
                          .attr("transform", function(d, i) {
                            return "translate(" + vars.x_scale[0]["func"](vars.accessor_data(d)[vars.var_x]) + ", " + vars.y_scale[0]["func"](vars.accessor_data(d)[vars.var_y]) + ")";
                          });

           // APPEND AND UPDATE ITEMS MARK
            vars.items[0].marks.forEach(function(params) {

              if(typeof params.filter == "undefined")
                params.filter = function() { return true; };

              gItems_enter.filter(params.filter).call(utils.draw_mark, params);
              gItems.filter(params.filter).call(utils.draw_mark, params);
            });

            // Bind events to groups after marks have been created
            gItems.each(utils.items_group);

            /* Should be as below but current params don't match this format

              // APPEND AND UPDATE ITEMS MARK
              vars.items.forEach(function(item) {
                item.marks.forEach(function(params) {
                  gItems_enter.call(utils.draw_mark, params);
                  gItems.call(utils.draw_mark, params);
                });
              });

            */

          // POST-UPDATE ITEMS GROUPS
          vars.svg.selectAll(".mark__group")
                          .transition()
                          .duration(vars.duration)
                          .attr("transform", function(d, i) {
                            return "translate(" + vars.x_scale[0]["func"](vars.accessor_data(d)[vars.var_x]) + ", " + vars.y_scale[0]["func"](vars.accessor_data(d)[vars.var_y]) + ")";
                          });

          // ITEMS EXIT
          var gItems_exit = gItems.exit().remove();

          if(vars.zoom.length > 0) {

            utils.zoom_to_nodes(vars.zoom);

          } else {

            vars.svg.transition()
                    .attr("transform", "");

          }

        }

        utils.background_label(vars.title);

      break;
<commit_msg>Re-ordering marks drawing<commit_after>      default:

       if(typeof vars.default_params[vars.type] === "undefined") {
          console.log("No params for chart " + vars.type);
       }

        // TODO
        // Contain the parameters in something different than global variable
        // Use the utils.create_chart()

        if(vars.dev) {
          console.log("[init.vars.default]", vars);
        }

        var scope = {};

        // Sparkline is currenlty the only chart that can have a scope as parameter
        if(vars.type == "sparkline" || vars.type == "dotplot" || vars.type == "barchart" || vars.type == "linechart" || vars.type == "scatterplot" || vars.type == "grid" || vars.type == "stacked" || vars.type == "piechart" || vars.type == "slopegraph" || vars.type == "productspace" || vars.type == "treemap") {
          //scope = {};
          //scope = utils.merge(scope, vars)
          scope = vars.default_params[vars.type](vars);
          vars = vistk.utils.merge(vars, scope);
          vars.items = vistk.utils.merge(vars.items, vars.user_vars.items);

        } else {

          // LOAD CHART PARAMS
          vars = vistk.utils.merge(vars, vars.default_params[vars.type]);

          // LOAD USER PARAMS
          vars.items = vistk.utils.merge(vars.items, vars.user_vars.items);

        }

        // CREATE AXIS
        if(vars.x_axis_show)
          vars.svg.call(utils.x_axis);

       if(vars.y_axis_show)
          vars.svg.call(utils.y_axis);

        if(vars.x_grid_show) {
          vars.svg.selectAll(".x.grid").data([vars.new_data])
            .enter()
              .append("g")
              .attr("class", "x grid")
              .style("display", function() { return vars.x_grid_show ? "block": "none"; })
              .attr("transform", "translate(0," + (vars.height-vars.margin.top-vars.margin.bottom) + ")");

          vars.svg.selectAll(".x.grid").transition()
              .duration(vars.duration)
              .call(utils.make_x_axis()
              .tickSize(-vars.height+vars.margin.top+vars.margin.bottom, 0, 0)
              .tickFormat(""));
        }

        if(vars.y_grid_show) {
          vars.svg.selectAll(".y.grid").data([vars.new_data])
            .enter()
              .append("g")
              .attr("class", "y grid")
              .style("display", function() { return vars.y_axis_show ? "block": "none"; })
              .attr("transform", "translate(" + vars.margin.left + ", 0)");

          vars.svg.selectAll(".y.grid").transition()
              .duration(vars.duration)
              .call(utils.make_y_axis()
              .tickSize(-vars.width+vars.margin.left+vars.margin.right, 0, 0)
              .tickFormat(""));
        }

        if(typeof vars.items !== "undefined" && vars.items[0] !== "undefined" && vars.type !== "stacked") {

          // PRE-UPDATE ITEMS
          var gItems = vars.svg.selectAll(".mark__group")
                          .data(vars.new_data, function(d, i) { return vars.accessor_data(d)[vars.var_id]; });

          // ENTER ITEMS
          var gItems_enter = gItems.enter()
                          .insert("g", ":first-child")
                          .attr("transform", function(d, i) {
                            return "translate(" + vars.x_scale[0]["func"](vars.accessor_data(d)[vars.var_x]) + ", " + vars.y_scale[0]["func"](vars.accessor_data(d)[vars.var_y]) + ")";
                          });

           // APPEND AND UPDATE ITEMS MARK
            vars.items[0].marks.forEach(function(params) {

              if(typeof params.filter == "undefined")
                params.filter = function() { return true; };

              gItems_enter.filter(params.filter).call(utils.draw_mark, params);
              gItems.filter(params.filter).call(utils.draw_mark, params);
            });

            // Bind events to groups after marks have been created
            gItems.each(utils.items_group);

            /* Should be as below but current params don't match this format

              // APPEND AND UPDATE ITEMS MARK
              vars.items.forEach(function(item) {
                item.marks.forEach(function(params) {
                  gItems_enter.call(utils.draw_mark, params);
                  gItems.call(utils.draw_mark, params);
                });
              });

            */

          // POST-UPDATE ITEMS GROUPS
          vars.svg.selectAll(".mark__group")
                          .transition()
                          .duration(vars.duration)
                          .attr("transform", function(d, i) {
                            return "translate(" + vars.x_scale[0]["func"](vars.accessor_data(d)[vars.var_x]) + ", " + vars.y_scale[0]["func"](vars.accessor_data(d)[vars.var_y]) + ")";
                          });

          // ITEMS EXIT
          var gItems_exit = gItems.exit().remove();

          if(vars.zoom.length > 0) {

            utils.zoom_to_nodes(vars.zoom);

          } else {

            vars.svg.transition()
                    .attr("transform", "");

          }

        }

        if(typeof vars.connect !== "undefined" && typeof vars.connect[0] !== "undefined") {

          // 1/ Between different items at a given time for one dimension
          // 2/ Between same items at a given time points
          // 2/ Between same items at multiple given times

          // By default, connecting time points
          var connect_data = vars.new_data;

          // Connecting items
          if(vars.connect[0].type == "items" && vars.type == "productspace") {
            connect_data = vars.links;

          // Connecting dimension
          } else if(vars.connect[0].type == "dimension") {

          }

          // PRE-UPDATE CONNECT
          // TOOD: find a common join to al types of connections
          var gConnect = vars.svg.selectAll(".connect__group")
                          .data(connect_data);

          // ENTER CONNECT
          var gConnect_enter = gConnect.enter()
                          .insert("g", ":first-child")
                          .attr("class", "connect__group");

          // APPEND AND UPDATE CONNECT MARK
          vars.connect.forEach(function(connect) {

              connect.marks.forEach(function(params) {

                if(typeof params.filter == "undefined")
                  params.filter = function() { return true; };

                gConnect_enter.filter(params.filter).call(utils.draw_mark, params);
                gConnect.filter(params.filter).call(utils.draw_mark, params);
              });

          });

          // EXIT
          var gConnect_exit = gConnect.exit().remove();

        }

        utils.background_label(vars.title);

      break;
<|endoftext|>