<commit_before>require.config({
  waitSeconds: 15000,
  paths: {
      "async": "vendor/requirejs-plugins/src/async",
      "backbone": "vendor/backbone/backbone",
      // Note this may import the .coffee instead of the .js file
      "backbone_couchdb": "backbone-couchdb/backbone-couchdb",
      "backbone_relational": "vendor/backbone-relational/backbone-relational",
      // Note this may import the .css instead of the .js file
      "backgrid":"vendor/backgrid/lib/backgrid",
      "bootstrap": "vendor/bootstrap/dist/js/bootstrap.min",
      "config": "config",
      "jquery": "vendor/jquery/jquery.min",
      "jquery_migrate": "vendor/jquery/jquery-migrate",
      // Commented out because it uses $.browser, which is deprecated
      // But this may break msie compatibility!
      //"jquery_couch": "/_utils/script/jquery.couch",
      "jquery_couch": "jquery.couch",
      "jquery_couchLogin": "vendor/jquery.couchLogin/jquery.couchLogin",
      "jquery_xpath": "vendor/jquery.xpath/jquery.xpath",
      "model": "model",
      "mustache": "vendor/mustache/mustache",
      "text": "vendor/requirejs-text/text",
      "underscore": "vendor/underscore/underscore"
  },
  shim: {
      backbone: {
          deps: ["jquery", "underscore"],
          exports: "Backbone"
      },
      backbone_relational: {
          deps: ["backbone"]
      },
      backbone_couchdb: {
          deps: ["backbone_relational", "jquery_couch"],
          init: function(){
              // TODO: Handle injecting Backbone.RelationalModel into backbone_couchdb here
              // return this.
          },
          exports: 'Backbone'
      },
      // TODO: Use require-css to inject the CSS as needed
      backgrid: {
          deps: ["jquery", "backbone_couchdb", "underscore"],
          exports: 'Backgrid'
      },
      // TODO: Use require-css to inject the CSS as needed
      "bootstrap": {
          deps: ["jquery"],
          exports: "$.fn.typeahead"
      },
      jquery_couch: {
          deps: ["jquery", "jquery_migrate"]
      },
      jquery_couchLogin: {
          deps: ["jquery", "jquery_couch"]
      },
      jquery_migrate: {
          deps: ["jquery"]
      },
      jquery_xpath: {
          deps: ["jquery"]
      },
      model: {
          deps: ["jquery", "config", "backbone", "backbone_relational", "backbone_couchdb"]
      },
      mustache: {
          exports: ["Mustache"]
      },
      underscore: {
          exports: '_'
      }
  }
});
// Define require() from require.js using the standard AMD define

require(
   [
    'config',
    'model',
    'views/view_loader',
    'jquery_couchLogin',
    'underscore',
    'backbone',
    'jquery_couch'
    ],
    function(
             config,
             model,
             views
        ){

       // Create main application
        var App = Backbone.Router.extend({
            initialize : function(){
                // Make it easy to reference this object in event handlers
                _.bindAll(this, 'find_a_church', 'import_directory')
            },
            // Set up URLs here
            // TODO: Set CouchDB routing for URLs it doesn't understand.  Is there a way to do this
            //    without duplicating what is written here?
            //    http://blog.couchbase.com/what%E2%80%99s-new-apache-couchdb-011-%E2%80%94-part-one-nice-urls-rewrite-rules-and-virtual-hosts
            //    Maybe in this.initialize we can dynamically get ddoc.rewrites, iterate through it,
            //    and dynamically create this.routes in the correct data format,
            //    which is {'url':'function_name',...}.
            //    (misunderstood_url)
            routes: {
                "index.html":                    "index.html",
                "find_a_church":                 "find_a_church",
                "import_directory":              "import_directory"
            },
            render:function(){
                this.menu_view = new views.MenuView({ el: $(".navbar") });
                this.menu_view.render()
                this.find_a_church_view = new views.FindAChurchView({ el: $("#content") });
                this.import_directory_view = new views.ImportDirectoryView({ el: $("#content") });
                // TODO: Login doesn't work yet
                $("#account").couchLogin({});
                // This renders the default view for the app
                // TODO:  If the page loaded from a different view's URL, load that view instead
                //    Maybe we can handle that in the router below.
                //  load-correct-view-from-url-on-first-load
                this.default_view = this[config.default_view]
                this.default_view.render()
                // Run tests only if configured to do so
                var thiz = this
                if (config.run_jasmine_tests === true) {
                    $('head').append('<link rel="stylesheet" href="js/vendor/jasmine/lib/jasmine-core/jasmine.css" type="text/css" />')
                    // TODO: Is the setTimeout call necessary?  Its purpose is to fix the problem where the tests
                    //  don't run on page load, maybe because they are loaded too early.  But loading them late
                    //  doesn't fix the problem either.  They just sometimes run, and sometimes don't.
                    setTimeout(function(){
                        thiz.run_tests_view = new views.RunTestsView({ el: $("#tests") });
                    }, 3000)
                }
            },
            find_a_church:function(){
                // TODO: This destroys the old view, and renders a new view, in the #content div.
                //    But if the view has already been rendered, and has some state, it might
                //    be better to HIDE other views, and DISPLAY this one, rather than render it,
                //    because this would preserve the rendered view's state.
                //    Con:  Preserving the rendered view's state is not what most users expect.
                //    Pro:  It might be what most users want
                //    So, do we want to preserve this view's state?
                //  https://blueprints.launchpad.net/reformedchurcheslocator/+spec/decide-whether-to-hide-or-destroy-views
                this.find_a_church_view.render()
            },
            import_directory:function(){
                this.import_directory_view.render()
            }
        });
        // Instantiate & initialize App
        window.app = new App
        // Render after initializing, so window.app can be accessed by the views this app renders
        window.app.render()
        // Make model, config available in tests
        window.app.model = model
        window.app.config = config

        // Create SEF URLs and handle clicks
        Backbone.history.start({pushState: true, root: "/rcl/_design/rcl/"})
        // Globally capture clicks. If they are internal and not in the pass
        // through list, route them through Backbone's navigate method.
        // TODO: Create vhosts entry to allow pages to load from direct access to the URL, like
        //  to http://localhost:5984/rcl/_design/rcl/import_directory.  Currently that URL returns:
        //  {"error":"not_found","reason":"Document is missing attachment"}
        // TODO: When I create a vhosts entry for /rcl2 and rewrites.json, then go to
        //  http://localhost:5984/rcl2/find_a_church, it returns:
        //  {"error":"not_found","reason":"Document is missing attachment"}
        //  https://blueprints.launchpad.net/reformedchurcheslocator/+spec/example-of-vhosts-not-working-yet
        $(document).on("click", "a[href^='/']", function(event){
            var href = $(event.currentTarget).attr('href')
            // chain 'or's for other black list routes
            var passThrough = href.indexOf('sign_out') >= 0 || href.indexOf('delete') >= 0
            // Allow shift+click for new tabs, etc.
            if (!passThrough && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey){
                event.preventDefault()
                // Instruct Backbone to trigger routing events
                app.navigate(href, { trigger: true })
                return false
            }
        })

        return {
            app:app
        }
});
<commit_msg>Made db name fully configurable so tests, routing don't change back to production db<commit_after>require.config({
  waitSeconds: 15000,
  paths: {
      "async": "vendor/requirejs-plugins/src/async",
      "backbone": "vendor/backbone/backbone",
      // Note this may import the .coffee instead of the .js file
      "backbone_couchdb": "backbone-couchdb/backbone-couchdb",
      "backbone_relational": "vendor/backbone-relational/backbone-relational",
      // Note this may import the .css instead of the .js file
      "backgrid":"vendor/backgrid/lib/backgrid",
      "bootstrap": "vendor/bootstrap/dist/js/bootstrap.min",
      "config": "config",
      "jquery": "vendor/jquery/jquery.min",
      "jquery_migrate": "vendor/jquery/jquery-migrate",
      // Commented out because it uses $.browser, which is deprecated
      // But this may break msie compatibility!
      //"jquery_couch": "/_utils/script/jquery.couch",
      "jquery_couch": "jquery.couch",
      "jquery_couchLogin": "vendor/jquery.couchLogin/jquery.couchLogin",
      "jquery_xpath": "vendor/jquery.xpath/jquery.xpath",
      "model": "model",
      "mustache": "vendor/mustache/mustache",
      "text": "vendor/requirejs-text/text",
      "underscore": "vendor/underscore/underscore"
  },
  shim: {
      backbone: {
          deps: ["jquery", "underscore"],
          exports: "Backbone"
      },
      backbone_relational: {
          deps: ["backbone"]
      },
      backbone_couchdb: {
          deps: ["backbone_relational", "jquery_couch"],
          init: function(){
              // TODO: Handle injecting Backbone.RelationalModel into backbone_couchdb here
              // return this.
          },
          exports: 'Backbone'
      },
      // TODO: Use require-css to inject the CSS as needed
      backgrid: {
          deps: ["jquery", "backbone_couchdb", "underscore"],
          exports: 'Backgrid'
      },
      // TODO: Use require-css to inject the CSS as needed
      "bootstrap": {
          deps: ["jquery"],
          exports: "$.fn.typeahead"
      },
      jquery_couch: {
          deps: ["jquery", "jquery_migrate"]
      },
      jquery_couchLogin: {
          deps: ["jquery", "jquery_couch"]
      },
      jquery_migrate: {
          deps: ["jquery"]
      },
      jquery_xpath: {
          deps: ["jquery"]
      },
      model: {
          deps: ["jquery", "config", "backbone", "backbone_relational", "backbone_couchdb"]
      },
      mustache: {
          exports: ["Mustache"]
      },
      underscore: {
          exports: '_'
      }
  }
});
// Define require() from require.js using the standard AMD define

