<commit_before>/*!
    Copyright (C) 2015 Google Inc., authors, and contributors <see AUTHORS file>
    Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
    Created By: ivan@reciprocitylabs.com
    Maintained By: ivan@reciprocitylabs.com
*/

(function(can, $) {
  "use strict";
  var MapperModel = can.Map({
      type: "AllObject", // We set default as All Object
      contact: {},
      deferred: "@",
      deferred_to: "@",
      term: "",
      object: "",
      model: {},
      bindings: {},
      is_loading: false,
      is_saving: false,
      all_selected: false,
      search_only: false,
      join_object_id: "",
      selected: new can.List(),
      entries: new can.List(),
      relevant: new can.List(),
      model_from_type: function (type) {
        var types = _.reduce(_.values(this.types()), function (memo, val) {
          if (val.items) {
            return memo.concat(val.items);
          }
          return memo;
        }, []);
        return _.findWhere(types, {value: type});
      },
      types: can.compute(function () {
        var selector_list = GGRC.Mappings.get_canonical_mappings_for(this.object),
            forbidden = ["workflow", "taskgroup", "gdrivefolder", "context"],
            groups = {
              "all_objects": {
                name: "All Objects",
                value: "AllObject",
                plural: "allobjects",
                table_plural: "allobjects",
                singular: "AllObject",
                models: []
              },
              "entities": {
                name: "People/Groups",
                items: []
              },
              "business": {
                name: "Assets/Business",
                items: []
              },
              "governance": {
                name: "Governance",
                items: []
              }
            };

        can.each(selector_list, function (model, model_name) {
          if (!model_name || !CMS.Models[model_name] || ~forbidden.indexOf(model_name.toLowerCase())) {
            return;
          }
          var cms_model = CMS.Models[model_name],
              group = !groups[cms_model.category] ? "governance" : cms_model.category;

          groups[group]["items"].push({
            name: cms_model.title_plural,
            value: cms_model.shortName,
            singular: cms_model.shortName,
            plural: cms_model.title_plural.toLowerCase().replace(/\s+/, "_"),
            table_plural: cms_model.table_plural,
            isSelected: cms_model.shortName === this.type
          });
          groups["all_objects"]["models"].push(cms_model.shortName);
        }, this);
        return groups;
      })
    });

  can.Component.extend({
    tag: "modal-mapper",
    template: can.view(GGRC.mustache_path + "/modals/mapper/base.mustache"),
    scope: function (attrs, parentScope, el) {
      var $el = $(el),
          data = {},
          id = +$el.attr("join-object-id"),
          object = $el.attr("object"),
          type = $el.attr("type"),
          tree_view = GGRC.tree_view.sub_tree_for[object];

      if ($el.attr("search-only")) {
        data["search_only"] =  /true/i.test($el.attr("search-only"));
      }
      if (object) {
        data["object"] = object;
      }
      if (!data["search_only"]) {
        if (id === GGRC.page_instance().id || !tree_view) {
          data["type"] = GGRC.Models[type] ? type : "AllObject";
        } else {
          data["type"] = tree_view.display_list[0];
        }
      }
      data["join_object_id"] = id || GGRC.page_instance().id;
      return {
        mapper: new MapperModel(data)
      };
    },
    events: {
      "inserted": function () {
        this.setModel();
        this.setBinding();
      },
      "deferedSave": function () {
        var data = {
              multi_map: true,
              arr: _.map(this.scope.attr("mapper.selected"), function (desination) {
                    var inst = _.find(this.scope.attr("mapper.entries"), function (entry) {
                      return entry.instance.id === desination.id;
                    });
                    if (inst) {
                      return inst.instance;
                    }
                  }.bind(this))
            };
        this.scope.attr("deferred_to").controller.element.trigger("defer:add", [data, {map_and_save: true}]);
        // TODO: Find proper way to dismiss the modal
        this.element.find(".modal-dismiss").trigger("click");
      },
      ".modal-footer .btn-map click": function (el, ev) {
        ev.preventDefault();
        if (el.hasClass("disabled")) {
          return;
        }
        // TODO: Figure out nicer / proper way to handle deferred save
        if (this.scope.attr("deferred")) {
          return this.deferedSave();
        }

        var type = this.scope.attr("mapper.type"),
            object = this.scope.attr("mapper.object"),
            isAllObject = type === "AllObject",
            instance = CMS.Models[object].findInCacheById(this.scope.attr("mapper.join_object_id")),
            mapping, Model, data = {}, defer = [],
            que = new RefreshQueue();

        this.scope.attr("mapper.is_saving", true);
        que.enqueue(instance).trigger().done(function (inst) {
          data["context"] = null;
          _.each(this.scope.attr("mapper.selected"), function (desination) {
            var modelInstance;

            mapping = GGRC.Mappings.get_canonical_mapping(object, isAllObject ? desination.type : type);
            Model = CMS.Models[mapping.model_name];
            data[mapping.object_attr] = {
              href: instance.href,
              type: instance.type,
              id: instance.id
            };
            data[mapping.option_attr] = desination;

            modelInstance = new Model(data);
            defer.push(modelInstance.save());
          }, this);

          $.when.apply($, defer)
            .fail(function (response, message) {
              $("body").trigger("ajax:flash", {"error": message});
            }.bind(this))
            .always(function () {
              this.scope.attr("mapper.is_saving", false);
              // TODO: Find proper way to dismiss the modal
              this.element.find(".modal-dismiss").trigger("click");
            }.bind(this));
        }.bind(this));
      },
      "setBinding": function () {
        if (this.scope.attr("mapper.search_only")) {
          return;
        }
        var table_plural = this.scope.attr("mapper.model.table_plural"),
            selected = CMS.Models.get_instance(this.scope.attr("mapper.object"), this.scope.attr("mapper.join_object_id")),
            binding;

        table_plural = (selected.has_binding(table_plural) ? "" : "related_") + table_plural;
        if (!selected.has_binding(table_plural)) {
          return;
        }
        binding = selected.get_binding(table_plural);
        binding.refresh_list().then(function (mappings) {
          can.each(mappings, function (mapping) {
            this.scope.attr("mapper.bindings")[mapping.instance.id] = mapping;
          }, this);
        }.bind(this));
      },
      "setModel": function () {
        var type = this.scope.attr("mapper.type"),
            types = this.scope.attr("mapper.types");

        if (~["All Object", "AllObject"].indexOf(type)) {
          return this.scope.attr("mapper.model", types["all_objects"]);
        }
        this.scope.attr("mapper.model", this.scope.mapper.model_from_type(type));
      },
      "{mapper} type": function () {
        this.scope.attr("mapper.term", "");
        this.scope.attr("mapper.contact", {});
        this.scope.attr("mapper.relevant", []);

        this.setModel();
        this.setBinding();
      },
      "#search-by-owner autocomplete:select": function (el, ev, data) {
        this.scope.attr("mapper.contact", data.item);
      },
      "#search-by-owner keyup": function (el, ev) {
        if (!el.val()) {
          this.scope.attr("mapper.contact", {});
        }
      },
      "allSelected": function () {
        var selected = this.scope.attr("mapper.selected"),
            entries = this.scope.attr("mapper.entries");

        this.scope.attr("mapper.all_selected", selected.length === entries.length);
      },
      "{mapper.entries} length": "allSelected",
      "{mapper.selected} length": "allSelected"
    }
  });
  can.Component.extend({
    tag: "mapper-results",
    template: "<content />",
    scope: {
      "items-per-page": "@",
      page: 0,
      options: new can.List(),
      select_all: false,
      page_loading: false,
      loading_or_saving: can.compute(function () {
        return this.attr("page_loading") || this.attr("mapper.is_saving");
      })
    },
    events: {
      "inserted":  function () {
        this.element.find(".results-wrap").cms_controllers_infinite_scroll();
        this.getResults();
      },
      ".modalSearchButton click": "getResults",
      "{scope} type": "getResults",
      "{scope} entries": "drawPage",
      ".results-wrap scrollNext": "drawPage",
      ".object-check-all change": function (el, ev) {
        var que = new RefreshQueue(),
            entries = this.scope.attr("entries");

        this.scope.attr("select_all", true);
        this.scope.attr("isloading", true);
        que.enqueue(_.pluck(entries, "instance")).trigger().then(function (models) {
          this.scope.attr("isloading", false);
          this.scope.attr("selected", _.map(models, function (model) {
            return {
              id: model.id,
              type: model.type,
              href: model.href
            };
          }));
        }.bind(this));
      },
      ".tree-item .object-check-single change": function (el, ev) {
        if (el.hasClass("disabled")) {
          return;
        }

        var uid = el.closest(".tree-item").data("id"),
            isChecked = el.prop("checked"),
            item = _.find(this.scope.attr("options"), function (option) {
                return option.instance.id === uid;
              }),
            selected = this.scope.attr("selected"),
            needle = {id: item.instance.id},
            index;

        if (!_.findWhere(selected, needle)) {
          selected.push({
            id: item.instance.id,
            type: item.instance.type,
            href: item.instance.href
          });
        } else {
          index = _.findIndex(selected, needle);
          selected.splice(index, 1);
        }
      },
      "drawPage": function () {
        if (this.scope.attr("page_loading")) {
          return;
        }
        var que = new RefreshQueue(),
            page = this.scope.attr("page"),
            next_page = page + 1,
            per_page = +this.scope.attr("items-per-page"),
            page_items = this.scope.attr("entries").slice(page * per_page, next_page * per_page),
            options = this.scope.attr("options");

        if (!page_items.length) {
          return;
        }
        this.scope.attr("page_loading", true);
        que.enqueue(_.pluck(page_items, "instance")).trigger().then(function (models) {
          this.scope.attr("page_loading", false);
          this.scope.attr("page", next_page);
          options.push.apply(options, can.map(models, function (model) {
            if (!model.type) {
              return;
            }
            if (this.scope.attr("mapper.search_only")) {
              return {
                instance: model,
                selected_object: false,
                binding: {},
                mappings: []
              };
            }
            var selected = CMS.Models.get_instance(this.scope.attr("mapper.object"), this.scope.attr("mapper.join_object_id")),
                mapper = this.scope.mapper.model_from_type(model.type),
                binding, bindings = this.scope.attr("mapper.bindings");

            if (bindings[model.id]) {
              return _.extend(bindings[model.id], {
                selected_object: selected
              });
            }
            if (selected.has_binding(mapper.plural.toLowerCase())) {
              binding = selected.get_binding(mapper.plural.toLowerCase());
            }
            return {
              instance: model,
              selected_object: selected,
              binding: binding,
              mappings: []
            };
          }.bind(this)));
        }.bind(this));
      },
      "searchFor": function (data) {
        data.options = data.options || {};
        var join_model = GGRC.Mappings.join_model_name_for(this.scope.attr("mapper.object"), data.model_name);

        if (join_model !== "TaskGroupObject" && data.model_name === "Program") {
          data.options.permission_parms = {
            __permission_type: "create",
            __permission_model: join_model
          };
        }
        data.options.__permission_type = data.options.__permission_type || "read";
        data.model_name = _.isString(data.model_name) ? [data.model_name] : data.model_name;

        return GGRC.Models.Search.search_for_types(data.term || "", data.model_name, data.options);
      },
      "getResults": function () {
        var model_name = this.scope.attr("type"),
            contact = this.scope.attr("contact"),
            permission_parms = {},
            search = [],
            list,
            relevant;

        this.scope.attr("page", 0);
        this.scope.attr("entries", []);
        this.scope.attr("selected", []);
        this.scope.attr("options", []);
        this.scope.attr("select_all", false);
        this.scope.attr("mapper.all_selected", false);

        if (model_name === "AllObject") {
          model_name = this.scope.attr("types.all_objects.models");
        }
        if (!_.isEmpty(contact)) {
          permission_parms.contact_id = contact.id;
        }

        this.scope.attr("page_loading", true);
        relevant = _.map(this.scope.attr("mapper.relevant"), function (relevant) {
          return {
            model_name: relevant.model_name,
            term: relevant.filter.title
          };
        });
        search.push({
          term: this.scope.attr("term"),
          model_name: model_name,
          options: permission_parms
        });
        $.merge(search, relevant);
        search = _.map(search, function (query) {
          return new GGRC.ListLoaders.SearchListLoader(function (binding) {
            return this.searchFor(query).then(function (mappings) {
              return mappings.entries;
            });
          }.bind(this)).attach({});
        }.bind(this));

        list = (search.length > 1) ?
                  new GGRC.ListLoaders.IntersectingListLoader(search).attach()
                : search[0];

        list.refresh_stubs().then(function (options) {
          this.scope.attr("page_loading", false);
          this.scope.attr("entries", options);
        }.bind(this));
      }
    }
  });

  $("body").on("click",
  '[data-toggle="unified-mapper"], \
   [data-toggle="unified-search"]',
  function (ev) {
    ev.preventDefault();
    var btn = $(ev.currentTarget),
        data = {},
        isSearch;

    _.each(btn.data(), function (val, key) {
      data[can.camelCaseToUnderscore(key)] = val;
    });

    if (data.tooltip) {
      data.tooltip.hide();
    }
    isSearch = /unified-search/ig.test(data.toggle);
    GGRC.Controllers.MapperModal.launch($(this), _.extend({
      "object": btn.data("join-object-type"),
      "type": btn.data("join-option-type"),
      "join-object-id": btn.data("join-object-id"),
      "search-only": isSearch
    }, data));
  });
})(window.can, window.can.$);