require(
   [
    'config',
    'model',
    'views/view_loader',
    'jquery_couchLogin',
    'underscore',
    'backbone',
    'jquery_couch'
    ],
    function(
             config,
             model,
             views
        ){

       // Create main application
        var App = Backbone.Router.extend({
            initialize : function(){
                // Make it easy to reference this object in event handlers
                _.bindAll(this, 'find_a_church', 'import_directory')
            },
            // Set up URLs here
            // TODO: Set CouchDB routing for URLs it doesn't understand.  Is there a way to do this
            //    without duplicating what is written here?
            //    http://blog.couchbase.com/what%E2%80%99s-new-apache-couchdb-011-%E2%80%94-part-one-nice-urls-rewrite-rules-and-virtual-hosts
            //    Maybe in this.initialize we can dynamically get ddoc.rewrites, iterate through it,
            //    and dynamically create this.routes in the correct data format,
            //    which is {'url':'function_name',...}.
            //    (misunderstood_url)
            routes: {
                "index.html":                    "index.html",
                "find_a_church":                 "find_a_church",
                "import_directory":              "import_directory"
            },
            render:function(){
                this.menu_view = new views.MenuView({ el: $(".navbar") });
                this.menu_view.render()
                this.find_a_church_view = new views.FindAChurchView({ el: $("#content") });
                this.import_directory_view = new views.ImportDirectoryView({ el: $("#content") });
                // TODO: Login doesn't work yet
                $("#account").couchLogin({});
                // This renders the default view for the app
                // TODO:  If the page loaded from a different view's URL, load that view instead
                //    Maybe we can handle that in the router below.
                //  load-correct-view-from-url-on-first-load
                this.default_view = this[config.default_view]
                this.default_view.render()
                // Run tests only if configured to do so
                var thiz = this
                if (config.run_jasmine_tests === true) {
                    $('head').append('<link rel="stylesheet" href="js/vendor/jasmine/lib/jasmine-core/jasmine.css" type="text/css" />')
                    // TODO: Is the setTimeout call necessary?  Its purpose is to fix the problem where the tests
                    //  don't run on page load, maybe because they are loaded too early.  But loading them late
                    //  doesn't fix the problem either.  They just sometimes run, and sometimes don't.
                    setTimeout(function(){
                        thiz.run_tests_view = new views.RunTestsView({ el: $("#tests") });
                    }, 3000)
                }
            },
            find_a_church:function(){
                // TODO: This destroys the old view, and renders a new view, in the #content div.
                //    But if the view has already been rendered, and has some state, it might
                //    be better to HIDE other views, and DISPLAY this one, rather than render it,
                //    because this would preserve the rendered view's state.
                //    Con:  Preserving the rendered view's state is not what most users expect.
                //    Pro:  It might be what most users want
                //    So, do we want to preserve this view's state?
                //  https://blueprints.launchpad.net/reformedchurcheslocator/+spec/decide-whether-to-hide-or-destroy-views
                this.find_a_church_view.render()
            },
            import_directory:function(){
                this.import_directory_view.render()
            }
        });
        // Instantiate & initialize App
        window.app = new App
        // Render after initializing, so window.app can be accessed by the views this app renders
        window.app.render()
        // Make model, config available in tests
        window.app.model = model
        window.app.config = config

        // Create SEF URLs and handle clicks
        Backbone.history.start({pushState: true, root: "/" + config.db_name + "/_design/rcl/"})
        // Globally capture clicks. If they are internal and not in the pass
        // through list, route them through Backbone's navigate method.
        // TODO: Create vhosts entry to allow pages to load from direct access to the URL, like
        //  to http://localhost:5984/rcl/_design/rcl/import_directory.  Currently that URL returns:
        //  {"error":"not_found","reason":"Document is missing attachment"}
        // TODO: When I create a vhosts entry for /rcl2 and rewrites.json, then go to
        //  http://localhost:5984/rcl2/find_a_church, it returns:
        //  {"error":"not_found","reason":"Document is missing attachment"}
        //  https://blueprints.launchpad.net/reformedchurcheslocator/+spec/example-of-vhosts-not-working-yet
        $(document).on("click", "a[href^='/']", function(event){
            var href = $(event.currentTarget).attr('href')
            // chain 'or's for other black list routes
            var passThrough = href.indexOf('sign_out') >= 0 || href.indexOf('delete') >= 0
            // Allow shift+click for new tabs, etc.
            if (!passThrough && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey){
                event.preventDefault()
                // Instruct Backbone to trigger routing events
                app.navigate(href, { trigger: true })
                return false
            }
        })

        return {
            app:app
        }
});
<|endoftext|>


<commit_before>// REPL
var clc = require('cli-color')
var through = require('through')
var read = require('read')

function REPL (msg) {
  var colors = [
    {f:0,b:11},
    {f:0,b:14},
    {f:0,b:15}
  ]

  var clr = colors[Math.floor(Math.random() * ((colors.length-1) - 0 + 1) + 0)]

  if (msg) console.log(clc.xterm(clr.b)(msg))
  read({}, function handleInput (e,c,d) {
    if (e) console.error(e)
    if (!e) {
      var args = c.match(' ')
      if (args !== null) {
        c = c.split(' ')
        s.write(c)
        REPL()
      } else {
        s.write(c)
        REPL()
      }
    }
  })

  var s = through(function write (d) {
    this.emit('data',d)
  }, function end () {
    this.emit('end')
  })

  return s
}

module.exports = REPL
<commit_msg>rm _repl.js<commit_after><|endoftext|>


<commit_before>Travis.Controllers.Sidebar = Ember.Object.extend({
  cookie: 'sidebar_minimized',
  queues: [
    { name: 'common',  display: 'Common' },
    { name: 'php',     display: 'PHP' },
    { name: 'node_js', display: 'Node.js' },
    { name: 'jvmotp',  display: 'JVM & Erlang' },
    { name: 'rails',   display: 'Rails' },
    { name: 'spree',   display: 'Spree' },
  ],

  init: function() {
    this._super();
    Travis.Controllers.Workers.create();
    $.each(this.queues, function(ix, queue) {
      Travis.Controllers.Queue.create({ queue: 'builds.' + queue.name, display: queue.display });
    });

    $(".slider").click(function() { this.toggle(); }.bind(this));
    if($.cookie(this.cookie) === 'true') { this.minimize(); }
    this.persist();
  },

  toggle: function() {
    this.isMinimized() ? this.maximize() : this.minimize();
    this.persist();
  },

  isMinimized: function() {
    return $('#right').hasClass('minimized');
  },

  minimize: function() {
    $('#right').addClass('minimized');
    $('#main').addClass('maximized');
  },

  maximize: function() {
    $('#right').removeClass('minimized');
    $('#main').removeClass('maximized');
  },

  persist: function() {
    $.cookie(this.cookie, this.isMinimized());
  }
});
<commit_msg>correct the queue names<commit_after>Travis.Controllers.Sidebar = Ember.Object.extend({
  cookie: 'sidebar_minimized',
  queues: [
    { name: 'common',  display: 'Common' },
    { name: 'php',     display: 'PHP, Perl and Python' },
    { name: 'node_js', display: 'Node.js' },
    { name: 'jvmotp',  display: 'JVM and Erlang' },
    { name: 'rails',   display: 'Rails' },
    { name: 'spree',   display: 'Spree' },
  ],

  init: function() {
    this._super();
    Travis.Controllers.Workers.create();
    $.each(this.queues, function(ix, queue) {
      Travis.Controllers.Queue.create({ queue: 'builds.' + queue.name, display: queue.display });
    });

    $(".slider").click(function() { this.toggle(); }.bind(this));
    if($.cookie(this.cookie) === 'true') { this.minimize(); }
    this.persist();
  },

  toggle: function() {
    this.isMinimized() ? this.maximize() : this.minimize();
    this.persist();
  },

  isMinimized: function() {
    return $('#right').hasClass('minimized');
  },

  minimize: function() {
    $('#right').addClass('minimized');
    $('#main').addClass('maximized');
  },

  maximize: function() {
    $('#right').removeClass('minimized');
    $('#main').removeClass('maximized');
  },

  persist: function() {
    $.cookie(this.cookie, this.isMinimized());
  }
});
<|endoftext|>


<commit_before>define(['app/modules/dbDirectives'], function (db) {
	'use strict';

	db.directive('repeater', [function () {
		var link = function ($scope, $element, $attrs) {
			$scope.repeats = $scope.repeats || [
				{}
			];

			$scope.getIndex = function (repeat) {
				return $scope.repeats.indexOf(repeat);
			};

			$scope.getRepeat = function (repeat) {
				return $scope.repeats[$scope.getIndex(repeat)];
			};

			$scope.createRepeat = function () {
				$scope.repeats.push({});

				return $scope.repeats.slice(-1)[0];
			};

			$scope.updateRepeat = function (old, repeat) {
				var index = $scope.getIndex(old);

				if (!~index)
					return false;

				$scope.repeats[index] = $.extend(true, {}, $scope.repeats[index], repeat);

				return $scope.repeats[index];
			};

			$scope.deleteRepeat = function (repeat) {
				var index = $scope.getIndex(repeat);

				if (!~index)
					return false;

				var deleted = $scope.repeats.splice(index, 1);

				if ($scope.repeats.length == 0)
					$scope.repeats.push({});

				return deleted;
			};

			$scope.isMoveable = function () {
				return false;
			}
		};

		return {
			restrict: 'A',
			scope: true,
			link: link
		}
	}]);
});
<commit_msg>repeater works with no initial items.<commit_after>define(['app/modules/dbDirectives'], function (db) {
	'use strict';

	db.directive('repeater', [function () {
		var link = function ($scope, $element, $attrs) {
			$scope.repeats = $scope.repeats || [
				{}
			];

			$scope.getIndex = function (repeat) {
				return $scope.repeats.indexOf(repeat);
			};

			$scope.getRepeat = function (repeat) {
				return $scope.repeats[$scope.getIndex(repeat)];
			};

			$scope.createRepeat = function () {
				$scope.repeats.push({});

				return $scope.repeats.slice(-1)[0];
			};

			$scope.updateRepeat = function (old, repeat) {
				var index = $scope.getIndex(old);

				if (!~index)
					return false;

				$scope.repeats[index] = $.extend(true, {}, $scope.repeats[index], repeat);

				return $scope.repeats[index];
			};

			$scope.deleteRepeat = function (repeat) {
				var index = $scope.getIndex(repeat);

				if (!~index)
					return false;

				var deleted = $scope.repeats.splice(index, 1);

				if ($scope.repeats.length == 0)
					$scope.repeats.push({});

				return deleted;
			};

			$scope.isMoveable = function () {
				return false;
			};

			//

			var initialize = function () {
				if($scope.repeats.length == 0) {
					$scope.createRepeat();
				}
			};

			initialize();
		};

		return {
			restrict: 'A',
			scope: true,
			link: link
		}
	}]);
});
<|endoftext|>


<commit_before>//browser-update.org notification script, <browser-update.org>
//Copyright (c) 2007-2009, MIT Style License <browser-update.org/LICENSE.txt>

(function() {

var $buo = function() {

  // sam: my main concern here is mobile, but its an outlier, for now we support ie9, set conditionally and stuff with pushState
  if (window.ie === "new" || (window.history && window.history.pushState)) {
      return;
  }

  // retrieve localized browser upgrade text
  var t = I18n.t('js.browser_update');

  // create the notification div HTML
  var div = document.createElement("div");
  div.className = "buorg";
  div.innerHTML = "
" + t + "
";

  // create the notification div stylesheet
  var sheet = document.createElement("style");
  var style = ".buorg {position:absolute; z-index:111111; width:100%; top:0px; left:0px; background:#FDF2AB; text-align:left; font-family: sans-serif; color:#000; font-size: 14px;} .buorg div {padding: 8px;} .buorg a, .buorg a:visited {color:#E25600; text-decoration: underline;}";

  // insert the div and stylesheet into the DOM
  document.body.insertBefore(div, document.body.firstChild);
  document.getElementsByTagName("head")[0].appendChild(sheet);
  try {
    sheet.innerText = style;
    sheet.innerHTML = style;
  }
  catch(e) {
    try {
      sheet.styleSheet.cssText = style;
    }
    catch(ex) {
      return;
    }
  }

  // shift the body down to make room for our notification div
  document.body.style.marginTop = (div.clientHeight) + "px";

};

$bu=$buo();

})(this);<commit_msg>Fix IE9 bug in browser-update. Unable to get value of the property 'insertBefore': object is null or undefined<commit_after>//browser-update.org notification script, <browser-update.org>
//Copyright (c) 2007-2009, MIT Style License <browser-update.org/LICENSE.txt>

(function() {

var $buo = function() {

  // sam: my main concern here is mobile, but its an outlier, for now we support ie9, set conditionally and stuff with pushState
  if (window.ie === "new" || (window.history && window.history.pushState)) {
      return;
  }

  // retrieve localized browser upgrade text
  var t = I18n.t('js.browser_update');

  // create the notification div HTML
  var div = document.createElement("div");
  div.className = "buorg";
  div.innerHTML = "
" + t + "
";

  // create the notification div stylesheet
  var sheet = document.createElement("style");
  var style = ".buorg {position:absolute; z-index:111111; width:100%; top:0px; left:0px; background:#FDF2AB; text-align:left; font-family: sans-serif; color:#000; font-size: 14px;} .buorg div {padding: 8px;} .buorg a, .buorg a:visited {color:#E25600; text-decoration: underline;}";

  // insert the div and stylesheet into the DOM
  document.body.insertBefore(div, document.body.firstChild);
  document.getElementsByTagName("head")[0].appendChild(sheet);
  try {
    sheet.innerText = style;
    sheet.innerHTML = style;
  }
  catch(e) {
    try {
      sheet.styleSheet.cssText = style;
    }
    catch(ex) {
      return;
    }
  }

  // shift the body down to make room for our notification div
  document.body.style.marginTop = (div.clientHeight) + "px";

};

$(function() {
  $bu=$buo();
});

})(this);<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- */
/* DATE PICKER
/* A wrapper around a third party date picker.
/* -------------------------------------------------------------------------- */
(function($) {
  var DatePicker = function(input) {
    this.$input = input;
    this.$label = input.siblings('label').first();
    this.$wrapper = $('');
    this.$display = $('');
    this.$button = $('');
    this.$wrapper.append(this.$display, this.$button);

    this.$wrapper.click($.proxy(this, 'toggle'));
    this.open = false;

    // Check to see if there is a date/time string. Otherwise, default to today.
    var val = this.$input.val().trim();
    if (_.isEmpty(val)) {
      this.update(new Date());
    }
    else {
      this.update(val);
    }

    $(document).click($.proxy(this, 'clickOutside'));

    this.$input.after(this.$wrapper);
    this.$input.hide();
  };

  DatePicker.prototype = {
    toggle: function(e) {
      if (this.$input.attr('disabled') || this.$input.attr('readonly')) {return false}
      if (this.open && this.$picker.has(e.target).length === 0) {
        this.close();
      } else {
        if (!this.picker) {
          this.picker = new Kalendae(this.$wrapper[0], {selected: this.current, useYearNav: this.$input.is('[data-years=true]' || false)});
          this.picker.subscribe('change', $.proxy(this, 'updateFromPicker'));
          this.$picker = $(this.picker.container);
        }

        $(document).on('keypress.datePicker', $.proxy(this, 'keyPress'));
        $(window).on('keydown.datePicker', $.proxy(this, 'catchKey'));

        this.$picker.show();
        this.open = true;
      }
    },

    clickOutside: function(e) {
      if (this.open && !this.$wrapper.is(e.target) && this.$wrapper.has(e.target).length === 0) {
        this.close();
      }
    },

    keyPress: function(e) {

      var val = [];

      //The first key press should clear the input
      if (!this.keyEntry()) {
        this.clear();
      } else {
        val = this.$display.text().split('');
      }

      switch (e.key) {
        case '/':
        case '-':
          val.push('/');
          e.preventDefault();
        break;
        default:
          if (e.which >=48 && e.which <=57) {
            //This is 0-9
            val.push(e.key);
          }
        break;
      }

      //Update the user's display with progress
      this.$display.text(val.join(''));

      //Check if we have a complete date
      var date = moment(val.join(''), 'DD/MM/YYYY', true);

      if (date.format() == 'Invalid date') {
        this.$input.val('');
      } else {
        //Update the input, since this looks legit
        this.update(date.format('YYYY-MM-DD'));
        this.$input.data('has-key-entry', false);
      }
    },

    catchKey: function(e) {
      if (e.key === 'Backspace') {
        e.preventDefault();
        this.clear();
        return false;
      }
    },

    keyEntry: function() {
      var result = this.$input.data('has-key-entry') ? true : false;
      this.$input.data('has-key-entry', true);
      return result;
    },

    clear: function(){
      this.$input.val('');
      this.$display.text('');
    },

    close: function(){
      this.$picker.hide();
      this.open = false;

      $(document).off('.datePicker');
      $(window).off('.datePicker');
    },

    update: function(date) {
      var val = moment(date);
      this.current = val;
      this.$display.text(val.format('DD/MM/YYYY'));
      this.$input.val(val.format('YYYY-MM-DD'))
    },

    updateFromPicker: function() {
      var date = this.picker.getSelectedRaw()[0];
      this.update(date);
      this.$picker.hide();
      this.open = false;
    }
  };

  $.fn.islayDatePicker = function() {
    this.each(function() {
      var $this = $(this);
      if (!$this.data('islayDatePicker')) {
        $this.data('islayDatePicker', new DatePicker($this));
      }
    });
    return this;
  };
})(jQuery);
<commit_msg>Update the date picker when typing<commit_after>/* -------------------------------------------------------------------------- */
/* DATE PICKER
/* A wrapper around a third party date picker.
/* -------------------------------------------------------------------------- */
(function($) {
  var DatePicker = function(input) {
    this.$input = input;
    this.$label = input.siblings('label').first();
    this.$wrapper = $('');
    this.$display = $('');
    this.$button = $('');
    this.$wrapper.append(this.$display, this.$button);

    this.$wrapper.click($.proxy(this, 'toggle'));
    this.open = false;

    // Check to see if there is a date/time string. Otherwise, default to today.
    var val = this.$input.val().trim();
    if (_.isEmpty(val)) {
      this.update(new Date());
    }
    else {
      this.update(val);
    }

    $(document).click($.proxy(this, 'clickOutside'));

    this.$input.after(this.$wrapper);
    this.$input.hide();
  };

  DatePicker.prototype = {
    toggle: function(e) {
      if (this.$input.attr('disabled') || this.$input.attr('readonly')) {return false}
      if (this.open && this.$picker.has(e.target).length === 0) {
        this.close();
      } else {
        if (!this.picker) {
          this.picker = new Kalendae(this.$wrapper[0], {
            selected: this.current,
            useYearNav: this.$input.is('[data-years=true]' || false),
          }
        );
          this.picker.subscribe('change', $.proxy(this, 'updateFromPicker'));
          this.$picker = $(this.picker.container);
        }

        $(document).on('keypress.datePicker', $.proxy(this, 'keyPress'));
        $(window).on('keydown.datePicker', $.proxy(this, 'catchKey'));

        this.$picker.show();
        this.open = true;
      }
    },

    clickOutside: function(e) {
      if (this.open && !this.$wrapper.is(e.target) && this.$wrapper.has(e.target).length === 0) {
        this.close();
      }
    },

    keyPress: function(e) {

      var val = [];

      //The first key press should clear the input
      if (!this.keyEntry()) {
        this.clear();
      } else {
        val = this.$display.text().split('');
      }

      switch (e.key) {
        case '/':
        case '-':
          val.push('/');
          e.preventDefault();
        break;
        default:
          if (e.which >=48 && e.which <=57) {
            //This is 0-9
            val.push(e.key);
          }
        break;
      }

      //Check if we have a complete date
      var date = moment(val.join(''), 'DD/MM/YYYY', true);

      if (date.format() == 'Invalid date') {
        console.log('boo')
        this.$input.val('');

        //Update the user's display with progress
        this.$display.text(val.join(''));
      } else {
        //Update the input, since this looks legit
        console.log('yay')
        this.picker.addSelected(date);
        this.$display.text(date.format('DD/MM/YYYY'));
        this.$input.val(date.format('YYYY-MM-DD'));
        this.$input.data('has-key-entry', false);
      }
    },

    catchKey: function(e) {
      if (e.key === 'Backspace') {
        e.preventDefault();
        this.clear();
        return false;
      }
    },

    keyEntry: function() {
      var result = this.$input.data('has-key-entry') ? true : false;
      this.$input.data('has-key-entry', true);
      return result;
    },

    clear: function(){
      this.$input.val('');
      this.$display.text('');
    },

    close: function(){
      this.$picker.hide();
      this.open = false;

      $(document).off('.datePicker');
      $(window).off('.datePicker');
    },

    update: function(date) {
      var val = moment(date);
      this.current = val;
      this.$display.text(val.format('DD/MM/YYYY'));
      this.$input.val(val.format('YYYY-MM-DD'));
    },

    updateFromPicker: function() {
      var date = this.picker.getSelectedRaw()[0];
      this.update(date);
      this.$picker.hide();
      this.open = false;
    }
  };

  $.fn.islayDatePicker = function() {
    this.each(function() {
      var $this = $(this);
      if (!$this.data('islayDatePicker')) {
        $this.data('islayDatePicker', new DatePicker($this));
      }
    });
    return this;
  };
})(jQuery);
<|endoftext|>


<commit_before>import React from 'react'
import Slider from 'react-nouislider'
import _ from 'lodash'
import ReactCSSTransitionGroup from 'react-addons-css-transition-group'
import c from 'classnames'
import { t } from '../utils/i18n'
import DataSelection from '../utils/data-selection'
import CalcDrop from './calc-dropdown'

import { newCalcId, hideModalCalc, selectConversion, updateSliderValue, updateUCC } from '../actions'
import { shortenNumber } from '../utils/format'
import { getBuildingData } from '../utils/building-calc'