<commit_msg>Fix typo deferedSave - deferredSave<commit_after>/*!
    Copyright (C) 2015 Google Inc., authors, and contributors <see AUTHORS file>
    Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
    Created By: ivan@reciprocitylabs.com
    Maintained By: ivan@reciprocitylabs.com
*/

(function(can, $) {
  "use strict";
  var MapperModel = can.Map({
      type: "AllObject", // We set default as All Object
      contact: {},
      deferred: "@",
      deferred_to: "@",
      term: "",
      object: "",
      model: {},
      bindings: {},
      is_loading: false,
      is_saving: false,
      all_selected: false,
      search_only: false,
      join_object_id: "",
      selected: new can.List(),
      entries: new can.List(),
      relevant: new can.List(),
      model_from_type: function (type) {
        var types = _.reduce(_.values(this.types()), function (memo, val) {
          if (val.items) {
            return memo.concat(val.items);
          }
          return memo;
        }, []);
        return _.findWhere(types, {value: type});
      },
      types: can.compute(function () {
        var selector_list = GGRC.Mappings.get_canonical_mappings_for(this.object),
            forbidden = ["workflow", "taskgroup", "gdrivefolder", "context"],
            groups = {
              "all_objects": {
                name: "All Objects",
                value: "AllObject",
                plural: "allobjects",
                table_plural: "allobjects",
                singular: "AllObject",
                models: []
              },
              "entities": {
                name: "People/Groups",
                items: []
              },
              "business": {
                name: "Assets/Business",
                items: []
              },
              "governance": {
                name: "Governance",
                items: []
              }
            };

        can.each(selector_list, function (model, model_name) {
          if (!model_name || !CMS.Models[model_name] || ~forbidden.indexOf(model_name.toLowerCase())) {
            return;
          }
          var cms_model = CMS.Models[model_name],
              group = !groups[cms_model.category] ? "governance" : cms_model.category;

          groups[group]["items"].push({
            name: cms_model.title_plural,
            value: cms_model.shortName,
            singular: cms_model.shortName,
            plural: cms_model.title_plural.toLowerCase().replace(/\s+/, "_"),
            table_plural: cms_model.table_plural,
            isSelected: cms_model.shortName === this.type
          });
          groups["all_objects"]["models"].push(cms_model.shortName);
        }, this);
        return groups;
      })
    });

  can.Component.extend({
    tag: "modal-mapper",
    template: can.view(GGRC.mustache_path + "/modals/mapper/base.mustache"),
    scope: function (attrs, parentScope, el) {
      var $el = $(el),
          data = {},
          id = +$el.attr("join-object-id"),
          object = $el.attr("object"),
          type = $el.attr("type"),
          tree_view = GGRC.tree_view.sub_tree_for[object];

      if ($el.attr("search-only")) {
        data["search_only"] =  /true/i.test($el.attr("search-only"));
      }
      if (object) {
        data["object"] = object;
      }
      if (!data["search_only"]) {
        if (id === GGRC.page_instance().id || !tree_view) {
          data["type"] = GGRC.Models[type] ? type : "AllObject";
        } else {
          data["type"] = tree_view.display_list[0];
        }
      }
      data["join_object_id"] = id || GGRC.page_instance().id;
      return {
        mapper: new MapperModel(data)
      };
    },
    events: {
      "inserted": function () {
        this.setModel();
        this.setBinding();
      },
      "deferredSave": function () {
        var data = {
              multi_map: true,
              arr: _.map(this.scope.attr("mapper.selected"), function (desination) {
                    var inst = _.find(this.scope.attr("mapper.entries"), function (entry) {
                      return entry.instance.id === desination.id;
                    });
                    if (inst) {
                      return inst.instance;
                    }
                  }.bind(this))
            };
        this.scope.attr("deferred_to").controller.element.trigger("defer:add", [data, {map_and_save: true}]);
        // TODO: Find proper way to dismiss the modal
        this.element.find(".modal-dismiss").trigger("click");
      },
      ".modal-footer .btn-map click": function (el, ev) {
        ev.preventDefault();
        if (el.hasClass("disabled")) {
          return;
        }
        // TODO: Figure out nicer / proper way to handle deferred save
        if (this.scope.attr("deferred")) {
          return this.deferredSave();
        }

        var type = this.scope.attr("mapper.type"),
            object = this.scope.attr("mapper.object"),
            isAllObject = type === "AllObject",
            instance = CMS.Models[object].findInCacheById(this.scope.attr("mapper.join_object_id")),
            mapping, Model, data = {}, defer = [],
            que = new RefreshQueue();

        this.scope.attr("mapper.is_saving", true);
        que.enqueue(instance).trigger().done(function (inst) {
          data["context"] = null;
          _.each(this.scope.attr("mapper.selected"), function (desination) {
            var modelInstance;

            mapping = GGRC.Mappings.get_canonical_mapping(object, isAllObject ? desination.type : type);
            Model = CMS.Models[mapping.model_name];
            data[mapping.object_attr] = {
              href: instance.href,
              type: instance.type,
              id: instance.id
            };
            data[mapping.option_attr] = desination;

            modelInstance = new Model(data);
            defer.push(modelInstance.save());
          }, this);

          $.when.apply($, defer)
            .fail(function (response, message) {
              $("body").trigger("ajax:flash", {"error": message});
            }.bind(this))
            .always(function () {
              this.scope.attr("mapper.is_saving", false);
              // TODO: Find proper way to dismiss the modal
              this.element.find(".modal-dismiss").trigger("click");
            }.bind(this));
        }.bind(this));
      },
      "setBinding": function () {
        if (this.scope.attr("mapper.search_only")) {
          return;
        }
        var table_plural = this.scope.attr("mapper.model.table_plural"),
            selected = CMS.Models.get_instance(this.scope.attr("mapper.object"), this.scope.attr("mapper.join_object_id")),
            binding;

        table_plural = (selected.has_binding(table_plural) ? "" : "related_") + table_plural;
        if (!selected.has_binding(table_plural)) {
          return;
        }
        binding = selected.get_binding(table_plural);
        binding.refresh_list().then(function (mappings) {
          can.each(mappings, function (mapping) {
            this.scope.attr("mapper.bindings")[mapping.instance.id] = mapping;
          }, this);
        }.bind(this));
      },
      "setModel": function () {
        var type = this.scope.attr("mapper.type"),
            types = this.scope.attr("mapper.types");

        if (~["All Object", "AllObject"].indexOf(type)) {
          return this.scope.attr("mapper.model", types["all_objects"]);
        }
        this.scope.attr("mapper.model", this.scope.mapper.model_from_type(type));
      },
      "{mapper} type": function () {
        this.scope.attr("mapper.term", "");
        this.scope.attr("mapper.contact", {});
        this.scope.attr("mapper.relevant", []);

        this.setModel();
        this.setBinding();
      },
      "#search-by-owner autocomplete:select": function (el, ev, data) {
        this.scope.attr("mapper.contact", data.item);
      },
      "#search-by-owner keyup": function (el, ev) {
        if (!el.val()) {
          this.scope.attr("mapper.contact", {});
        }
      },
      "allSelected": function () {
        var selected = this.scope.attr("mapper.selected"),
            entries = this.scope.attr("mapper.entries");

        this.scope.attr("mapper.all_selected", selected.length === entries.length);
      },
      "{mapper.entries} length": "allSelected",
      "{mapper.selected} length": "allSelected"
    }
  });
  can.Component.extend({
    tag: "mapper-results",
    template: "<content />",
    scope: {
      "items-per-page": "@",
      page: 0,
      options: new can.List(),
      select_all: false,
      page_loading: false,
      loading_or_saving: can.compute(function () {
        return this.attr("page_loading") || this.attr("mapper.is_saving");
      })
    },
    events: {
      "inserted":  function () {
        this.element.find(".results-wrap").cms_controllers_infinite_scroll();
        this.getResults();
      },
      ".modalSearchButton click": "getResults",
      "{scope} type": "getResults",
      "{scope} entries": "drawPage",
      ".results-wrap scrollNext": "drawPage",
      ".object-check-all change": function (el, ev) {
        var que = new RefreshQueue(),
            entries = this.scope.attr("entries");

        this.scope.attr("select_all", true);
        this.scope.attr("isloading", true);
        que.enqueue(_.pluck(entries, "instance")).trigger().then(function (models) {
          this.scope.attr("isloading", false);
          this.scope.attr("selected", _.map(models, function (model) {
            return {
              id: model.id,
              type: model.type,
              href: model.href
            };
          }));
        }.bind(this));
      },
      ".tree-item .object-check-single change": function (el, ev) {
        if (el.hasClass("disabled")) {
          return;
        }

        var uid = el.closest(".tree-item").data("id"),
            isChecked = el.prop("checked"),
            item = _.find(this.scope.attr("options"), function (option) {
                return option.instance.id === uid;
              }),
            selected = this.scope.attr("selected"),
            needle = {id: item.instance.id},
            index;

        if (!_.findWhere(selected, needle)) {
          selected.push({
            id: item.instance.id,
            type: item.instance.type,
            href: item.instance.href
          });
        } else {
          index = _.findIndex(selected, needle);
          selected.splice(index, 1);
        }
      },
      "drawPage": function () {
        if (this.scope.attr("page_loading")) {
          return;
        }
        var que = new RefreshQueue(),
            page = this.scope.attr("page"),
            next_page = page + 1,
            per_page = +this.scope.attr("items-per-page"),
            page_items = this.scope.attr("entries").slice(page * per_page, next_page * per_page),
            options = this.scope.attr("options");

        if (!page_items.length) {
          return;
        }
        this.scope.attr("page_loading", true);
        que.enqueue(_.pluck(page_items, "instance")).trigger().then(function (models) {
          this.scope.attr("page_loading", false);
          this.scope.attr("page", next_page);
          options.push.apply(options, can.map(models, function (model) {
            if (!model.type) {
              return;
            }
            if (this.scope.attr("mapper.search_only")) {
              return {
                instance: model,
                selected_object: false,
                binding: {},
                mappings: []
              };
            }
            var selected = CMS.Models.get_instance(this.scope.attr("mapper.object"), this.scope.attr("mapper.join_object_id")),
                mapper = this.scope.mapper.model_from_type(model.type),
                binding, bindings = this.scope.attr("mapper.bindings");

            if (bindings[model.id]) {
              return _.extend(bindings[model.id], {
                selected_object: selected
              });
            }
            if (selected.has_binding(mapper.plural.toLowerCase())) {
              binding = selected.get_binding(mapper.plural.toLowerCase());
            }
            return {
              instance: model,
              selected_object: selected,
              binding: binding,
              mappings: []
            };
          }.bind(this)));
        }.bind(this));
      },
      "searchFor": function (data) {
        data.options = data.options || {};
        var join_model = GGRC.Mappings.join_model_name_for(this.scope.attr("mapper.object"), data.model_name);

        if (join_model !== "TaskGroupObject" && data.model_name === "Program") {
          data.options.permission_parms = {
            __permission_type: "create",
            __permission_model: join_model
          };
        }
        data.options.__permission_type = data.options.__permission_type || "read";
        data.model_name = _.isString(data.model_name) ? [data.model_name] : data.model_name;

        return GGRC.Models.Search.search_for_types(data.term || "", data.model_name, data.options);
      },
      "getResults": function () {
        var model_name = this.scope.attr("type"),
            contact = this.scope.attr("contact"),
            permission_parms = {},
            search = [],
            list,
            relevant;

        this.scope.attr("page", 0);
        this.scope.attr("entries", []);
        this.scope.attr("selected", []);
        this.scope.attr("options", []);
        this.scope.attr("select_all", false);
        this.scope.attr("mapper.all_selected", false);

        if (model_name === "AllObject") {
          model_name = this.scope.attr("types.all_objects.models");
        }
        if (!_.isEmpty(contact)) {
          permission_parms.contact_id = contact.id;
        }

        this.scope.attr("page_loading", true);
        relevant = _.map(this.scope.attr("mapper.relevant"), function (relevant) {
          return {
            model_name: relevant.model_name,
            term: relevant.filter.title
          };
        });
        search.push({
          term: this.scope.attr("term"),
          model_name: model_name,
          options: permission_parms
        });
        $.merge(search, relevant);
        search = _.map(search, function (query) {
          return new GGRC.ListLoaders.SearchListLoader(function (binding) {
            return this.searchFor(query).then(function (mappings) {
              return mappings.entries;
            });
          }.bind(this)).attach({});
        }.bind(this));

        list = (search.length > 1) ?
                  new GGRC.ListLoaders.IntersectingListLoader(search).attach()
                : search[0];

        list.refresh_stubs().then(function (options) {
          this.scope.attr("page_loading", false);
          this.scope.attr("entries", options);
        }.bind(this));
      }
    }
  });

  $("body").on("click",
  '[data-toggle="unified-mapper"], \
   [data-toggle="unified-search"]',
  function (ev) {
    ev.preventDefault();
    var btn = $(ev.currentTarget),
        data = {},
        isSearch;

    _.each(btn.data(), function (val, key) {
      data[can.camelCaseToUnderscore(key)] = val;
    });

    if (data.tooltip) {
      data.tooltip.hide();
    }
    isSearch = /unified-search/ig.test(data.toggle);
    GGRC.Controllers.MapperModal.launch($(this), _.extend({
      "object": btn.data("join-object-type"),
      "type": btn.data("join-option-type"),
      "join-object-id": btn.data("join-object-id"),
      "search-only": isSearch
    }, data));
  });
})(window.can, window.can.$);

<|endoftext|>


<commit_before>'use strict';

var SfChangePasswordPage = function() {
	this.get = function() {
		browser.get('http://jamaicanpsalms.scriptureforge.local/app/changepassword');
	};
	// Do the
	this.passwordForm = element('form#passwordForm');
	this.passwordInput = element(by.model('vars.password'));
	this.confirmInput = element(by.model('vars.confirm_password'));
	this.signupButton = element(by.tagName('button')); // Might need to change this...?
};

describe('E2E testing: Change password', function() {
	var sfChangePasswordPage = new SfChangePasswordPage();

	beforeEach(function() {
		sfChangePasswordPage.get();
	});

	it('contains a password form', function() {
		expect(sfChangePasswordPage.passwordForm).toBeDefined();
	});

	it('refuses to allow form submission if the confirm input does not match', function() {
		sfChangePasswordPage.passwordInput.sendKeys('abc123').then(function() {
			sfChangePasswordPage.confirmInput.sendKeys('abcd1234').then(function() {
				expect(sfChangePasswordPage.signupButton.isDisabled()).toBeTruthy();
			});
		});
	});

});
<commit_msg>WIP: E2E test for changing password<commit_after>'use strict';

var SfChangePasswordPage = function() {
	this.get = function() {
		browser.get('http://jamaicanpsalms.scriptureforge.local/app/changepassword');
	};
	// Do the
	this.passwordForm = element('form#passwordForm');
	this.passwordInput = element(by.model('vars.password'));
	this.confirmInput = element(by.model('vars.confirm_password'));
	this.signupButton = element(by.tagName('button')); // Might need to change this...?
};

describe('E2E testing: Change password', function() {
	var sfChangePasswordPage = new SfChangePasswordPage();

	beforeEach(function() {
		sfChangePasswordPage.get();
	});

	it('contains a password form', function() {
		expect(sfChangePasswordPage.passwordForm).toBeDefined();
	});

	it('refuses to allow form submission if the confirm input does not match', function() {
		sfChangePasswordPage.passwordInput.sendKeys('abc123').then(function() {
			sfChangePasswordPage.confirmInput.sendKeys('abcd1234').then(function() {
				expect(sfChangePasswordPage.signupButton.isEnabled()).toBeFalsy();
			});
		});
	});

});
<|endoftext|>


<commit_before>var ParsonsWidget = function(options) {
    this.modified_lines = [];
    this.extra_lines = [];
    this.model_solution = [];

    //To collect statistics, feedback should not be based on this
    this.user_actions = [];

    var defaults = {
            'incorrectSound': false,
            'x_indent': 50,
            'feedback_cb': false,
            'first_error_only': true,
            'max_wrong_lines': 10,
            'trash_label': 'Drag from here',
            'solution_label': 'Construct your solution here'
    };

    this.options = jQuery.extend({}, defaults, options);
    this.feedback_exists = false;
    this.FEEDBACK_STYLES = { 'correctPosition' : 'correctPosition',
            'incorrectPosition' : 'incorrectPosition',
            'correctIndent' : 'correctIndent',
            'incorrectIndent' : 'incorrectIndent'};
};

//Public methods

ParsonsWidget.prototype.parseLine = function(spacePrefixedLine) {
    return {
	code: spacePrefixedLine.trim().replace(/\\n/,"\n"),
	indent: spacePrefixedLine.length - spacePrefixedLine.replace(/^\s+/,"").length
    };
}