const Calculator = React.createClass({
  propTypes: {
    dispatch: React.PropTypes.func,
    calcVisible: React.PropTypes.bool,
    attributes: React.PropTypes.object,
    conversion: React.PropTypes.string,
    sliderValue: React.PropTypes.number,
    unitCostOfConstruction: React.PropTypes.number,
    newCalcId: React.PropTypes.string,
    queryParams: React.PropTypes.object,
    mapSource: React.PropTypes.object
  },

  changeCountry: function (id) {
    this.props.dispatch(newCalcId(id))
  },

  hideModal: function () {
    this.props.dispatch(updateUCC(null))
    this.props.dispatch(hideModalCalc())
  },

  onOutClick: function (e) {
    if (e.target === e.currentTarget) {
      this.hideModal()
    }
  },

  onChangeSlide: function (e) {
    this.props.dispatch(updateSliderValue(Number(e[0]) / 100))
  },

  selectConversion: function (conversion) {
    this.props.dispatch(selectConversion(conversion))
  },

  handleUCC: function (e) {
    this.props.dispatch(updateUCC(Number(e.target.value)))
  },

  onOptSelect: function (key, value, e) {
    e.preventDefault()
    const dataSelection = DataSelection(this.props.queryParams)
    dataSelection[key].setActive(value)
  },

  renderDropdown: function (paramKey, active, dropOpts) {
    return (
      <CalcDrop
        triggerElement='button'
        triggerClassName='button button--base-unbounded button__drop drop__toggle--caret'
        triggerTitle={t('Show/hide parameter options')}
        triggerText={t(active.key)} >

          {dropOpts.map(o => {
            return (

                  {t(o.key)}

)
          })}

      </CalcDrop>
    )
  },

  renderModal: function () {
    if (!this.props.calcVisible) return null
    const {sliderValue, conversion, newCalcId, queryParams} = this.props

    var countryCode = newCalcId

    // dataSelection['countryName'].setActive(countryCode)

    // Country codes not yet added to Mapbox data; hardcoding a country code for now
    // if (this.props.attributes.id) {
    //   countryCode = this.props.attributes.id
    //   newCalcId(countryCode)
    // } else {
    //   countryCode = 'BZ' // this.props.selectedCode
    // }

    const data = getBuildingData(countryCode, conversion, sliderValue, this.props.unitCostOfConstruction)
    let ucc = this.props.unitCostOfConstruction || data.unitCostOfConstruction

    // A little nonsense to create single roots for react
    const listKey = (conversion === 'retrofit' ? 'AAL as % of Value' : 'AAL in USD T')
    const TopFive = data.topFiveAAL.map(building => {
      return [
        (
{building['Description'].replace(/single|multi family/, '')}
),
        (
{`${(conversion === 'retrofit' ? '' : '$')}${(building[listKey] * (conversion === 'retrofit' ? 100 : 1)).toFixed(2)} ${(conversion === 'retrofit' ? '%' : '')}`}
)
      ]
    })

    const dataSelection = new DataSelection(queryParams)

    return (

Risk mitigation cost and benefit calculation

              <button className='modal__button-dismiss' title='Close' onClick={this.hideModel}>Dismiss</button>

Conversion Settings

Area calculated for

                      {this.renderDropdown('countryName', dataSelection.countryName.getActive(), dataSelection.countryName.getOptions())}

Type of Conversion

                    <button
                      className={'button header__language--toggle button__leftside ' + (conversion === 'retrofit' ? 'button--active' : '')}
                      onClick={() => this.selectConversion('retrofit')}>
                      Retrofit</button>
                    <button
                      className={'button header__language--toggle button__rightside ' + (conversion === 'replacement' ? 'button--active' : '')}
                      onClick={() => this.selectConversion('replacement')}>
                      Replace</button>

Unit cost per {(conversion === 'retrofit' ? 'retrofitted' : 'replaced')} building

<input type='number' className='calculator__input' value={Math.round(ucc)} onChange={this.handleUCC} />

Percent of buildings {(conversion === 'retrofit' ? 'retrofitted' : 'replaced')}

{Math.floor(sliderValue * 100)}%

                  <Slider
                    range={{min: 0, max: 100}}
                    start={[Math.round(sliderValue * 100)]}
                    step={5}
                    pips={{mode: 'range', density: 20}}
                    onSlide={this.onChangeSlide}
                  />

Building Stocks Converted

{data.buildingFrom}

are {(conversion === 'retrofit' ? 'retrofitted' : 'replaced')} with

{data.buildingTo}

Results

Reduction of overall AAL

 0 },
                    { 'stat__value--negative': (data.overallChangeAAL * this.props.attributes.AAL) < 0 }
                     )}>
                  ${shortenNumber(data.overallChangeAAL * this.props.attributes.AAL, 0, false)}

Total {(conversion === 'retrofit' ? 'retrofit' : 'replacement')} cost

${shortenNumber(data.conversionValue, 0, false)}

Flat rate years to break even

{(data.breakEven > 0 ? Math.round(data.breakEven) + ' Years' : 'Never')}

Percent Change in AAL for these housing units

 0 },
                    { 'stat__value--negative': (data.buildingChangeAAL * 100) < 0 }
                    )}>
                  {((data.buildingChangeAAL * 100) > 0 ? '-' : '')}{Math.abs(Math.round(data.buildingChangeAAL * 100))}%

Change in overall AAL

 0 },
                    { 'stat__value--negative': (data.overallChangeAAL * 100) < 0 }
                    )}>
                  {((data.overallChangeAAL * 100) > 0 ? '-' : '')}{Math.abs(Math.round(data.overallChangeAAL * 100))}%

Building Stock types most at risk ({listKey})

                {_.flatten(TopFive)}

    )
  },

  refill: function () {
    this.props.dispatch(newCalcId('BZ'))
  },

  render: function () {
    return (
      <ReactCSSTransitionGroup
        component='div'
        transitionName='modal'
        transitionEnterTimeout={300}
        transitionLeaveTimeout={300} >
          {this.renderModal()}
      </ReactCSSTransitionGroup>
    )
  }
})

export default Calculator
<commit_msg>Updating -- numbers change, active dropdown element doesnt<commit_after>import React from 'react'
import Slider from 'react-nouislider'
import _ from 'lodash'
import ReactCSSTransitionGroup from 'react-addons-css-transition-group'
import c from 'classnames'
import { t } from '../utils/i18n'
import DataSelection from '../utils/data-selection'
import CalcDrop from './calc-dropdown'

import { newCalcId, hideModalCalc, selectConversion, updateSliderValue, updateUCC } from '../actions'
import { shortenNumber } from '../utils/format'
import { getBuildingData } from '../utils/building-calc'

const Calculator = React.createClass({
  propTypes: {
    dispatch: React.PropTypes.func,
    calcVisible: React.PropTypes.bool,
    attributes: React.PropTypes.object,
    conversion: React.PropTypes.string,
    sliderValue: React.PropTypes.number,
    unitCostOfConstruction: React.PropTypes.number,
    newCalcId: React.PropTypes.string,
    queryParams: React.PropTypes.object,
    mapSource: React.PropTypes.object
  },

  changeCountry: function (id) {
    this.props.dispatch(newCalcId(id))
  },

  hideModal: function () {
    this.props.dispatch(updateUCC(null))
    this.props.dispatch(hideModalCalc())
  },

  onOutClick: function (e) {
    if (e.target === e.currentTarget) {
      this.hideModal()
    }
  },

  onChangeSlide: function (e) {
    this.props.dispatch(updateSliderValue(Number(e[0]) / 100))
  },

  selectConversion: function (conversion) {
    this.props.dispatch(selectConversion(conversion))
  },

  handleUCC: function (e) {
    this.props.dispatch(updateUCC(Number(e.target.value)))
  },

  onOptSelect: function (key, value, e) {
    e.preventDefault()
    this.props.dispatch(newCalcId(value))

    // These functions don't seem to be updating the Data...
    const dataSelection = DataSelection(this.props.queryParams)
    dataSelection[key].setActive(value)
  },

  renderDropdown: function (active, dropOpts) {

    return (
      <CalcDrop
        triggerElement='button'
        triggerClassName='button button--base-unbounded button__drop drop__toggle--caret'
        triggerTitle={t('Show/hide parameter options')}
        triggerText={t(active.key)} >

          {dropOpts.map(o => {
            return (

                  {t(o.key)}

)
          })}

      </CalcDrop>
    )
  },

  renderModal: function () {
    if (!this.props.calcVisible) return null
    const {sliderValue, conversion, newCalcId, queryParams} = this.props

    var countryCode = newCalcId

    const data = getBuildingData(countryCode, conversion, sliderValue, this.props.unitCostOfConstruction)
    let ucc = this.props.unitCostOfConstruction || data.unitCostOfConstruction

    // A little nonsense to create single roots for react
    const listKey = (conversion === 'retrofit' ? 'AAL as % of Value' : 'AAL in USD T')
    const TopFive = data.topFiveAAL.map(building => {
      return [
        (
{building['Description'].replace(/single|multi family/, '')}
),
        (
{`${(conversion === 'retrofit' ? '' : '$')}${(building[listKey] * (conversion === 'retrofit' ? 100 : 1)).toFixed(2)} ${(conversion === 'retrofit' ? '%' : '')}`}
)
      ]
    })

    const dataSelection = new DataSelection(queryParams)

    return (

Risk mitigation cost and benefit calculation

              <button className='modal__button-dismiss' title='Close' onClick={this.hideModel}>Dismiss</button>

Conversion Settings

Area calculated for

                      {this.renderDropdown(dataSelection.countryName.getActive(), dataSelection.countryName.getOptions())}

Type of Conversion

                    <button
                      className={'button header__language--toggle button__leftside ' + (conversion === 'retrofit' ? 'button--active' : '')}
                      onClick={() => this.selectConversion('retrofit')}>
                      Retrofit</button>
                    <button
                      className={'button header__language--toggle button__rightside ' + (conversion === 'replacement' ? 'button--active' : '')}
                      onClick={() => this.selectConversion('replacement')}>
                      Replace</button>

Unit cost per {(conversion === 'retrofit' ? 'retrofitted' : 'replaced')} building

<input type='number' className='calculator__input' value={Math.round(ucc)} onChange={this.handleUCC} />

Percent of buildings {(conversion === 'retrofit' ? 'retrofitted' : 'replaced')}

{Math.floor(sliderValue * 100)}%

                  <Slider
                    range={{min: 0, max: 100}}
                    start={[Math.round(sliderValue * 100)]}
                    step={5}
                    pips={{mode: 'range', density: 20}}
                    onSlide={this.onChangeSlide}
                  />

Building Stocks Converted

{data.buildingFrom}

are {(conversion === 'retrofit' ? 'retrofitted' : 'replaced')} with

{data.buildingTo}

Results

Reduction of overall AAL

 0 },
                    { 'stat__value--negative': (data.overallChangeAAL * this.props.attributes.AAL) < 0 }
                     )}>
                  ${shortenNumber(data.overallChangeAAL * this.props.attributes.AAL, 0, false)}

Total {(conversion === 'retrofit' ? 'retrofit' : 'replacement')} cost

${shortenNumber(data.conversionValue, 0, false)}

Flat rate years to break even

{(data.breakEven > 0 ? Math.round(data.breakEven) + ' Years' : 'Never')}

Percent Change in AAL for these housing units

 0 },
                    { 'stat__value--negative': (data.buildingChangeAAL * 100) < 0 }
                    )}>
                  {((data.buildingChangeAAL * 100) > 0 ? '-' : '')}{Math.abs(Math.round(data.buildingChangeAAL * 100))}%

Change in overall AAL

 0 },
                    { 'stat__value--negative': (data.overallChangeAAL * 100) < 0 }
                    )}>
                  {((data.overallChangeAAL * 100) > 0 ? '-' : '')}{Math.abs(Math.round(data.overallChangeAAL * 100))}%

Building Stock types most at risk ({listKey})

                {_.flatten(TopFive)}

    )
  },

  refill: function () {
    this.props.dispatch(newCalcId('BZ'))
  },

  render: function () {
    return (
      <ReactCSSTransitionGroup
        component='div'
        transitionName='modal'
        transitionEnterTimeout={300}
        transitionLeaveTimeout={300} >
          {this.renderModal()}
      </ReactCSSTransitionGroup>
    )
  }
})

export default Calculator
<|endoftext|>


<commit_before>import Ember from 'ember';
import ValidatedPaymentFormComponent from './validated-payment-form';

export default ValidatedPaymentFormComponent.extend({

  creditsCoverTotal: Ember.computed.alias('currentUser.nextBox.creditsCoverTotal'),
  purchase: 'purchase',
  skipPaymentMethod: Ember.computed.alias('currentUser.createSubscriptionAction.skipPaymentMethod'),
  total: Ember.computed.alias('currentUser.nextBox.total'),
  walletBalance: Ember.computed.alias('currentUser.walletBalance'),

  showPaymentForm: function () {
    if (!this.get('creditsCoverTotal')) return true;
    return this.get('skipPaymentMethod') === '0';
  }.property('creditsCoverTotal', 'skipPaymentMethod'),

  hasWalletBalance: function () {
    return this.get('walletBalance') > 0;
  }.property('walletBalance')

});
<commit_msg>Only scrolling to billing info if shipping address information has been completely filled out<commit_after>import Ember from 'ember';
import ValidatedPaymentFormComponent from './validated-payment-form';

export default ValidatedPaymentFormComponent.extend({

  creditsCoverTotal: Ember.computed.alias('currentUser.nextBox.creditsCoverTotal'),
  purchase: 'purchase',
  skipPaymentMethod: Ember.computed.alias('currentUser.createSubscriptionAction.skipPaymentMethod'),
  total: Ember.computed.alias('currentUser.nextBox.total'),
  walletBalance: Ember.computed.alias('currentUser.walletBalance'),

  shippingAddress: Ember.computed.oneWay('currentUser.subscription.shippingAddress'),

  shippingAddressComplete: function () {
    return !!(
      this.get('shippingAddress.firstName') &&
      this.get('shippingAddress.lastName') &&
      this.get('shippingAddress.addressLine_1') &&
      this.get('shippingAddress.city') &&
      this.get('shippingAddress.state') &&
      this.get('shippingAddress.zipCode')
    );
  }.property( 'shippingAddress.firstName',
              'shippingAddress.lastName',
              'shippingAddress.addressLine_1',
              'shippingAddress.city',
              'shippingAddress.state',
              'shippingAddress.zipCode'),

  showPaymentForm: function () {
    if (!this.get('creditsCoverTotal')) return true;
    return this.get('skipPaymentMethod') === '0';
  }.property('creditsCoverTotal', 'skipPaymentMethod'),

  hasWalletBalance: function () {
    return this.get('walletBalance') > 0;
  }.property('walletBalance')

});
<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose'),
	User = mongoose.model('User'),
	Group = mongoose.model('Group'),
	Helper = require('../../../custom_objects/Helper'),
	_ = require('lodash');

var PrivateFunctions = (function() {

	return {
		create:
		{
			createdGroups: function(req,group)
			{
				return function(arg1,arg2,done) {

					req.user.createdGroups.push(group._id);
					req.user.save(function(err) {
						if (err) {
							err = {
								model: User,
								err: err
							};
							console.log('error: ' + err);
							done(err,null,arg2);
						}
						else {

							done(null,group,arg2);
						}
					});
				};
			}
		},
		update:
		{
			joinedGroups: function(req,group)
			{
				return function(arg1,arg2,done) {

					var i = 0;
					var j = 0;

					console.log('req.body: ' + JSON.stringify(req.body,null,4));
					var oldMembers = group.members;
					var oldMembersIDs = [];
					var oldMembersIDsHash = {};

					//create the hashes for a speed increase later
					for(i = 0; i < oldMembers.length; ++i) {

						oldMembersIDs.push(oldMembers[i].toString());
						oldMembersIDsHash[oldMembers[i].toString()] = 1;
					}

					var members = req.body.members;
					req.body.members = [];

					for(i = 0; i < members.length; ++i) {
						req.body.members.push(members[i].toString());
					}

					for(i = 0; i < req.body.members.length; ++i)
						console.log('req members type: ' + typeof req.body.members[i]);

					for(i = 0; i < oldMembersIDs.length; ++i)
						console.log('oldMembersIDs type: ' + typeof oldMembersIDs[i]);

					console.log('oldMembersIDs: ' + oldMembersIDs);
					var removedUsers = _.difference(oldMembersIDs,req.body.members);
					console.log('removedUsers: ' + removedUsers);

					var query = {
						_id : {
							$in : req.body.members.concat(removedUsers)
						}
					};

					console.log('all users: ' + req.body.members.concat(removedUsers));
					Helper.find(User,query,function(err,mod) {

						Helper.populate(User,mod,function(err,mod) {
							var saveArray = [];

							console.log('members: ' + req.body.members);
							console.log('mod: ' + mod.length);
							for(i = 0; i < mod.length; ++i)
							{
								var updateData = {};
								var user = mod[i];
								console.log('working on user._id: ' + user._id);

								//check if user is an old member
								if(typeof oldMembersIDsHash[user._id.toString()] !== 'undefined' && removedUsers.indexOf(user._id.toString()) > -1)
								{
									console.log('user id old member: ' + user._id);
									console.log('groups count: ' + user.joinedGroups.length);
									for(j = 0; j < user.joinedGroups.length; ++j)
									{
										console.log('remove id? : ' + user.joinedGroups[j]._id);
										console.log('group id: ' + group.id);
										if(user.joinedGroups[j]._id.toString() === group.id.toString())
										{
											user.joinedGroups.splice(j,1);
											updateData.joinedGroups = user.joinedGroups;
											break;
										}
									}
								}
								//user is a current member of the group
								else
								{
									var alreadyJoined = false;
									console.log('user is new member or existing user: ' + user._id);
									console.log('groups count: ' + user.joinedGroups.length);

									for(j = 0; j < user.joinedGroups.length; ++j)
									{
										//user already has group added to its joinedGroups attribute. Just skip this user
										if(user.joinedGroups[j]._id.toString() === group.id.toString())
										{
											alreadyJoined = true;
											break;
										}
									}
									if(!alreadyJoined)
									{
										console.log('user has not yet joined: ' + user._id);
										user.joinedGroups.push(group.id);
										updateData.joinedGroups = user.joinedGroups;
									}
								}

								//create save functions for each object to use in waterfall later
								query = {
									_id : user._id
								};

								saveArray.push(Helper.update(User,query,updateData));
							}

							//call all of the saves using waterfall
							saveArray = Helper.buildWaterfall(saveArray);

							Helper.executeWaterfall(saveArray,function(err,data) {

								if (err) {
									err = {
										model: User,
										err: err
									};
									console.log('error: ' + err);
									done(err,null,arg2);
								}
								else {
									query = {
										_id : group.id
									};

									Helper.find(Group,query,function(err,mod) {

										if (err) {
											err = {
												model: Group,
												err: err
											};
											console.log('error: ' + err);
											done(err,null,arg2);
										}
										else {
											group = mod[0];
											done(null,group,1);
										}
									});
								}

							});
						});
					});
					//5515a5d1c6c8151504e9bedf
					//554908e4a7ed040000a11461
					//add to joinedGroup
					//for(var i = 0; i < req.body.members; ++i)
					//{

					//}

				};
			}
		}
	};
})();

module.exports = PrivateFunctions;<commit_msg>commit for ticket #65<commit_after>'use strict';

var mongoose = require('mongoose'),
	User = mongoose.model('User'),
	Group = mongoose.model('Group'),
	Helper = require('../../../custom_objects/Helper'),
	_ = require('lodash');

var PrivateFunctions = (function() {

	return {
		create:
		{
			createdGroups: function(req,group)
			{
				return function(arg1,arg2,done) {

					req.user.createdGroups.push(group._id);
					req.user.save(function(err) {
						if (err) {
							err = {
								model: User,
								err: err
							};
							console.log('error: ' + err);
							done(err,null,arg2);
						}
						else {

							done(null,group,arg2);
						}
					});
				};
			}
		},
		update:
		{
			joinedGroups: function(req,group)
			{
				return function(arg1,arg2,done) {

					var i = 0;
					var j = 0;

					console.log('req.body: ' + JSON.stringify(req.body,null,4));
					var oldMembers = group.members;
					var oldMembersIDs = [];
					var oldMembersIDsHash = {};

					//create the hashes for a speed increase later
					for(i = 0; i < oldMembers.length; ++i) {

						oldMembersIDs.push(oldMembers[i].toString());
						oldMembersIDsHash[oldMembers[i].toString()] = 1;
					}

					var members = req.body.members;
					req.body.members = [];

					for(i = 0; i < members.length; ++i) {
						var membersID = members[i].toString();
						if(membersID !== group.createdBy.toString())
							req.body.members.push(membersID);
					}

					for(i = 0; i < req.body.members.length; ++i)
						console.log('req members type: ' + typeof req.body.members[i]);

					for(i = 0; i < oldMembersIDs.length; ++i)
						console.log('oldMembersIDs type: ' + typeof oldMembersIDs[i]);

					console.log('oldMembersIDs: ' + oldMembersIDs);
					var removedUsers = _.difference(oldMembersIDs,req.body.members);
					console.log('removedUsers: ' + removedUsers);

					var query = {
						_id : {
							$in : req.body.members.concat(removedUsers)
						}
					};

					console.log('all users: ' + req.body.members.concat(removedUsers));
					Helper.find(User,query,function(err,mod) {

						Helper.populate(User,mod,function(err,mod) {
							var saveArray = [];

							console.log('members: ' + req.body.members);
							console.log('mod: ' + mod.length);
							for(i = 0; i < mod.length; ++i)
							{
								var updateData = {};
								var user = mod[i];
								console.log('working on user._id: ' + user._id);

								//check if user is an old member
								if(typeof oldMembersIDsHash[user._id.toString()] !== 'undefined' && removedUsers.indexOf(user._id.toString()) > -1)
								{
									console.log('user id old member: ' + user._id);
									console.log('groups count: ' + user.joinedGroups.length);
									for(j = 0; j < user.joinedGroups.length; ++j)
									{
										console.log('remove id? : ' + user.joinedGroups[j]._id);
										console.log('group id: ' + group.id);
										if(user.joinedGroups[j]._id.toString() === group.id.toString())
										{
											user.joinedGroups.splice(j,1);
											updateData.joinedGroups = user.joinedGroups;
											break;
										}
									}
								}
								//user is a current member of the group
								else
								{
									var alreadyJoined = false;
									console.log('user is new member or existing user: ' + user._id);
									console.log('groups count: ' + user.joinedGroups.length);

									for(j = 0; j < user.joinedGroups.length; ++j)
									{
										//user already has group added to its joinedGroups attribute. Just skip this user
										if(user.joinedGroups[j]._id.toString() === group.id.toString())
										{
											alreadyJoined = true;
											break;
										}
									}
									if(!alreadyJoined)
									{
										console.log('user has not yet joined: ' + user._id);
										user.joinedGroups.push(group.id);
										updateData.joinedGroups = user.joinedGroups;
									}
								}

								//create save functions for each object to use in waterfall later
								query = {
									_id : user._id
								};

								saveArray.push(Helper.update(User,query,updateData));
							}

							//call all of the saves using waterfall
							saveArray = Helper.buildWaterfall(saveArray);

							Helper.executeWaterfall(saveArray,function(err,data) {

								if (err) {
									err = {
										model: User,
										err: err
									};
									console.log('error: ' + err);
									done(err,null,arg2);
								}
								else {
									query = {
										_id : group.id
									};

									Helper.find(Group,query,function(err,mod) {

										if (err) {
											err = {
												model: Group,
												err: err
											};
											console.log('error: ' + err);
											done(err,null,arg2);
										}
										else {
											group = mod[0];
											done(null,group,1);
										}
									});
								}

							});
						});
					});
					//5515a5d1c6c8151504e9bedf
					//554908e4a7ed040000a11461
					//add to joinedGroup
					//for(var i = 0; i < req.body.members; ++i)
					//{

					//}

				};
			}
		}
	};
})();