ParsonsWidget.prototype.parseCode = function(lines, max_distractors) {
    var distractors = [],
    indented = [],
    widgetData = [],
    lineObject,
    errors = [],
    that = this;
    $.each(lines, function(index, item) {
        if (item.search(/#distractor\s*$/) >= 0) {
	    lineObject = {
		code: item.replace(/#distractor\s*$/,"").trim().replace(/\\n/,"\n"),
		indent: -1,
		distractor: true,
		orig: index
	    };
	    if (lineObject.code.length > 0) {
		distractors.push(lineObject);
	    }
	} else {
	    lineObject = that.parseLine(item);
	    if (lineObject.code.length > 0) {
		lineObject.distractor = false;
		lineObject.orig = index;
		indented.push(lineObject);
	    }
	}
	});

    // Normalize indents and make sure indentation is valid
    var normalized = this.normalizeIndents(indented);

    $.each(normalized, function(index, item) {
        if (item.indent < 0) {
	    errors.push("Line " + normalized.orig + " is not correctly indented. No matching indentation.");
	}
	widgetData.push(item);
        });

    // Remove extra distractors
    permutation = this.getRandomPermutation(distractors.length)
    var selected_distractors = []
    for (var i = 0; i < max_distractors; i++) {
	selected_distractors.push(distractors[permutation[i]]);
    }

    return {
	solution:  $.extend(true, [], normalized),
        distractors: $.extend(true, [], selected_distractors),
        widgetInitial: $.extend(true, [], widgetData),
        errors: errors};
};

ParsonsWidget.prototype.init = function(text) {
    var initial_structures = this.parseCode(text.split("\n"), this.options.max_wrong_lines);
    this.model_solution = initial_structures.solution;
    this.extra_lines = initial_structures.distractors;
    this.modified_lines = initial_structures.widgetInitial;

    $.each(this.modified_lines, function(index, item) {
	    item.id = 'codeline' + index;
	    item.indent = 0;
        });

};

ParsonsWidget.prototype.addLogEntry = function(entry, extend) {
    var logData = {};
    if (entry && !extend) {
        this.user_actions.push(entry);
    } else {
        if (this.options.trashId) {
            logData = {
                time: new Date(),
                output: jQuery.extend(true, [], this.getModifiedCode("#ul-" + this.options.sortableId)),
                input: jQuery.extend(true, [], this.getModifiedCode("#ul-" + this.options.trashId)),
                type: "action"
            };
        } else {
            logData = {
                time: new Date(),
                output: jQuery.extend(true, [], this.getModifiedCode("#ul-" + this.options.sortableId)),
                type: "action"
            };
        }
        if (entry && extend) {
          jQuery.extend(logData, entry);
        }
        this.user_actions.push(logData);
    }
};

/**
 * Update indentation of a line based on new coordinates
 * leftDiff horizontal difference from (before and after drag) in px
 ***/
ParsonsWidget.prototype.updateIndent = function(leftDiff, id) {
    var code_line = this.getLineById(id);
    var new_indent = code_line.indent + Math.floor(leftDiff / this.options.x_indent);
    new_indent = Math.max(0, new_indent);
    code_line.indent = new_indent;
    return new_indent;
};

/**
 *
 * @param id
 * @return
 */
ParsonsWidget.prototype.getLineById = function(id) {
    var index = -1;
    for (var i = 0; i < this.modified_lines.length; i++) {
        if (this.modified_lines[i].id == id) {
            index = i;
            break;
        }
    }
    return this.modified_lines[index];
};

/** Does not use the current object - only the argument */
ParsonsWidget.prototype.normalizeIndents = function(lines) {

    var normalized = [];
    var new_line;
    var match_indent = function(index) {
        //return line index from the previous lines with matching indentation
        for (var i = index-1; i >= 0; i--) {
            if (lines[i].indent == lines[index].indent) {
                return normalized[i].indent;
            }
        }
        return -1;
    };
    for ( var i = 0; i < lines.length; i++ ) {
        //create shallow copy from the line object
        new_line = jQuery.extend({}, lines[i]);
        if (i === 0) {
            new_line.indent = 0;
            if (lines[i].indent !== 0) {
              new_line.indent = -1;
            }
        } else if (lines[i].indent == lines[i-1].indent) {
            new_line.indent = normalized[i-1].indent;
        } else if (lines[i].indent > lines[i-1].indent) {
            new_line.indent = normalized[i-1].indent + 1;
        } else {
            // indentation can be -1 if no matching indentation exists, i.e. IndentationError in Python
            new_line.indent = match_indent(i);
        }
        normalized[i] = new_line;
    }
    return normalized;
};

/**
 * Retrieve the code lines based on what is in the DOM
 *
 * TODO(petri) refactor to UI
 * */
ParsonsWidget.prototype.getModifiedCode = function(search_string) {
    //ids of the the modified code
    var users_code_ids = $(search_string).sortable('toArray');
    var lines_to_return = [];
    for ( var i = 0; i < users_code_ids.length; i++ ) {
        lines_to_return[i] = this.getLineById(users_code_ids[i]);
    }
    return lines_to_return;
};

/**
 * TODO(petri) refoctor to UI
 */
ParsonsWidget.prototype.displayError = function(message) {
    if (this.options.incorrectSound && $.sound) {
        $.sound.play(this.options.incorrectSound);
    }
    alert(message);
};

/**
 * @return
 * TODO(petri): Separate UI from here
 */
ParsonsWidget.prototype.getFeedback = function() {
    this.feedback_exists = true;
    var student_code = this.normalizeIndents(this.getModifiedCode("#ul-" + this.options.sortableId));
    var lines_to_check = Math.min(student_code.length, this.model_solution.length);
    var errors = [], log_errors = [];

    var lines = _.map(student_code, function(line) {
      return parseInt(line.id.substring(8), 10);
    });
    var inv = LIS.best_lise_inverse(lines);
    var incorrectLines = [];
    _.each(inv, function(itemId) {
      $("#codeline" + itemId).addClass("incorrectPosition");
      incorrectLines.push(itemId);
    });
    if (inv.length > 0) {
      errors.push("Some lines in incorrect position relative to others");
      log_errors.push({type: "incorrectPosition", lines: incorrectLines});
    }

    // Always show this feedback
    if (this.model_solution.length < student_code.length) {
        $("#ul-" + this.options.sortableId).addClass("incorrect");
        errors.push("Too many lines in your solution");
        log_errors.push({type: "tooManyLines", lines: student_code.length});
    } else if (this.model_solution.length > student_code.length){
        $("#ul-" + this.options.sortableId).addClass("incorrect");
        errors.push("Too few lines in your solution");
        log_errors.push({type: "tooFewLines", lines: student_code.length});
    }

    if (errors.length == 0) { // check indent if no other errors
      for (var i = 0; i < lines_to_check; i++) {
        var code_line = student_code[i];
        var model_line = this.model_solution[i];
        // need to uncomment following if distractors are added back!!
        /*if (code_line.code !== model_line.code &&
                ((!this.options.first_error_only) || errors.length == 0)) {
            $("#" + code_line.id).addClass("incorrectPosition");
            errors.push("line " + (i+1) + " is not correct!");
            log_errors.push({type: "incorrectPosition", line: (i+1)});
        }*/
        if (code_line.indent !== model_line.indent &&
                ((!this.options.first_error_only) || errors.length == 0)) {
            $("#" + code_line.id).addClass("incorrectIndent");
            errors.push("line " + (i+1) + " is not indented correctly");
            log_errors.push({type: "incorrectIndent", line: (i+1)});
        }
        if (code_line.code == model_line.code &&
            code_line.indent == model_line.indent &&
            errors.length == 0) {
            $("#" + code_line.id).addClass("correctPosition");
        }
      }
    }

    if (errors.length == 0) {
        if (this.options.correctSound && $.sound) {
            $.sound.play(this.options.correctSound);
        }
        $("#ul-" + this.options.sortableId).addClass("correct");
    }

    if (this.options.feedback_cb) {
        feedback_cb(); //TODO(petri): what is needed?
    }
    this.addLogEntry({type: "feedback", errors: log_errors}, true);
    //alert("ok");
    return errors;
};

ParsonsWidget.prototype.clearFeedback = function() {
    if (this.feedback_exists) {
        $("#ul-" + this.options.sortableId).removeClass("incorrect correct");
        var li_elements = $("#ul-" + this.options.sortableId + " li");
        $.each(this.FEEDBACK_STYLES, function(index, value) {
            li_elements.removeClass(value);
        });
    }
    this.feedback_exists = false;
};

ParsonsWidget.prototype.getRandomPermutation = function(n) {
    var permutation = [];
    var i;
    for (i = 0; i < n; i++) {
        permutation.push(i);
    }
    var swap1, swap2, tmp;
    for (i = 0; i < n; i++) {
        swap1 = Math.floor(Math.random() * n);
        swap2 = Math.floor(Math.random() * n);
        tmp = permutation[swap1];
        permutation[swap1] = permutation[swap2];
        permutation[swap2] = tmp;
    }
    return permutation;
};

ParsonsWidget.prototype.getFixedPermutation = function(n) {
    var permutation = [];
    var i;
    for (i = 0; i < n; i++) {
        permutation.push(i);
    }

    return permutation;
};

ParsonsWidget.prototype.shuffleLines = function() {
    this.createHtml(this.getRandomPermutation);
};

/** modifies the DOM by inserting exercise elements into it */
ParsonsWidget.prototype.createHtml = function(randomizeCallback) {
// TODO(petri): needs more refactoring
        var codelines = [];
        var initial_state = []; //used only for logging

        var that = this;
        for (var i=0; i<this.modified_lines.length; i++) {
            codelines.push('
' + this.modified_lines[i].code + '<\/li>');
        }

        //randomize is a permutation array, i.e. array with index values where [1, 2, ..., n] implies nothing is permutated
        if (randomizeCallback) {
            var permutation = randomizeCallback(codelines.length);
            var randomized_lines = [];
            for (i = 0; i < codelines.length; i++) {
                randomized_lines[i] = codelines[permutation[i]];
                initial_state[i] = this.modified_lines[permutation[i]];
            }
            codelines = randomized_lines;
        } else {
          initial_state = this.modified_lines;
        }

        this.addLogEntry({type: 'init', time: new Date(), initial: initial_state});

        if (this.options.trashId) {
            $("#" + this.options.trashId).html('
'+this.options.trash_label+'

'+codelines.join('')+'
');
            $("#" + this.options.sortableId).html('
'+this.options.solution_label+'

');
        } else {
            var d = $("#" + this.options.sortableId);
            var h = '
'+codelines.join('')+'
';
            $("#" + this.options.sortableId).html('
'+codelines.join('')+'
');
        }
        if (window.prettyPrint && (typeof(this.options.prettyPrint) === "undefined" || this.options.prettyPrint)) {
            prettyPrint(); //NOT IMPLEMENTET YET?
        }
        var sortable = $("#ul-" + this.options.sortableId).sortable({
            start : function() { that.clearFeedback(); },
            stop : function(event, ui) {
                if ($(event.target)[0] != ui.item.parent()[0]) {
                    return;
                }
                var ind = that.updateIndent(ui.position.left - ui.item.parent().offset().left,
                                        ui.item[0].id);
                ui.item.css("margin-left", that.options.x_indent * ind + "px");
                that.addLogEntry({type: "moveOutput", target: ui.item[0].id}, true);
            },
            receive : function(event, ui) {
                var ind = that.updateIndent(ui.position.left - ui.item.parent().offset().left,
                                        ui.item[0].id);
                ui.item.css("margin-left", that.options.x_indent * ind + "px");
                that.addLogEntry({type: "addOutput", target: ui.item[0].id}, true);
            },
            grid : [that.options.x_indent, 1 ]
        });
        if (this.options.trashId) {
            var trash = $("#ul-" + this.options.trashId).sortable({
                connectWith: sortable,
                start: function() { that.clearFeedback(); },
                receive: function(event, ui) {
                    that.getLineById(ui.item[0].id).indent = 0;
                    ui.item.css("margin-left", "0");
                    that.addLogEntry({type: "removeOutput", target: ui.item[0].id}, true);
                },
                stop: function(event, ui) {
                    if ($(event.target)[0] != ui.item.parent()[0]) {
                        // line moved to output and logged there
                        return;
                    }
                    that.addLogEntry({type: "moveInput", target: ui.item[0].id}, true);
                }
            });
            sortable.sortable('option', 'connectWith', trash);
        }
    };

<commit_msg>fixed a failing test and indentations<commit_after>var ParsonsWidget = function(options) {
    this.modified_lines = [];
    this.extra_lines = [];
    this.model_solution = [];

    //To collect statistics, feedback should not be based on this
    this.user_actions = [];

    var defaults = {
            'incorrectSound': false,
            'x_indent': 50,
            'feedback_cb': false,
            'first_error_only': true,
            'max_wrong_lines': 10,
            'trash_label': 'Drag from here',
            'solution_label': 'Construct your solution here'
    };

    this.options = jQuery.extend({}, defaults, options);
    this.feedback_exists = false;
    this.FEEDBACK_STYLES = { 'correctPosition' : 'correctPosition',
            'incorrectPosition' : 'incorrectPosition',
            'correctIndent' : 'correctIndent',
            'incorrectIndent' : 'incorrectIndent'};
};

//Public methods

ParsonsWidget.prototype.parseLine = function(spacePrefixedLine) {
    return {
	code: spacePrefixedLine.trim().replace(/\\n/,"\n"),
	indent: spacePrefixedLine.length - spacePrefixedLine.replace(/^\s+/,"").length
    };
}

ParsonsWidget.prototype.parseCode = function(lines, max_distractors) {
    var distractors = [],
    indented = [],
    widgetData = [],
    lineObject,
    errors = [],
    that = this;
    $.each(lines, function(index, item) {
	    if (item.search(/#distractor\s*$/) >= 0) {
		lineObject = {
		    code: item.replace(/#distractor\s*$/,"").trim().replace(/\\n/,"\n"),
		    indent: -1,
		    distractor: true,
		    orig: index
		};
		if (lineObject.code.length > 0) {
		    distractors.push(lineObject);
		}
	    } else {
		lineObject = that.parseLine(item);
		if (lineObject.code.length > 0) {
		    lineObject.distractor = false;
		    lineObject.orig = index;
		    indented.push(lineObject);
		}
	    }
	});

    // Normalize indents and make sure indentation is valid
    var normalized = this.normalizeIndents(indented);

    $.each(normalized, function(index, item) {
	    if (item.indent < 0) {
		errors.push("Line " + normalized.orig + " is not correctly indented. No matching indentation.");
	    }
	    widgetData.push(item);
        });

    // Remove extra distractors
    permutation = this.getRandomPermutation(distractors.length)
    var selected_distractors = []
    for (var i = 0; i < max_distractors; i++) {
	selected_distractors.push(distractors[permutation[i]]);
	widgetData.push(distractors[permutation[i]]);
    }

    return {
	solution:  $.extend(true, [], normalized),
        distractors: $.extend(true, [], selected_distractors),
        widgetInitial: $.extend(true, [], widgetData),
        errors: errors};
};

ParsonsWidget.prototype.init = function(text) {
    var initial_structures = this.parseCode(text.split("\n"), this.options.max_wrong_lines);
    this.model_solution = initial_structures.solution;
    this.extra_lines = initial_structures.distractors;
    this.modified_lines = initial_structures.widgetInitial;

    $.each(this.modified_lines, function(index, item) {
	    item.id = 'codeline' + index;
	    item.indent = 0;
        });

};

ParsonsWidget.prototype.addLogEntry = function(entry, extend) {
    var logData = {};
    if (entry && !extend) {
        this.user_actions.push(entry);
    } else {
        if (this.options.trashId) {
            logData = {
                time: new Date(),
                output: jQuery.extend(true, [], this.getModifiedCode("#ul-" + this.options.sortableId)),
                input: jQuery.extend(true, [], this.getModifiedCode("#ul-" + this.options.trashId)),
                type: "action"
            };
        } else {
            logData = {
                time: new Date(),
                output: jQuery.extend(true, [], this.getModifiedCode("#ul-" + this.options.sortableId)),
                type: "action"
            };
        }
        if (entry && extend) {
          jQuery.extend(logData, entry);
        }
        this.user_actions.push(logData);
    }
};

/**
 * Update indentation of a line based on new coordinates
 * leftDiff horizontal difference from (before and after drag) in px
 ***/
ParsonsWidget.prototype.updateIndent = function(leftDiff, id) {
    var code_line = this.getLineById(id);
    var new_indent = code_line.indent + Math.floor(leftDiff / this.options.x_indent);
    new_indent = Math.max(0, new_indent);
    code_line.indent = new_indent;
    return new_indent;
};

/**
 *
 * @param id
 * @return
 */
ParsonsWidget.prototype.getLineById = function(id) {
    var index = -1;
    for (var i = 0; i < this.modified_lines.length; i++) {
        if (this.modified_lines[i].id == id) {
            index = i;
            break;
        }
    }
    return this.modified_lines[index];
};

/** Does not use the current object - only the argument */
ParsonsWidget.prototype.normalizeIndents = function(lines) {

    var normalized = [];
    var new_line;
    var match_indent = function(index) {
        //return line index from the previous lines with matching indentation
        for (var i = index-1; i >= 0; i--) {
            if (lines[i].indent == lines[index].indent) {
                return normalized[i].indent;
            }
        }
        return -1;
    };
    for ( var i = 0; i < lines.length; i++ ) {
        //create shallow copy from the line object
        new_line = jQuery.extend({}, lines[i]);
        if (i === 0) {
            new_line.indent = 0;
            if (lines[i].indent !== 0) {
              new_line.indent = -1;
            }
        } else if (lines[i].indent == lines[i-1].indent) {
            new_line.indent = normalized[i-1].indent;
        } else if (lines[i].indent > lines[i-1].indent) {
            new_line.indent = normalized[i-1].indent + 1;
        } else {
            // indentation can be -1 if no matching indentation exists, i.e. IndentationError in Python
            new_line.indent = match_indent(i);
        }
        normalized[i] = new_line;
    }
    return normalized;
};

/**
 * Retrieve the code lines based on what is in the DOM
 *
 * TODO(petri) refactor to UI
 * */
ParsonsWidget.prototype.getModifiedCode = function(search_string) {
    //ids of the the modified code
    var users_code_ids = $(search_string).sortable('toArray');
    var lines_to_return = [];
    for ( var i = 0; i < users_code_ids.length; i++ ) {
        lines_to_return[i] = this.getLineById(users_code_ids[i]);
    }
    return lines_to_return;
};

/**
 * TODO(petri) refoctor to UI
 */
ParsonsWidget.prototype.displayError = function(message) {
    if (this.options.incorrectSound && $.sound) {
        $.sound.play(this.options.incorrectSound);
    }
    alert(message);
};

/**
 * @return
 * TODO(petri): Separate UI from here
 */
ParsonsWidget.prototype.getFeedback = function() {
    this.feedback_exists = true;
    var student_code = this.normalizeIndents(this.getModifiedCode("#ul-" + this.options.sortableId));
    var lines_to_check = Math.min(student_code.length, this.model_solution.length);
    var errors = [], log_errors = [];

    var lines = _.map(student_code, function(line) {
      return parseInt(line.id.substring(8), 10);
    });
    var inv = LIS.best_lise_inverse(lines);
    var incorrectLines = [];
    _.each(inv, function(itemId) {
      $("#codeline" + itemId).addClass("incorrectPosition");
      incorrectLines.push(itemId);
    });
    if (inv.length > 0) {
      errors.push("Some lines in incorrect position relative to others");
      log_errors.push({type: "incorrectPosition", lines: incorrectLines});
    }

    // Always show this feedback
    if (this.model_solution.length < student_code.length) {
        $("#ul-" + this.options.sortableId).addClass("incorrect");
        errors.push("Too many lines in your solution");
        log_errors.push({type: "tooManyLines", lines: student_code.length});
    } else if (this.model_solution.length > student_code.length){
        $("#ul-" + this.options.sortableId).addClass("incorrect");
        errors.push("Too few lines in your solution");
        log_errors.push({type: "tooFewLines", lines: student_code.length});
    }

    if (errors.length == 0) { // check indent if no other errors
      for (var i = 0; i < lines_to_check; i++) {
        var code_line = student_code[i];
        var model_line = this.model_solution[i];
        // need to uncomment following if distractors are added back!!
        /*if (code_line.code !== model_line.code &&
                ((!this.options.first_error_only) || errors.length == 0)) {
            $("#" + code_line.id).addClass("incorrectPosition");
            errors.push("line " + (i+1) + " is not correct!");
            log_errors.push({type: "incorrectPosition", line: (i+1)});
        }*/
        if (code_line.indent !== model_line.indent &&
                ((!this.options.first_error_only) || errors.length == 0)) {
            $("#" + code_line.id).addClass("incorrectIndent");
            errors.push("line " + (i+1) + " is not indented correctly");
            log_errors.push({type: "incorrectIndent", line: (i+1)});
        }
        if (code_line.code == model_line.code &&
            code_line.indent == model_line.indent &&
            errors.length == 0) {
            $("#" + code_line.id).addClass("correctPosition");
        }
      }
    }

    if (errors.length == 0) {
        if (this.options.correctSound && $.sound) {
            $.sound.play(this.options.correctSound);
        }
        $("#ul-" + this.options.sortableId).addClass("correct");
    }

    if (this.options.feedback_cb) {
        feedback_cb(); //TODO(petri): what is needed?
    }
    this.addLogEntry({type: "feedback", errors: log_errors}, true);
    //alert("ok");
    return errors;
};

ParsonsWidget.prototype.clearFeedback = function() {
    if (this.feedback_exists) {
        $("#ul-" + this.options.sortableId).removeClass("incorrect correct");
        var li_elements = $("#ul-" + this.options.sortableId + " li");
        $.each(this.FEEDBACK_STYLES, function(index, value) {
            li_elements.removeClass(value);
        });
    }
    this.feedback_exists = false;
};

ParsonsWidget.prototype.getRandomPermutation = function(n) {
    var permutation = [];
    var i;
    for (i = 0; i < n; i++) {
        permutation.push(i);
    }
    var swap1, swap2, tmp;
    for (i = 0; i < n; i++) {
        swap1 = Math.floor(Math.random() * n);
        swap2 = Math.floor(Math.random() * n);
        tmp = permutation[swap1];
        permutation[swap1] = permutation[swap2];
        permutation[swap2] = tmp;
    }
    return permutation;
};

ParsonsWidget.prototype.getFixedPermutation = function(n) {
    var permutation = [];
    var i;
    for (i = 0; i < n; i++) {
        permutation.push(i);
    }

    return permutation;
};

ParsonsWidget.prototype.shuffleLines = function() {
    this.createHtml(this.getRandomPermutation);
};

/** modifies the DOM by inserting exercise elements into it */
ParsonsWidget.prototype.createHtml = function(randomizeCallback) {
// TODO(petri): needs more refactoring
        var codelines = [];
        var initial_state = []; //used only for logging

        var that = this;
        for (var i=0; i<this.modified_lines.length; i++) {
            codelines.push('
' + this.modified_lines[i].code + '<\/li>');
        }

        //randomize is a permutation array, i.e. array with index values where [1, 2, ..., n] implies nothing is permutated
        if (randomizeCallback) {
            var permutation = randomizeCallback(codelines.length);
            var randomized_lines = [];
            for (i = 0; i < codelines.length; i++) {
                randomized_lines[i] = codelines[permutation[i]];
                initial_state[i] = this.modified_lines[permutation[i]];
            }
            codelines = randomized_lines;
        } else {
          initial_state = this.modified_lines;
        }

        this.addLogEntry({type: 'init', time: new Date(), initial: initial_state});

        if (this.options.trashId) {
            $("#" + this.options.trashId).html('
'+this.options.trash_label+'

'+codelines.join('')+'
');
            $("#" + this.options.sortableId).html('
'+this.options.solution_label+'

');
        } else {
            var d = $("#" + this.options.sortableId);
            var h = '
'+codelines.join('')+'
';
            $("#" + this.options.sortableId).html('
'+codelines.join('')+'
');
        }
        if (window.prettyPrint && (typeof(this.options.prettyPrint) === "undefined" || this.options.prettyPrint)) {
            prettyPrint(); //NOT IMPLEMENTET YET?
        }
        var sortable = $("#ul-" + this.options.sortableId).sortable({
            start : function() { that.clearFeedback(); },
            stop : function(event, ui) {
                if ($(event.target)[0] != ui.item.parent()[0]) {
                    return;
                }
                var ind = that.updateIndent(ui.position.left - ui.item.parent().offset().left,
                                        ui.item[0].id);
                ui.item.css("margin-left", that.options.x_indent * ind + "px");
                that.addLogEntry({type: "moveOutput", target: ui.item[0].id}, true);
            },
            receive : function(event, ui) {
                var ind = that.updateIndent(ui.position.left - ui.item.parent().offset().left,
                                        ui.item[0].id);
                ui.item.css("margin-left", that.options.x_indent * ind + "px");
                that.addLogEntry({type: "addOutput", target: ui.item[0].id}, true);
            },
            grid : [that.options.x_indent, 1 ]
        });
        if (this.options.trashId) {
            var trash = $("#ul-" + this.options.trashId).sortable({
                connectWith: sortable,
                start: function() { that.clearFeedback(); },
                receive: function(event, ui) {
                    that.getLineById(ui.item[0].id).indent = 0;
                    ui.item.css("margin-left", "0");
                    that.addLogEntry({type: "removeOutput", target: ui.item[0].id}, true);
                },
                stop: function(event, ui) {
                    if ($(event.target)[0] != ui.item.parent()[0]) {
                        // line moved to output and logged there
                        return;
                    }
                    that.addLogEntry({type: "moveInput", target: ui.item[0].id}, true);
                }
            });
            sortable.sortable('option', 'connectWith', trash);
        }
    };

<|endoftext|>


<commit_before>/* Author:

 */

barWidth = 0.6;
mainFont = "inherit";

function plot_pie(raw, chartdiv) {
    var data = {labels: [], values: []};
    $.each(raw.values, function(index, value) {
        data.labels.push(value[0]+' ('+value[1]+')');
        data.values.push(value[1]);
    });

    $('#'+chartdiv).before('
'+raw.title+'
');

    var r = Raphael(chartdiv);
    var pie = r.piechart(150, 150, 120, data.values, { legend: data.labels, legendpos: "east"}).attr({ font: mainFont });
    pie.hover(function () {
        this.sector.stop();
        this.sector.scale(1.1, 1.1, this.cx, this.cy);
        if (this.label) {
            this.label[0].stop();
            this.label[0].attr({ r: 7.5 });
            this.label[1].attr({ "font-weight": 800 });
        }
    }, function () {
        this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
        if (this.label) {
            this.label[0].animate({ r: 5 }, 500, "bounce");
            this.label[1].attr({ "font-weight": 400 });
        }
    });
}

function plot_cotation(raw, chartdiv) {

    $('#'+chartdiv).before('
'+raw.title+'
');

    var r = Raphael(chartdiv);

    var fin = function () {
      this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
    };
    var fout = function () {
      this.flag.animate({opacity: 0}, 300, function () {this.remove();});
    };

    r.barchart(10, 10, 420, 260, [raw.values], {type: "soft"})
    .hover(fin, fout)
    .label([raw.labels], true);

    // var xs = [];
    // for(var i=0; i<raw.values.length; i++){
    //     xs.push(i);
    // }
    // ys = raw.values,
    // // axisy = raw.values,
    // r.dotchart(10, 10, 420, 260, xs, ys, raw.values, {
    //     symbol: "o",
    //     max: 10,
    //     heat: true,
    //     axis: "0 0 1 0",
    //     axisxstep: raw.labels.length-1, axisxlabels: raw.labels, axisxtype: " "
    // }).hover(function () {
    //     this.marker = this.marker || r.tag(this.x, this.y, this.value, 0, this.r + 2).insertBefore(this);
    //     this.marker.show();
    // }, function () {
    //     this.marker && this.marker.hide();
    // }).attr({ font: mainFont });
}

/*
function plot_cotation_globale_per_activity(data) {
    var d = [];
    $.each(data.values, function(index, value) {
        val = [];
        $.each(value, function(index2, value2) {
            val.push([index2, value2]);
        });
        d.push({
            label: data.labels[index],
            data: val
        });
    });

    var labels = [];
    $.each(data.xlabels, function(index, value) {
        labels.push([index + barWidth/2., value]);
    });

    $('#chart_cot_globale').before('
'+data.title+'
');
    $.plot($('#chart_cot_globale'), d,  {
	series: {
            stack: true,
            bars: { show: true, barWidth: barWidth }
        },
        legend: { show: true },
        xaxis: {
            show: true,
            ticks: labels,
        },
        grid: {
            hoverable: true,
            clickable: true,
            backgroundColor: { colors: ["#fff", "#eee"] }
        }
    });

}
*/

function renderplot(data) {
    if (data === null || data.length === 0) {
        $("#charts").html('Error retrieving data');
        return 1;
    }

    $("#nb_outings").text(data.nb_outings);
    $("#date_generated").text(data.date_generated);
    $("#download_time").text(data.time.download);
    $("#parse_time").text(data.time.parse);
    $("#generation_time").text(data.time.generation);
    $("#total_time").text(data.time.total);
    $("#origin-link").attr({href: data.url});

    plot_pie(data.global.activities, 'chart_activities');
    plot_pie(data.global.area, 'chart_area');

    plot_cotation(data.global.cotation_globale, 'chart_cot_globale');
    // plot_cotation_globale_per_activity(data.global.cotation_per_activity);

    $.each(data.activities, function(index, value) {
        if (data[value].cotation.values != null && data[value].cotation.values.length > 0) {
            $("#charts").append('

');
            plot_cotation(data[value].cotation, 'cotation_'+value);
        }
    });
}
<commit_msg>plots - cotation_per_activity + use the same bar width for all barcharts<commit_after>/* Author:

 */

txtattr = { font: "inherit" };

function plot_pie(raw, chartdiv) {
    var data = {labels: [], values: []};
    $.each(raw.values, function(index, value) {
        data.labels.push(value[0]+' ('+value[1]+')');
        data.values.push(value[1]);
    });

    $('#'+chartdiv).before('
'+raw.title+'
');

    var r = Raphael(chartdiv);
    var pie = r.piechart(150, 150, 120, data.values, { legend: data.labels, legendpos: "east"}).attr(txtattr);
    pie.hover(function () {
        this.sector.stop();
        this.sector.scale(1.1, 1.1, this.cx, this.cy);
        if (this.label) {
            this.label[0].stop();
            this.label[0].attr({ r: 7.5 });
            this.label[1].attr({ "font-weight": 800 });
        }
    }, function () {
        this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
        if (this.label) {
            this.label[0].animate({ r: 5 }, 500, "bounce");
            this.label[1].attr({ "font-weight": 400 });
        }
    });
}

function plot_cotation(raw, chartdiv, width, height) {

    $('#'+chartdiv).before('
'+raw.title+'
');

    var r = Raphael(chartdiv, width+50, height+20);

    var fin = function () {
      this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
    };
    var fout = function () {
      this.flag.animate({opacity: 0}, 300, function () {this.remove();});
    };

    r.barchart(50, 10, width, height, [raw.values], {type: "soft"})
    .hover(fin, fout)
    .label([raw.labels], true);

    // var xs = [];
    // for(var i=0; i<raw.values.length; i++){
    //     xs.push(i);
    // }
    // ys = raw.values,
    // // axisy = raw.values,
    // r.dotchart(10, 10, 420, 260, xs, ys, raw.values, {
    //     symbol: "o",
    //     max: 10,
    //     heat: true,
    //     axis: "0 0 1 0",
    //     axisxstep: raw.labels.length-1, axisxlabels: raw.labels, axisxtype: " "
    // }).hover(function () {
    //     this.marker = this.marker || r.tag(this.x, this.y, this.value, 0, this.r + 2).insertBefore(this);
    //     this.marker.show();
    // }, function () {
    //     this.marker && this.marker.hide();
    // }).attr(txtattr);
}

function plot_cotation_globale_per_activity(data, chartdiv) {

  $('#'+chartdiv).before('
'+data.title+'
');
  var r = Raphael(chartdiv);

  var fin2 = function () {
    var y = [], res = [];
    for (var i = this.bars.length; i--;) {
      y.push(this.bars[i].y);
      if (this.bars[i].value) {
        res.push(data.labels[i] + " : " + this.bars[i].value);
      }
    }
    this.flag = r.popup(this.bars[0].x, Math.min.apply(Math, y), res.join("\n ")).insertBefore(this);
  };
  var fout2 = function () {
    this.flag.animate({opacity: 0}, 300, function () {this.remove();});
  };

  r.barchart(50, 50, 420, 260, data.values, {stacked: true, type: "soft"})
    .hoverColumn(fin2, fout2)
    .label(data.xlabels, true);
}

function renderplot(data) {
    if (data === null || data.length === 0) {
        $("#charts").html('Error retrieving data');
        return 1;
    }

    $("#nb_outings").text(data.nb_outings);
    $("#date_generated").text(data.date_generated);
    $("#download_time").text(data.time.download);
    $("#parse_time").text(data.time.parse);
    $("#generation_time").text(data.time.generation);
    $("#total_time").text(data.time.total);
    $("#origin-link").attr({href: data.url});

    plot_pie(data.global.activities, 'chart_activities');
    plot_pie(data.global.area, 'chart_area');

    // plot_cotation(data.global.cotation_globale, 'chart_cot_globale');
    plot_cotation_globale_per_activity(data.global.cotation_per_activity, 'chart_cot_globale');

    $.each(data.activities, function(index, value) {
        if (data[value].cotation.values != null && data[value].cotation.values.length > 0) {
            $("#charts").append('

');
            plot_cotation(data[value].cotation, 'cotation_'+value,
                          30*data[value].cotation.labels.length, 250);
        }
    });
}
<|endoftext|>


<commit_before>/*jshint indent:2, curly:true eqeqeq:true, immed:true, latedef:true,
newcap:true, noarg:true, regexp:true, undef:true, strict:true, trailing:true
white:true*/
/*global XT:true, XM:true, Backbone:true, _:true, console:true */

(function () {
  "use strict";

  /**
    @class

    @extends XM.Document
  */
  XM.OpportunityType = XM.Document.extend({
    /** @scope XM.OpportunityType.prototype */

    recordType: 'XM.OpportunityType',

    documentKey: 'name',

    privileges: {
      "all": {
        "create": "MaintainOpportunityTypes",
        "read": true,
        "update": "MaintainOpportunityTypes",
        "delete": "MaintainOpportunityTypes"
      }
    }

  });

  /**
    @class

    @extends XM.Document
  */
  XM.OpportunityStage = XM.Document.extend({
    /** @scope XM.OpportunityStage.prototype */

    recordType: 'XM.OpportunityStage',

    documentKey: 'name',

    privileges: {
      "all": {
        "create": "MaintainOpportunityStages",
        "read": true,
        "update": "MaintainOpportunityStages",
        "delete": "MaintainOpportunityStages"
      }
    },

    defaults: {
      deactivate: false
    },

    requiredAttributes: [
      "deactivate"
    ]

  });

  /**
    @class

    @extends XM.Document
  */
  XM.OpportunitySource = XM.Document.extend({
    /** @scope XM.OpportunitySource.prototype */

    recordType: 'XM.OpportunitySource',

    documentKey: 'name',

    privileges: {
      "all": {
        "create": "MaintainOpportunitySources",
        "read": true,
        "update": "MaintainOpportunitySources",
        "delete": "MaintainOpportunitySources"
      }
    }

  });

  /**
    @class

    @extends XM.Document
  */
  XM.Opportunity = XM.Document.extend({
    /** @scope XM.OpportunityInfo.prototype */

    recordType: 'XM.Opportunity',

    numberPolicy: XM.Document.AUTO_NUMBER,

    defaults: {
      isActive: true
    },

    requiredAttributes: [
      "account",
      "name",
      "isActive",
      "opportunityStage",
      "opportunitySource",
      "opportunityType"
    ],

    relations: [{
      type: Backbone.HasOne,
      key: 'account',
      relatedModel: 'XM.AccountInfo'
    }, {
      type: Backbone.HasOne,
      key: 'contact',
      relatedModel: 'XM.ContactInfo'
    }, {
      type: Backbone.HasOne,
      key: 'assignedTo',
      relatedModel: 'XM.UserAccountInfo'
    }, {
      type: Backbone.HasOne,
      key: 'priority',
      relatedModel: 'XM.Priority',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasOne,
      key: 'opportunityStage',
      relatedModel: 'XM.OpportunityStage',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasOne,
      key: 'opportunityType',
      relatedModel: 'XM.OpportunityType',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasOne,
      key: 'opportunitySource',
      relatedModel: 'XM.OpportunitySource',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasMany,
      key: 'comments',
      relatedModel: 'XM.OpportunityComment',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'characteristics',
      relatedModel: 'XM.OpportunityCharacteristic',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'accounts',
      relatedModel: 'XM.OpportunityAccount',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'contacts',
      relatedModel: 'XM.OpportunityContact',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'items',
      relatedModel: 'XM.OpportunityItem',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'files',
      relatedModel: 'XM.OpportunityFile',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'images',
      relatedModel: 'XM.OpportunityImage',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'urls',
      relatedModel: 'XM.OpportunityUrl',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'opportunities',
      relatedModel: 'XM.OpportunityOpportunity',
      reverseRelation: {
        key: 'opportunity'
      }
    }],

    // ..........................................................
    // METHODS
    //

    initialize: function () {
      XM.Document.prototype.initialize.apply(this, arguments);
      this.on('change:assignedTo', this.assignedToDidChange);
    },

    assignedToDidChange: function (model, value, options) {
      var status = this.getStatus(),
        K = XT.Model,
        assignedTo,
        assignDate;
      if ((options && options.force) || !(status & K.READY)) { return; }

      // Set the assign date if it hasn't been already
      assignedTo = this.get('assignedTo');
      assignDate = this.get('assignDate');
      if (assignedTo && !assignDate) {
        this.set('assignDate', new Date());
      }
    }

  });

  /**
    @class

    @extends XM.Comment
  */
  XM.OpportunityComment = XM.Comment.extend({
    /** @scope XM.OpportunityComment.prototype */

    recordType: 'XM.OpportunityComment'

  });

  /**
    @class

    @extends XM.Characteristic
  */
  XM.OpportunityCharacteristic = XM.Characteristic.extend({
    /** @scope XM.OpportunityCharacteristic.prototype */

    recordType: 'XM.OpportunityCharacteristic'

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityAccount = XT.Model.extend({
    /** @scope XM.OpportunityAccount.prototype */

    recordType: 'XM.OpportunityAccount',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'account',
      relatedModel: 'XM.AccountInfo'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityContact = XT.Model.extend({
    /** @scope XM.OpportunityContact.prototype */

    recordType: 'XM.OpportunityContact',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'contact',
      relatedModel: 'XM.ContactInfo'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityItem = XT.Model.extend({
    /** @scope XM.OpportunityItem.prototype */

    recordType: 'XM.OpportunityItem',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'item',
      relatedModel: 'XM.ItemInfo'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityFile = XT.Model.extend({
    /** @scope XM.OpportunityFile.prototype */

    recordType: 'XM.OpportunityFile',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'file',
      relatedModel: 'XM.FileInfo'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityImage = XT.Model.extend({
    /** @scope XM.OpportunityImage.prototype */

    recordType: 'XM.OpportunityImage',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'image',
      relatedModel: 'XM.ImageInfo'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityUrl = XT.Model.extend({
    /** @scope XM.OpportunityUrl.prototype */

    recordType: 'XM.OpportunityUrl',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'url',
      relatedModel: 'XM.Url'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityOpportunity = XT.Model.extend({
    /** @scope XM.OpportunityOpportunity.prototype */

    recordType: 'XM.OpportunityOpportunity',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'Opportunity',
      relatedModel: 'XM.OpportunityInfo'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityInfo = XT.Model.extend({
    /** @scope XM.OpportunityInfo.prototype */

    recordType: 'XM.OpportunityInfo',

    readOnly: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'account',
      relatedModel: 'XM.AccountInfo'
    }, {
      type: Backbone.HasOne,
      key: 'contact',
      relatedModel: 'XM.ContactInfo'
    }, {
      type: Backbone.HasOne,
      key: 'assignedTo',
      relatedModel: 'XM.UserAccountInfo'
    }, {
      type: Backbone.HasOne,
      key: 'priority',
      relatedModel: 'XM.Priority',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasOne,
      key: 'opportunityStage',
      relatedModel: 'XM.OpportunityStage',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasOne,
      key: 'opportunityType',
      relatedModel: 'XM.OpportunityType',
      includeInJSON: 'guid'
    }]

  });

  // ..........................................................
  // COLLECTIONS
  //

  /**
    @class

    @extends XT.Collection
  */
  XM.OpportunityTypeCollection = XT.Collection.extend({
    /** @scope XM.OpportunityTypeCollection.prototype */

    model: XM.OpportunityType

  });

  /**
    @class

    @extends XT.Collection
  */
  XM.OpportunityStageCollection = XT.Collection.extend({
    /** @scope XM.OpportunityStageCollection.prototype */

    model: XM.OpportunityStage

  });

  /**
    @class

    @extends XT.Collection
  */
  XM.OpportunitySourceCollection = XT.Collection.extend({
    /** @scope XM.OpportunitySourceCollection.prototype */

    model: XM.OpportunitySource

  });

  /**
    @class

    @extends XT.Collection
  */
  XM.OpportunityInfoCollection = XT.Collection.extend({
    /** @scope XM.OpportunityInfoCollection.prototype */

    model: XM.OpportunityInfo

  });

}());
<commit_msg>add currency relation to opportunity.<commit_after>/*jshint indent:2, curly:true eqeqeq:true, immed:true, latedef:true,
newcap:true, noarg:true, regexp:true, undef:true, strict:true, trailing:true
white:true*/
/*global XT:true, XM:true, Backbone:true, _:true, console:true */

(function () {
  "use strict";

  /**
    @class

    @extends XM.Document
  */
  XM.OpportunityType = XM.Document.extend({
    /** @scope XM.OpportunityType.prototype */

    recordType: 'XM.OpportunityType',

    documentKey: 'name',

    privileges: {
      "all": {
        "create": "MaintainOpportunityTypes",
        "read": true,
        "update": "MaintainOpportunityTypes",
        "delete": "MaintainOpportunityTypes"
      }
    }

  });

  /**
    @class

    @extends XM.Document
  */
  XM.OpportunityStage = XM.Document.extend({
    /** @scope XM.OpportunityStage.prototype */

    recordType: 'XM.OpportunityStage',

    documentKey: 'name',

    privileges: {
      "all": {
        "create": "MaintainOpportunityStages",
        "read": true,
        "update": "MaintainOpportunityStages",
        "delete": "MaintainOpportunityStages"
      }
    },

    defaults: {
      deactivate: false
    },

    requiredAttributes: [
      "deactivate"
    ]

  });

  /**
    @class

    @extends XM.Document
  */
  XM.OpportunitySource = XM.Document.extend({
    /** @scope XM.OpportunitySource.prototype */

    recordType: 'XM.OpportunitySource',

    documentKey: 'name',

    privileges: {
      "all": {
        "create": "MaintainOpportunitySources",
        "read": true,
        "update": "MaintainOpportunitySources",
        "delete": "MaintainOpportunitySources"
      }
    }

  });

  /**
    @class

    @extends XM.Document
  */
  XM.Opportunity = XM.Document.extend({
    /** @scope XM.OpportunityInfo.prototype */

    recordType: 'XM.Opportunity',

    numberPolicy: XM.Document.AUTO_NUMBER,

    defaults: {
      isActive: true
    },

    requiredAttributes: [
      "account",
      "name",
      "isActive",
      "opportunityStage",
      "opportunitySource",
      "opportunityType"
    ],

    relations: [{
      type: Backbone.HasOne,
      key: 'account',
      relatedModel: 'XM.AccountInfo'
    }, {
      type: Backbone.HasOne,
      key: 'contact',
      relatedModel: 'XM.ContactInfo'
    }, {
      type: Backbone.HasOne,
      key: 'assignedTo',
      relatedModel: 'XM.UserAccountInfo'
    }, {
      type: Backbone.HasOne,
      key: 'priority',
      relatedModel: 'XM.Priority',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasOne,
      key: 'currency',
      relatedModel: 'XM.Currency',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasOne,
      key: 'opportunityStage',
      relatedModel: 'XM.OpportunityStage',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasOne,
      key: 'opportunityType',
      relatedModel: 'XM.OpportunityType',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasOne,
      key: 'opportunitySource',
      relatedModel: 'XM.OpportunitySource',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasMany,
      key: 'comments',
      relatedModel: 'XM.OpportunityComment',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'characteristics',
      relatedModel: 'XM.OpportunityCharacteristic',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'accounts',
      relatedModel: 'XM.OpportunityAccount',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'contacts',
      relatedModel: 'XM.OpportunityContact',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'items',
      relatedModel: 'XM.OpportunityItem',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'files',
      relatedModel: 'XM.OpportunityFile',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'images',
      relatedModel: 'XM.OpportunityImage',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'urls',
      relatedModel: 'XM.OpportunityUrl',
      reverseRelation: {
        key: 'opportunity'
      }
    }, {
      type: Backbone.HasMany,
      key: 'opportunities',
      relatedModel: 'XM.OpportunityOpportunity',
      reverseRelation: {
        key: 'opportunity'
      }
    }],

    // ..........................................................
    // METHODS
    //

    initialize: function () {
      XM.Document.prototype.initialize.apply(this, arguments);
      this.on('change:assignedTo', this.assignedToDidChange);
    },

    assignedToDidChange: function (model, value, options) {
      var status = this.getStatus(),
        K = XT.Model,
        assignedTo,
        assignDate;
      if ((options && options.force) || !(status & K.READY)) { return; }

      // Set the assign date if it hasn't been already
      assignedTo = this.get('assignedTo');
      assignDate = this.get('assignDate');
      if (assignedTo && !assignDate) {
        this.set('assignDate', new Date());
      }
    }

  });

  /**
    @class

    @extends XM.Comment
  */
  XM.OpportunityComment = XM.Comment.extend({
    /** @scope XM.OpportunityComment.prototype */

    recordType: 'XM.OpportunityComment'

  });

  /**
    @class

    @extends XM.Characteristic
  */
  XM.OpportunityCharacteristic = XM.Characteristic.extend({
    /** @scope XM.OpportunityCharacteristic.prototype */

    recordType: 'XM.OpportunityCharacteristic'

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityAccount = XT.Model.extend({
    /** @scope XM.OpportunityAccount.prototype */

    recordType: 'XM.OpportunityAccount',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'account',
      relatedModel: 'XM.AccountInfo'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityContact = XT.Model.extend({
    /** @scope XM.OpportunityContact.prototype */

    recordType: 'XM.OpportunityContact',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'contact',
      relatedModel: 'XM.ContactInfo'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityItem = XT.Model.extend({
    /** @scope XM.OpportunityItem.prototype */

    recordType: 'XM.OpportunityItem',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'item',
      relatedModel: 'XM.ItemInfo'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityFile = XT.Model.extend({
    /** @scope XM.OpportunityFile.prototype */

    recordType: 'XM.OpportunityFile',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'file',
      relatedModel: 'XM.FileInfo'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityImage = XT.Model.extend({
    /** @scope XM.OpportunityImage.prototype */

    recordType: 'XM.OpportunityImage',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'image',
      relatedModel: 'XM.ImageInfo'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityUrl = XT.Model.extend({
    /** @scope XM.OpportunityUrl.prototype */

    recordType: 'XM.OpportunityUrl',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'url',
      relatedModel: 'XM.Url'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityOpportunity = XT.Model.extend({
    /** @scope XM.OpportunityOpportunity.prototype */

    recordType: 'XM.OpportunityOpportunity',

    isDocumentAssignment: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'Opportunity',
      relatedModel: 'XM.OpportunityInfo'
    }]

  });

  /**
    @class

    @extends XT.Model
  */
  XM.OpportunityInfo = XT.Model.extend({
    /** @scope XM.OpportunityInfo.prototype */

    recordType: 'XM.OpportunityInfo',

    readOnly: true,

    relations: [{
      type: Backbone.HasOne,
      key: 'account',
      relatedModel: 'XM.AccountInfo'
    }, {
      type: Backbone.HasOne,
      key: 'contact',
      relatedModel: 'XM.ContactInfo'
    }, {
      type: Backbone.HasOne,
      key: 'assignedTo',
      relatedModel: 'XM.UserAccountInfo'
    }, {
      type: Backbone.HasOne,
      key: 'priority',
      relatedModel: 'XM.Priority',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasOne,
      key: 'currency',
      relatedModel: 'XM.Currency',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasOne,
      key: 'opportunityStage',
      relatedModel: 'XM.OpportunityStage',
      includeInJSON: 'guid'
    }, {
      type: Backbone.HasOne,
      key: 'opportunityType',
      relatedModel: 'XM.OpportunityType',
      includeInJSON: 'guid'
    }]

  });

  // ..........................................................
  // COLLECTIONS
  //

  /**
    @class

    @extends XT.Collection
  */
  XM.OpportunityTypeCollection = XT.Collection.extend({
    /** @scope XM.OpportunityTypeCollection.prototype */

    model: XM.OpportunityType

  });

  /**
    @class

    @extends XT.Collection
  */
  XM.OpportunityStageCollection = XT.Collection.extend({
    /** @scope XM.OpportunityStageCollection.prototype */

    model: XM.OpportunityStage

  });

  /**
    @class

    @extends XT.Collection
  */
  XM.OpportunitySourceCollection = XT.Collection.extend({
    /** @scope XM.OpportunitySourceCollection.prototype */

    model: XM.OpportunitySource

  });

  /**
    @class

    @extends XT.Collection
  */
  XM.OpportunityInfoCollection = XT.Collection.extend({
    /** @scope XM.OpportunityInfoCollection.prototype */

    model: XM.OpportunityInfo

  });

}());
<|endoftext|>


<commit_before>$(document).ready(function(){
  $summary = $('#summary');
  $nav = $('.nav');

  $('#loading')
    .hide()  // hide it initially
    .ajaxStart(function() {
      $summary.hide();
      $nav.hide();
      $(this).show();
    })
    .ajaxSuccess(function() {
      $(this).hide();
      $summary.show();
      $nav.show();
    })
    .ajaxError(function() {
      $summary.hide();
      $nav.hide();
      $(this).hide();
      $(this).after('
Erreur lors du chargement des données
');
    });
  $.getJSON(jsonurl, renderplot);
});

function renderplot(data) {
    if (data === null || data.length === 0) {
        $("#charts").html('Error retrieving data');
        return 1;
    }

    $("#nb_outings").text(data.nb_outings);
    $("#date_generated").text(data.date_generated);
    $("#download_time").text(data.time.download);
    $("#total_time").text(data.time.total);
    $("#origin-link").attr({href: data.url});
    $("#origin-profile").attr({href: data.user_url});

    pieplot(data.global.activities, 'chart_activities');
    pieplot(data.global.area, 'chart_area');

    // plot_cotation(data.global.cotation, 'chart_cot_globale');
    plot_cotation_globale_per_activity(data.global.cotation_per_activity, 'chart_cot_globale');

    $.each(data.activities, function(index, value) {
      if (data[value] &&
          data[value].cotation.values != null &&
          data[value].cotation.values.length > 0)
      {
        $(".nav").append('
'+value+'
');
        $("#tabs").append('

');

        $("#"+value).append('

');
        barplot(data[value].cotation, 'cotation_'+value);

        $("#"+value).append('

');
        barplot(data[value].cotation_globale, 'cotation_globale_'+value);

        $("#"+value).append('

');
        hbarplot(data[value].outings_per_year, 'outings_'+value);

        $("#"+value).append('

');
        hbarplot(data[value].gain_per_year, 'gain_'+value);

        $("#"+value).append('

');
        lineplot(data[value].gain_per_year, 'gain_cumul_'+value);
      }
    });
}
<commit_msg>don't plot cotation_globale_per_activity<commit_after>$(document).ready(function(){
  $summary = $('#summary');
  $nav = $('.nav');

  $('#loading')
    .hide()  // hide it initially
    .ajaxStart(function() {
      $summary.hide();
      $nav.hide();
      $(this).show();
    })
    .ajaxSuccess(function() {
      $(this).hide();
      $summary.show();
      $nav.show();
    })
    .ajaxError(function() {
      $summary.hide();
      $nav.hide();
      $(this).hide();
      $(this).after('
Erreur lors du chargement des données
');
    });
  $.getJSON(jsonurl, renderplot);
});

function renderplot(data) {
    if (data === null || data.length === 0) {
        $("#charts").html('Error retrieving data');
        return 1;
    }

    $("#nb_outings").text(data.nb_outings);
    $("#date_generated").text(data.date_generated);
    $("#download_time").text(data.time.download);
    $("#total_time").text(data.time.total);
    $("#origin-link").attr({href: data.url});
    $("#origin-profile").attr({href: data.user_url});

    pieplot(data.global.activities, 'chart_activities');
    pieplot(data.global.area, 'chart_area');

    // plot_cotation(data.global.cotation, 'chart_cot_globale');
    // plot_cotation_globale_per_activity(data.global.cotation_per_activity, 'chart_cot_globale');

    $.each(data.activities, function(index, value) {
      if (data[value] &&
          data[value].cotation.values != null &&
          data[value].cotation.values.length > 0)
      {
        $(".nav").append('
'+value+'
');
        $("#tabs").append('

');

        $("#"+value).append('

');
        barplot(data[value].cotation, 'cotation_'+value);

        $("#"+value).append('

');
        barplot(data[value].cotation_globale, 'cotation_globale_'+value);

        $("#"+value).append('

');
        hbarplot(data[value].outings_per_year, 'outings_'+value);

        $("#"+value).append('

');
        hbarplot(data[value].gain_per_year, 'gain_'+value);

        $("#"+value).append('

');
        lineplot(data[value].gain_per_year, 'gain_cumul_'+value);
      }
    });
}
<|endoftext|>