module.exports = PrivateFunctions;<|endoftext|>


<commit_before>import React from 'react';
import ComposeFormContainer from './containers/compose_form_container';
import NavigationContainer from './containers/navigation_container';
import TrendTagsContainer from './containers/trend_tags_container';
import Announcements from './components/announcements';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { mountCompose, unmountCompose } from '../../actions/compose';
import { Link } from 'react-router-dom';
import { injectIntl, defineMessages } from 'react-intl';
import SearchContainer from './containers/search_container';
import Motion from '../ui/util/optional_motion';
import spring from 'react-motion/lib/spring';
import SearchResultsContainer from './containers/search_results_container';
import { changeComposing } from '../../actions/compose';

const messages = defineMessages({
  start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
  home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
  notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
  suggested_accounts: { id: 'column.suggested_accounts', defaultMessage: 'Active Users' },
  community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
  preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
  help: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
});

const mapStateToProps = state => ({
  columns: state.getIn(['settings', 'columns']),
  showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
});

@connect(mapStateToProps)
@injectIntl
export default class Compose extends React.PureComponent {

  static propTypes = {
    dispatch: PropTypes.func.isRequired,
    columns: ImmutablePropTypes.list.isRequired,
    multiColumn: PropTypes.bool,
    showSearch: PropTypes.bool,
    intl: PropTypes.object.isRequired,
  };

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

  componentWillUnmount () {
    this.props.dispatch(unmountCompose());
  }

  onFocus = () => {
    this.props.dispatch(changeComposing(true));
  }

  onBlur = () => {
    this.props.dispatch(changeComposing(false));
  }

  render () {
    const { multiColumn, showSearch, intl } = this.props;

    let header = '';

    if (multiColumn) {
      const { columns } = this.props;
      header = (

          <Link to='/getting-started' className='drawer__tab' title={intl.formatMessage(messages.start)} aria-label={intl.formatMessage(messages.start)}></Link>
          {!columns.some(column => column.get('id') === 'HOME') && (
            <Link to='/timelines/home' className='drawer__tab' title={intl.formatMessage(messages.home_timeline)} aria-label={intl.formatMessage(messages.home_timeline)}></Link>
          )}
          {!columns.some(column => column.get('id') === 'NOTIFICATIONS') && (
            <Link to='/notifications' className='drawer__tab' title={intl.formatMessage(messages.notifications)} aria-label={intl.formatMessage(messages.notifications)}></Link>
          )}
          {!columns.some(column => column.get('id') === 'COMMUNITY') && (
            <Link to='/timelines/public/local' className='drawer__tab' title={intl.formatMessage(messages.community)} aria-label={intl.formatMessage(messages.community)}></Link>
          )}
          <Link to='/web/suggested_accounts' className='drawer__tab' title={intl.formatMessage(messages.suggested_accounts)} aria-label={intl.formatMessage(messages.suggested_accounts)}></Link>

      );
    }

    return (

        {header}

        <SearchContainer />

              <NavigationContainer onClose={this.onBlur} />
              <ComposeFormContainer />

            <Announcements />

              <TrendTagsContainer />

          <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
            {({ x }) =>
              <div className='drawer__inner darker' style={{ transform: `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
                <SearchResultsContainer />

            }
          </Motion>

    );
  }

}
<commit_msg>fix bug<commit_after>import React from 'react';
import ComposeFormContainer from './containers/compose_form_container';
import NavigationContainer from './containers/navigation_container';
import TrendTagsContainer from './containers/trend_tags_container';
import Announcements from './components/announcements';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import { mountCompose, unmountCompose } from '../../actions/compose';
import { Link } from 'react-router-dom';
import { injectIntl, defineMessages } from 'react-intl';
import SearchContainer from './containers/search_container';
import Motion from '../ui/util/optional_motion';
import spring from 'react-motion/lib/spring';
import SearchResultsContainer from './containers/search_results_container';
import { changeComposing } from '../../actions/compose';

const messages = defineMessages({
  start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
  home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
  notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
  suggested_accounts: { id: 'column.suggested_accounts', defaultMessage: 'Active Users' },
  community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
  preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
  help: { id: 'navigation_bar.help', defaultMessage: 'Help' },
});

const mapStateToProps = state => ({
  columns: state.getIn(['settings', 'columns']),
  showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']),
});

@connect(mapStateToProps)
@injectIntl
export default class Compose extends React.PureComponent {

  static propTypes = {
    dispatch: PropTypes.func.isRequired,
    columns: ImmutablePropTypes.list.isRequired,
    multiColumn: PropTypes.bool,
    showSearch: PropTypes.bool,
    intl: PropTypes.object.isRequired,
  };

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

  componentWillUnmount () {
    this.props.dispatch(unmountCompose());
  }

  onFocus = () => {
    this.props.dispatch(changeComposing(true));
  }

  onBlur = () => {
    this.props.dispatch(changeComposing(false));
  }

  render () {
    const { multiColumn, showSearch, intl } = this.props;

    let header = '';

    if (multiColumn) {
      const { columns } = this.props;
      header = (

          <Link to='/getting-started' className='drawer__tab' title={intl.formatMessage(messages.start)} aria-label={intl.formatMessage(messages.start)}></Link>
          {!columns.some(column => column.get('id') === 'HOME') && (
            <Link to='/timelines/home' className='drawer__tab' title={intl.formatMessage(messages.home_timeline)} aria-label={intl.formatMessage(messages.home_timeline)}></Link>
          )}
          {!columns.some(column => column.get('id') === 'NOTIFICATIONS') && (
            <Link to='/notifications' className='drawer__tab' title={intl.formatMessage(messages.notifications)} aria-label={intl.formatMessage(messages.notifications)}></Link>
          )}
          {!columns.some(column => column.get('id') === 'COMMUNITY') && (
            <Link to='/timelines/public/local' className='drawer__tab' title={intl.formatMessage(messages.community)} aria-label={intl.formatMessage(messages.community)}></Link>
          )}
          <Link to='/web/suggested_accounts' className='drawer__tab' title={intl.formatMessage(messages.suggested_accounts)} aria-label={intl.formatMessage(messages.suggested_accounts)}></Link>

      );
    }

    return (

        {header}

        <SearchContainer />

              <NavigationContainer onClose={this.onBlur} />
              <ComposeFormContainer />

            <Announcements />

              <TrendTagsContainer />

          <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
            {({ x }) =>
              <div className='drawer__inner darker' style={{ transform: `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}>
                <SearchResultsContainer />

            }
          </Motion>

    );
  }

}
<|endoftext|>


<commit_before>import React, {Component} from 'react'
import {
  Route,
  Link,
  Switch,
  withRouter
} from 'react-router-dom'

import sanitizeHtml from 'sanitize-html';

import { connect } from 'react-redux'

import {
  RowColumnContainer,
  ColumnContainer,
  GridElement,
  FixedHeader,
  ConversationsButtons,
  Overflow
} from '../components/conversation/styles'

import Hidden from '@material-ui/core/Hidden'
import Button from '@material-ui/core/Button'
import CheckIcon from '@material-ui/icons/Check'
import InboxIcon from '@material-ui/icons/Inbox'
import FilterListIcon from '@material-ui/icons/FilterList'
import ChatIcon from '@material-ui/icons/Chat'
import Tooltip from '@material-ui/core/Tooltip';
import Paper from '@material-ui/core/Paper'
import Typography from '@material-ui/core/Typography'
import UserListItem from '../components/conversation/UserListItem'
import Progress from '../shared/Progress'
import FilterMenu from '../components/conversation/filterMenu'

import ConversationContainerShow from '../components/conversation/container'
import image from '../../../assets/images/empty-icon8.png'
import {
  getConversations,
  updateConversationsData,
  clearConversations,
} from '../actions/conversations'

import {
  getAppUser
} from '../actions/app_user'

import AssignmentRules from '../components/conversation/assignmentRules'
import {setCurrentSection, setCurrentPage} from '../actions/navigation'

class ConversationContainer extends Component {

  constructor(props){
    super(props)
    this.fetching = false
  }

  componentDidMount(){
    this.getConversations()

    this.props.dispatch(
      setCurrentSection('Conversations')
    )
  }

  handleScroll = (e) => {
    let element = e.target

    //console.log(element.scrollHeight - element.scrollTop, element.clientHeight)
    if (element.scrollHeight - element.scrollTop === element.clientHeight) {
      if (this.props.conversations.meta.next_page && !this.fetching){
        this.fetching = true
        this.getConversations(()=>{
          this.fetching = false
        })
      }
    }
  }

  getConversations = (cb)=>{
    this.props.dispatch(getConversations( ()=>{
      cb && cb()
    }))
  }

  setSort = (option)=>{
    this.props.dispatch(updateConversationsData({sort: option}))
    this.setState({sort: option})
  }

  setFilter = (option)=>{
    this.props.dispatch(updateConversationsData({filter: option}))
  }

  filterButton = (handleClick)=>{
    return <Tooltip title="filter conversations">

        <Button
          aria-label="More"
          aria-controls="long-menu"
          aria-haspopup="true"
          variant={"outlined"}
          onClick={handleClick}
          size="small"
        >
          {/*<MoreVertIcon />*/}
          {this.props.conversations.filter}
        </Button>

       </Tooltip>
  }

  sortButton = (handleClick)=>{
    return <Tooltip title="sort conversations">
        <Button
          aria-label="More"
          aria-controls="long-menu"
          aria-haspopup="true"
          variant={"outlined"}
          onClick={handleClick}
          size="small"
        >
          {/*<MoreVertIcon />*/}
          {this.props.conversations.sort}
        </Button>

       </Tooltip>
  }

  filterConversations = (options, cb)=>{
    this.props.dispatch(
      updateConversationsData({filter: options.id, collection: []}, ()=>{
        this.getConversations(cb)
      })
    )
  }

  sortConversations = (options, cb)=>{
    this.props.dispatch(
      updateConversationsData({sort: options.id, collection: []}, ()=>{
        this.getConversations(cb)
      })
    )
  }

  renderConversationContent = (o)=>{
    const message = o.lastMessage.message
    if(message.htmlContent)
      return sanitizeHtml(message.htmlContent).substring(0, 250)
  }

  renderConversations = (appId)=>{
    return <GridElement noFlex style={{
                boxShadow: 'rgba(204, 204, 204, 0.52) 8px -4px 20px 0px'
              }}>
              {/*<FixedHeader>Conversations</FixedHeader>*/}

              <FixedHeader style={{height: '67px'}}>

                {/*<HeaderTitle>
                  Conversations
                </HeaderTitle>*/}

                <ConversationsButtons>

                  <FilterMenu
                    options={[
                      {id: "opened", name: "opened", count: 1, icon: <InboxIcon/> },
                      {id: "closed", name: "closed", count: 2, icon: <CheckIcon/>},
                    ]}
                    value={this.props.conversations.filter}
                    filterHandler={this.filterConversations}
                    triggerButton={this.filterButton}
                  />

                  <FilterMenu
                    options={[
                      {id: "newest", name: "newest", count: 1, selected: true},
                      {id: "oldest", name: "oldest", count: 1},
                      {id: "waiting", name: "waiting", count: 1},
                      {id: "priority-first", name: "priority first", count: 1},
                      {id: "unfiltered", name: "all", count: 1 }
                    ]}
                    value={this.props.conversations.sort}
                    filterHandler={this.sortConversations}
                    triggerButton={this.sortButton}
                  />

                </ConversationsButtons>

              </FixedHeader>

              <Overflow onScroll={this.handleScroll}>

                {
                  this.props.conversations.collection.map((o, i)=>{

                    const user = o.mainParticipant

                    return
 this.props.history.push(`/apps/${appId}/conversations/${o.key}`) }>

                              <UserListItem
                                value={this.props.conversation.key}
                                mainUser={user}
                                object={o.key}
                                messageUser={o.lastMessage.appUser}
                                showUserDrawer={()=>this.props.actions.showUserDrawer(user.id)}
                                messageObject={o.lastMessage}
                                conversation={o}
                                createdAt={o.lastMessage.message.createdAt}
                                message={this.renderConversationContent(o)}
                              />

                              {/*<MessageItem
                                conversation={o}
                                message={o.lastMessage}
                              />*/}

                  })
                }

                {this.props.conversations.loading ?
                  <Progress/>
                : null }

              </Overflow>
            </GridElement>
  }

  render(){
    const {appId} = this.props.match.params

    return <RowColumnContainer>

            <ColumnContainer>

              <Hidden smUp>
                <Route exact path={`/apps/${appId}/conversations`}
                  render={(props)=>(
                    this.renderConversations(appId)
                  )}
                />
              </Hidden>

              <Hidden smDown>
                {
                  this.renderConversations(appId)
                }
              </Hidden>

              {/*
                <Drawer
                open={this.state.displayMode === "conversations"}
                onClose={this.hideDrawer}>
                {this.renderConversations()}
                </Drawer>
              */}

              <Switch>

                <Route exact path={`/apps/${appId}/conversations`}
                  render={(props)=>(
                    <EmptyConversation dispatch={this.props.dispatch}/>
                  )} />

                <Route exact path={`/apps/${appId}/conversations/assignment_rules`}
                    render={(props)=>(
                      <GridElement grow={2} style={{
                        display: 'flex',
                        justifyContent: 'space-around'
                      }}>

                        <AssignmentRules/>

                      </GridElement>
                  )} />

                <Route exact path={`/apps/${appId}/conversations/:id`}
                    render={(props)=>(
                      <ConversationContainerShow
                        appId={appId}
                        app={this.props.app}
                        events={this.props.events}
                        conversation={this.props.conversation}
                        showUserDrawer={this.props.actions.showUserDrawer}
                        currentUser={this.props.currentUser}
                        {...props}
                      />
                  )} />

              </Switch>

            </ColumnContainer>
          </RowColumnContainer>
  }
}

class EmptyConversation extends Component {

  componentDidMount(){
    this.props.dispatch(setCurrentPage("Conversations"))
  }

  render(){
    return <Hidden smDown>
            <GridElement grow={2} style={{
              display: 'flex',
              justifyContent: 'space-around'
            }}>

                <Paper style={{padding: '2em'}}>

                    <ChatIcon fontSize="large" />
                    <Typography variant="h5">
                        Conversations
                      </Typography>

                      <Typography component="p">
                        Select a conversation
                      </Typography>

                </Paper>

            </GridElement>
          </Hidden>

  }
}

function mapStateToProps(state) {

  const { auth, app, conversations, conversation, app_user } = state
  const { loading, isAuthenticated } = auth
  //const { sort, filter, collection , meta, loading} = conversations

  return {
    conversations,
    conversation,
    app_user,
    app,
    isAuthenticated
  }
}

export default withRouter(connect(mapStateToProps)(ConversationContainer))<commit_msg>Update ConversationContainer.js<commit_after>import React, {Component} from 'react'
import {
  Route,
  Link,
  Switch,
  withRouter
} from 'react-router-dom'

import sanitizeHtml from 'sanitize-html';

import { connect } from 'react-redux'

import {
  RowColumnContainer,
  ColumnContainer,
  GridElement,
  FixedHeader,
  ConversationsButtons,
  Overflow
} from '../components/conversation/styles'

import Hidden from '@material-ui/core/Hidden'
import Button from '@material-ui/core/Button'
import CheckIcon from '@material-ui/icons/Check'
import InboxIcon from '@material-ui/icons/Inbox'
import FilterListIcon from '@material-ui/icons/FilterList'
import ChatIcon from '@material-ui/icons/Chat'
import Tooltip from '@material-ui/core/Tooltip';
import Paper from '@material-ui/core/Paper'
import Typography from '@material-ui/core/Typography'
import UserListItem from '../components/conversation/UserListItem'
import Progress from '../shared/Progress'
import FilterMenu from '../components/conversation/filterMenu'

import ConversationContainerShow from '../components/conversation/container'
import image from '../../../assets/images/empty-icon8.png'
import {
  getConversations,
  updateConversationsData,
  clearConversations,
} from '../actions/conversations'

import {
  getAppUser
} from '../actions/app_user'

import AssignmentRules from '../components/conversation/assignmentRules'
import {setCurrentSection, setCurrentPage} from '../actions/navigation'

class ConversationContainer extends Component {

  constructor(props){
    super(props)
    this.fetching = false
    this.props.dispatch( clearConversations([]) )
  }

  componentDidMount(){
    this.getConversations()

    this.props.dispatch(
      setCurrentSection('Conversations')
    )
  }

  handleScroll = (e) => {
    let element = e.target

    //console.log(element.scrollHeight - element.scrollTop, element.clientHeight)
    if (element.scrollHeight - element.scrollTop === element.clientHeight) {
      if (this.props.conversations.meta.next_page && !this.fetching){
        this.fetching = true
        this.getConversations(()=>{
          this.fetching = false
        })
      }
    }
  }

  getConversations = (cb)=>{
    this.props.dispatch(getConversations( ()=>{
      cb && cb()
    }))
  }

  setSort = (option)=>{
    this.props.dispatch(updateConversationsData({sort: option}))
    this.setState({sort: option})
  }

  setFilter = (option)=>{
    this.props.dispatch(updateConversationsData({filter: option}))
  }

  filterButton = (handleClick)=>{
    return <Tooltip title="filter conversations">

        <Button
          aria-label="More"
          aria-controls="long-menu"
          aria-haspopup="true"
          variant={"outlined"}
          onClick={handleClick}
          size="small"
        >
          {/*<MoreVertIcon />*/}
          {this.props.conversations.filter}
        </Button>

       </Tooltip>
  }

  sortButton = (handleClick)=>{
    return <Tooltip title="sort conversations">
        <Button
          aria-label="More"
          aria-controls="long-menu"
          aria-haspopup="true"
          variant={"outlined"}
          onClick={handleClick}
          size="small"
        >
          {/*<MoreVertIcon />*/}
          {this.props.conversations.sort}
        </Button>

       </Tooltip>
  }

  filterConversations = (options, cb)=>{
    this.props.dispatch(
      updateConversationsData({filter: options.id, collection: []}, ()=>{
        this.getConversations(cb)
      })
    )
  }

  sortConversations = (options, cb)=>{
    this.props.dispatch(
      updateConversationsData({sort: options.id, collection: []}, ()=>{
        this.getConversations(cb)
      })
    )
  }

  renderConversationContent = (o)=>{
    const message = o.lastMessage.message
    if(message.htmlContent)
      return sanitizeHtml(message.htmlContent).substring(0, 250)
  }

  renderConversations = (appId)=>{
    return <GridElement noFlex style={{
                boxShadow: 'rgba(204, 204, 204, 0.52) 8px -4px 20px 0px'
              }}>
              {/*<FixedHeader>Conversations</FixedHeader>*/}

              <FixedHeader style={{height: '67px'}}>

                {/*<HeaderTitle>
                  Conversations
                </HeaderTitle>*/}

                <ConversationsButtons>

                  <FilterMenu
                    options={[
                      {id: "opened", name: "opened", count: 1, icon: <InboxIcon/> },
                      {id: "closed", name: "closed", count: 2, icon: <CheckIcon/>},
                    ]}
                    value={this.props.conversations.filter}
                    filterHandler={this.filterConversations}
                    triggerButton={this.filterButton}
                  />

                  <FilterMenu
                    options={[
                      {id: "newest", name: "newest", count: 1, selected: true},
                      {id: "oldest", name: "oldest", count: 1},
                      {id: "waiting", name: "waiting", count: 1},
                      {id: "priority-first", name: "priority first", count: 1},
                      {id: "unfiltered", name: "all", count: 1 }
                    ]}
                    value={this.props.conversations.sort}
                    filterHandler={this.sortConversations}
                    triggerButton={this.sortButton}
                  />

                </ConversationsButtons>

              </FixedHeader>

              <Overflow onScroll={this.handleScroll}>

                {
                  this.props.conversations.collection.map((o, i)=>{

                    const user = o.mainParticipant

                    return
 this.props.history.push(`/apps/${appId}/conversations/${o.key}`) }>

                              <UserListItem
                                value={this.props.conversation.key}
                                mainUser={user}
                                object={o.key}
                                messageUser={o.lastMessage.appUser}
                                showUserDrawer={()=>this.props.actions.showUserDrawer(user.id)}
                                messageObject={o.lastMessage}
                                conversation={o}
                                createdAt={o.lastMessage.message.createdAt}
                                message={this.renderConversationContent(o)}
                              />

                              {/*<MessageItem
                                conversation={o}
                                message={o.lastMessage}
                              />*/}

                  })
                }

                {this.props.conversations.loading ?
                  <Progress/>
                : null }

              </Overflow>
            </GridElement>
  }

  render(){
    const {appId} = this.props.match.params

    return <RowColumnContainer>

            <ColumnContainer>

              <Hidden smUp>
                <Route exact path={`/apps/${appId}/conversations`}
                  render={(props)=>(
                    this.renderConversations(appId)
                  )}
                />
              </Hidden>

              <Hidden smDown>
                {
                  this.renderConversations(appId)
                }
              </Hidden>

              {/*
                <Drawer
                open={this.state.displayMode === "conversations"}
                onClose={this.hideDrawer}>
                {this.renderConversations()}
                </Drawer>
              */}

              <Switch>

                <Route exact path={`/apps/${appId}/conversations`}
                  render={(props)=>(
                    <EmptyConversation dispatch={this.props.dispatch}/>
                  )} />

                <Route exact path={`/apps/${appId}/conversations/assignment_rules`}
                    render={(props)=>(
                      <GridElement grow={2} style={{
                        display: 'flex',
                        justifyContent: 'space-around'
                      }}>

                        <AssignmentRules/>

                      </GridElement>
                  )} />

                <Route exact path={`/apps/${appId}/conversations/:id`}
                    render={(props)=>(
                      <ConversationContainerShow
                        appId={appId}
                        app={this.props.app}
                        events={this.props.events}
                        conversation={this.props.conversation}
                        showUserDrawer={this.props.actions.showUserDrawer}
                        currentUser={this.props.currentUser}
                        {...props}
                      />
                  )} />

              </Switch>

            </ColumnContainer>
          </RowColumnContainer>
  }
}

class EmptyConversation extends Component {

  componentDidMount(){
    this.props.dispatch(setCurrentPage("Conversations"))
  }

  render(){
    return <Hidden smDown>
            <GridElement grow={2} style={{
              display: 'flex',
              justifyContent: 'space-around'
            }}>

                <Paper style={{padding: '2em'}}>

                    <ChatIcon fontSize="large" />
                    <Typography variant="h5">
                        Conversations
                      </Typography>

                      <Typography component="p">
                        Select a conversation
                      </Typography>

                </Paper>

            </GridElement>
          </Hidden>

  }
}

function mapStateToProps(state) {

  const { auth, app, conversations, conversation, app_user } = state
  const { loading, isAuthenticated } = auth
  //const { sort, filter, collection , meta, loading} = conversations

  return {
    conversations,
    conversation,
    app_user,
    app,
    isAuthenticated
  }
}

export default withRouter(connect(mapStateToProps)(ConversationContainer))<|endoftext|>


<commit_before>'use strict';

angular.module('ddsApp').controller('FoyerRessourceYearMoins2Ctrl', function($scope, $state, categoriesRnc, IndividuService) {
    $scope.yearMoins2 = moment().subtract('years', 2).format('YYYY');

    var parents = IndividuService.getParents($scope.situation.individus);
    $scope.individuRefs = [];
    parents.forEach(function(parent) {
        var individuRef = {
            individu: parent,
            label: IndividuService.label(parent),
            rnc: []
        };
        categoriesRnc.forEach(function(categorieRnc) {
            var ressource = _.find(parent.ressources, { type: categorieRnc.id });
            var montant = ressource ? ressource.montant : 0;
            individuRef.rnc.push({ categorie: categorieRnc, montant: montant });
        });
        $scope.individuRefs.push(individuRef);
    });

    $scope.submit = function() {
        $scope.individuRefs.forEach(function(individuRef) {
            // clean anciennes valeurs
            individuRef.individu.ressources = _.filter(individuRef.individu.ressources, function(ressource) {
                return !_.find(categoriesRnc, { id: ressource.type });
            });
            individuRef.rnc.forEach(function(rnc) {
                if (rnc.montant > 0) {
                    individuRef.individu.ressources.push({
                        periode: $scope.yearMoins2,
                        type: rnc.categorie.id,
                        montant: rnc.montant
                    });
                }
            });
        });

        $scope.$emit('rnc');
    };
});
<commit_msg>sauvegarde des montants de rnc capturés même s'ils sont nuls<commit_after>'use strict';

angular.module('ddsApp').controller('FoyerRessourceYearMoins2Ctrl', function($scope, $state, categoriesRnc, IndividuService) {
    $scope.yearMoins2 = moment().subtract('years', 2).format('YYYY');

    var parents = IndividuService.getParents($scope.situation.individus);
    $scope.individuRefs = [];
    parents.forEach(function(parent) {
        var individuRef = {
            individu: parent,
            label: IndividuService.label(parent),
            rnc: []
        };
        categoriesRnc.forEach(function(categorieRnc) {
            var ressource = _.find(parent.ressources, { type: categorieRnc.id });
            var montant = ressource ? ressource.montant : 0;
            individuRef.rnc.push({ categorie: categorieRnc, montant: montant });
        });
        $scope.individuRefs.push(individuRef);
    });

    $scope.submit = function() {
        $scope.individuRefs.forEach(function(individuRef) {
            // clean anciennes valeurs
            individuRef.individu.ressources = _.filter(individuRef.individu.ressources, function(ressource) {
                return !_.find(categoriesRnc, { id: ressource.type });
            });
            individuRef.rnc.forEach(function(rnc) {
                individuRef.individu.ressources.push({
                    periode: $scope.yearMoins2,
                    type: rnc.categorie.id,
                    montant: rnc.montant
                });
            });
        });

        $scope.$emit('rnc');
    };
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import DS from 'ember-data';

const { computed }  = Ember;
const { alias, equal } = computed;

export default DS.Model.extend({
  title: DS.attr('string'),
  description: DS.attr('string'),
  required: DS.attr('number'),
  childSequenceOrder: DS.attr('number'),
  orderInSequence: DS.attr('number'),
  minimum: DS.attr('number'),
  maximum: DS.attr('number'),
  track: DS.attr('boolean'),
  startDate: DS.attr('date'),
  endDate: DS.attr('date'),
  duration: DS.attr('number'),
  academicLevel: DS.belongsTo('curriculum-inventory-academic-level', {async: true}),
  parent: DS.belongsTo('curriculum-inventory-sequence-block', {async: true, inverse: 'children'}),
  children: DS.hasMany('curriculum-inventory-sequence-block', {async: true, inverse: 'parent'}),
  report: DS.belongsTo('curriculum-inventory-report', {async: true}),
  sessions: DS.hasMany('session', {async: true}),
  course: DS.belongsTo('course', {async: true}),

  isFinalized: alias('report.isFinalized'),
  isRequired: equal('required', 1),
  isOptional: equal('required', 2),
  isRequiredInTrack: equal('required', 3),
  isOrdered: equal('childSequenceOrder', 1),
  isUnordered: equal('childSequenceOrder', 2),
  isParallel: equal('childSequenceOrder', 3),

  allParents: computed('parent', 'parent.allParents.[]', function(){
    return new Promise(resolve => {
      this.get('parent').then(parent => {
        let parents = [];
        if(!parent){
          resolve(parents);
        } else {
          parents.pushObject(parent);
          parent.get('allParents').then(allParents => {
            parents.pushObjects(allParents);
            resolve(parents);
          });
        }
      });
    });
  }),
});
<commit_msg>added docblock to allParents computed property of sequence block model.<commit_after>import Ember from 'ember';
import DS from 'ember-data';

const { computed }  = Ember;
const { alias, equal } = computed;

export default DS.Model.extend({
  title: DS.attr('string'),
  description: DS.attr('string'),
  required: DS.attr('number'),
  childSequenceOrder: DS.attr('number'),
  orderInSequence: DS.attr('number'),
  minimum: DS.attr('number'),
  maximum: DS.attr('number'),
  track: DS.attr('boolean'),
  startDate: DS.attr('date'),
  endDate: DS.attr('date'),
  duration: DS.attr('number'),
  academicLevel: DS.belongsTo('curriculum-inventory-academic-level', {async: true}),
  parent: DS.belongsTo('curriculum-inventory-sequence-block', {async: true, inverse: 'children'}),
  children: DS.hasMany('curriculum-inventory-sequence-block', {async: true, inverse: 'parent'}),
  report: DS.belongsTo('curriculum-inventory-report', {async: true}),
  sessions: DS.hasMany('session', {async: true}),
  course: DS.belongsTo('course', {async: true}),

  isFinalized: alias('report.isFinalized'),
  isRequired: equal('required', 1),
  isOptional: equal('required', 2),
  isRequiredInTrack: equal('required', 3),
  isOrdered: equal('childSequenceOrder', 1),
  isUnordered: equal('childSequenceOrder', 2),
  isParallel: equal('childSequenceOrder', 3),

  /**
   * A list of all ancestors (parent, its parents parent etc) of this sequence block.
   * First element of the list is the block's direct ancestor (parent), while the last element is the oldest ancestor.
   *
   * Returns a promise that resolves to an array of sequence block objects.
   * If this sequence block is a top-level block within its owning report, then that array is empty.
   * @property allParents
   * @type {Ember.computed}
   * @public
   * @todo Rename this property to 'ancestors'. [ST 2016/11/01]
   */
  allParents: computed('parent', 'parent.allParents.[]', function(){
    return new Promise(resolve => {
      this.get('parent').then(parent => {
        let parents = [];
        if(!parent){
          resolve(parents);
        } else {
          parents.pushObject(parent);
          parent.get('allParents').then(allParents => {
            parents.pushObjects(allParents);
            resolve(parents);
          });
        }
      });
    });
  }),
});
<|endoftext|>


<commit_before><commit_msg>added 2 sheets<commit_after><|endoftext|>