<commit_before>import Ember from 'ember';
import { emberDataVersionIs } from 'ember-version-is';

const keys = Object.keys || Ember.keys;
/**
  The Simple Infinity Route Mixin enables an application route to load
  records for the route `model` as triggered by the controller (or Infinity Loader
  component).

  @class RouteMixin
  @namespace EmberInfinity
  @module ember-infinity/mixins/route
  @extends Ember.Mixin
*/
export default Ember.Mixin.create({

  /**
    @private
    @property _perPage
    @type Integer
    @default 25
  */
  _perPage: 25,

  /**
    @private
    @property _extraParams
    @type Object
    @default {}
  */
  _extraParams: {},

  /**
    @private
    @property _boundParams
    @type Object
    @default {}
  */
  _boundParams: {},

  /**
    @private
    @property _loadingMore
    @type Boolean
    @default false
  */
  _loadingMore: false,

  /**
   * How many items have been fetched thus far. This is the 'skip' count.
   *
   *  @private
   *  @property _totalItemCount
   *  @type Integer
   *  @default 0
   */
  _totalItemCount: 0,

  /**
   * The name of the total items parameter, ie. 'skip'.
   */
  totalItemParam: 'skip',

  /**
    @private
    @property _infinityModelName
    @type String
    @default null
  */
  _infinityModelName: null,

  /**
    @private
    @property _modelPath
    @type String
    @default 'controller.model'
  */
  _modelPath: 'controller.model',

  /**
   * Name of the "per page" param in the
   * resource request payload
   * @type {String}
   * @default  "take"
   */
  perPageParam: 'take',

  /**
   * The supported findMethod name for
   * the developers Ember Data version.
   * Provided here for backwards compat.
   * @type {String}
   * @default "query"
   */
  _storeFindMethod: 'query',

  /*
   * Instead of tracking total pages and current pages, we simply keep fetching until the
   * server returns no more items. Now this property can be the flag that says whether
   * we've fetched zero items or not. Defaulting to true ensures we always fetch at
   * least once.
   */
  _canLoadMore: true,

  _storeService: null,

  /**
    Use the infinityModel method in the place of `this.store.find('model')` to
    initialize the Infinity Model for your route.

    @method infinityModel
    @param {String} modelName The name of the model.
    @param {Object} options Optional, the perPage and startingPage to load from.
    @param {Object} boundParams Optional, any route properties to be included as additional params.
    @return {Ember.RSVP.Promise}
  */
  infinityModel(modelName, options, boundParams, storeService) {
    if (storeService) this.set('_storeService', storeService);
    let store = storeService || this.store;

    if (emberDataVersionIs('greaterThan', '1.0.0-beta.19.2') && emberDataVersionIs('lessThan', '1.13.4')) {
      throw new Ember.Error("Ember Infinity: You are using an unsupported version of Ember Data.  Please upgrade to at least 1.13.4 or downgrade to 1.0.0-beta.19.2");
    }

    if (emberDataVersionIs('lessThan', '1.13.0')) {
      this.set('_storeFindMethod', 'find');
    }

    if (Ember.isEmpty(store) || Ember.isEmpty(store[this.get("_storeFindMethod")])){
      throw new Ember.Error("Ember Infinity: Ember Data store is not available to infinityModel");
    } else if (modelName === undefined) {
      throw new Ember.Error("Ember Infinity: You must pass a Model Name to infinityModel");
    }

    this.set("_totalItemCount", 0);
    this.set("_canLoadMore", true);
    this.set("reachedInfinity", false);

    this.set('_infinityModelName', modelName);

    options = options ? Ember.merge({}, options) : {};
    var startingItem = options.startingItem === undefined ? 0 : options.startingItem;
    var perPage      = options.perPage || this.get('_perPage');
    var modelPath    = options.modelPath || this.get('_modelPath');

    delete options.startingItem;
    delete options.perPage;
    delete options.modelPath;

    this.set('_perPage', perPage);
    this.set('_modelPath', modelPath);
    this.set('_extraParams', options);

    var requestPayloadBase = {};
    requestPayloadBase[this.get('perPageParam')] = perPage;
    requestPayloadBase[this.get('totalItemParam')] = startingItem;

    if (typeof boundParams === 'object') {
      this.set('_boundParams', boundParams);
      options = this._includeBoundParams(options, boundParams);
    }

    var params = Ember.merge(requestPayloadBase, options);
    let promise = store[this.get("_storeFindMethod")](modelName, params);

    promise.then(
      infinityModel => {
        let count = infinityModel.get("length");

        if (count === 0) {
          this.set("_canLoadMore", false);
        }

        let currentCount = this.get("_totalItemCount");
        let newTotal = currentCount + count;
        this.set("_totalItemCount", newTotal);

        infinityModel.set('reachedInfinity', !this.get('_canLoadMore'));

        if(this.infinityModelUpdated) {
          Ember.run.scheduleOnce('afterRender', this, 'infinityModelUpdated', {
            lastItemLoaded: newTotal,
            newObjects: infinityModel
          });
        }
      },
      () => {
        throw new Ember.Error("Ember Infinity: Could not fetch Infinity Model. Please check your serverside configuration.");
      }
    );

    return promise;
  },

  /**
   Trigger a load of the next page of results.

   @method infinityLoad
   @return {Boolean}
   */
  _infinityLoad() {
    var skipItems   = this.get("_totalItemCount");
    var perPage     = this.get('_perPage');
    var modelName   = this.get('_infinityModelName');
    var options     = this.get('_extraParams');
    var boundParams = this.get('_boundParams');
    let store = this.get('_storeService') || this.store;

    if (!this.get('_loadingMore') && this.get('_canLoadMore')) {
      this.set('_loadingMore', true);

      var requestPayloadBase = {};
      requestPayloadBase[this.get('perPageParam')] = perPage;
      requestPayloadBase[this.get('totalItemParam')] = skipItems;

      options = this._includeBoundParams(options, boundParams);
      var params = Ember.merge(requestPayloadBase, this.get('_extraParams'));

      let promise = store[this._storeFindMethod](modelName, params);

      promise.then(
        newObjects => {

          let count = newObjects.get("length");

          if (count === 0) {
            this.set("_canLoadMore", false);
          }

          let currentCount = this.get("_totalItemCount");
          let newTotal = currentCount + count;
          this.set("_totalItemCount", newTotal);

          this.updateInfinityModel(newObjects);
          this.set('_loadingMore', false);

          if(this.infinityModelUpdated) {
            Ember.run.scheduleOnce('afterRender', this, 'infinityModelUpdated', {
              lastItemLoaded: newTotal,
              newObjects: newObjects
            });
          }
          if (!this.get('_canLoadMore')) {
            this.set(this.get('_modelPath') + '.reachedInfinity', true);
            if(this.infinityModelLoaded) {
              Ember.run.scheduleOnce('afterRender', this, 'infinityModelLoaded', {
                totalItems: newTotal
              });
            }
          }
        },
        () => {
          this.set('_loadingMore', false);
          throw new Ember.Error("Ember Infinity: Could not fetch Infinity Model. Please check your serverside configuration.");
        }
      );
    } else {
      if (!this.get('_canLoadMore')) {
        this.set(this.get('_modelPath') + '.reachedInfinity', true);
        if(this.infinityModelLoaded) {
          Ember.run.scheduleOnce('afterRender', this, 'infinityModelLoaded', { totalItems: skipItems });
        }
      }
    }
    return false;
  },

  /**
   include any bound params into the options object.

   @method includeBoundParams
   @param {Object} options, the object to include bound params into.
   @param {Object} boundParams, an object of properties to be included into options.
   @return {Object}
   */
  _includeBoundParams: function(options, boundParams) {
    if (!Ember.isEmpty(boundParams)) {
      keys(boundParams).forEach(k => options[k] = this.get(boundParams[k]));
    }

    return options;
  },

  /**
   Update the infinity model with new objects

   @method updateInfinityModel
   @param {Ember.Enumerable} newObjects The new objects to add to the model
   @return {Ember.Array} returns the updated infinity model
   */
  updateInfinityModel(newObjects) {
    var infinityModel = this.get(this.get('_modelPath'));

    return infinityModel.pushObjects(newObjects.get('content'));
  },

  actions: {
    infinityLoad() {
      this._infinityLoad();
    }
  }
});
<commit_msg>Handle running in a component better.<commit_after>import Ember from 'ember';
import { emberDataVersionIs } from 'ember-version-is';

const keys = Object.keys || Ember.keys;
/**
  The Simple Infinity Route Mixin enables an application route to load
  records for the route `model` as triggered by the controller (or Infinity Loader
  component).

  @class RouteMixin
  @namespace EmberInfinity
  @module ember-infinity/mixins/route
  @extends Ember.Mixin
*/
export default Ember.Mixin.create({

  /**
    @private
    @property _perPage
    @type Integer
    @default 25
  */
  _perPage: 25,

  /**
    @private
    @property _extraParams
    @type Object
    @default {}
  */
  _extraParams: {},

  /**
    @private
    @property _boundParams
    @type Object
    @default {}
  */
  _boundParams: {},

  /**
    @private
    @property _loadingMore
    @type Boolean
    @default false
  */
  _loadingMore: false,

  /**
   * How many items have been fetched thus far. This is the 'skip' count.
   *
   *  @private
   *  @property _totalItemCount
   *  @type Integer
   *  @default 0
   */
  _totalItemCount: 0,

  /**
   * The name of the total items parameter, ie. 'skip'.
   */
  totalItemParam: 'skip',

  /**
    @private
    @property _infinityModelName
    @type String
    @default null
  */
  _infinityModelName: null,

  /**
    @private
    @property _modelPath
    @type String
    @default 'controller.model'
  */
  _modelPath: 'controller.model',

  /**
   * Name of the "per page" param in the
   * resource request payload
   * @type {String}
   * @default  "take"
   */
  perPageParam: 'take',

  /**
   * The supported findMethod name for
   * the developers Ember Data version.
   * Provided here for backwards compat.
   * @type {String}
   * @default "query"
   */
  _storeFindMethod: 'query',

  /*
   * Instead of tracking total pages and current pages, we simply keep fetching until the
   * server returns no more items. Now this property can be the flag that says whether
   * we've fetched zero items or not. Defaulting to true ensures we always fetch at
   * least once.
   */
  _canLoadMore: true,

  _storeService: -1,

  /**
    Use the infinityModel method in the place of `this.store.find('model')` to
    initialize the Infinity Model for your route.

    @method infinityModel
    @param {String} modelName The name of the model.
    @param {Object} options Optional, the perPage and startingPage to load from.
    @param {Object} boundParams Optional, any route properties to be included as additional params.
    @return {Ember.RSVP.Promise}
  */
  infinityModel(modelName, options, boundParams, storeService) {
    if (storeService) {
        this.set('_storeService', storeService);
    } else {
        this.set('_storeService', null);
    }

    let store = storeService || this.store;

    if (emberDataVersionIs('greaterThan', '1.0.0-beta.19.2') && emberDataVersionIs('lessThan', '1.13.4')) {
      throw new Ember.Error("Ember Infinity: You are using an unsupported version of Ember Data.  Please upgrade to at least 1.13.4 or downgrade to 1.0.0-beta.19.2");
    }

    if (emberDataVersionIs('lessThan', '1.13.0')) {
      this.set('_storeFindMethod', 'find');
    }

    if (Ember.isEmpty(store) || Ember.isEmpty(store[this.get("_storeFindMethod")])){
      throw new Ember.Error("Ember Infinity: Ember Data store is not available to infinityModel");
    } else if (modelName === undefined) {
      throw new Ember.Error("Ember Infinity: You must pass a Model Name to infinityModel");
    }

    this.set("_totalItemCount", 0);
    this.set("_canLoadMore", true);
    this.set("reachedInfinity", false);

    this.set('_infinityModelName', modelName);

    options = options ? Ember.merge({}, options) : {};
    var startingItem = options.startingItem === undefined ? 0 : options.startingItem;
    var perPage      = options.perPage || this.get('_perPage');
    var modelPath    = options.modelPath || this.get('_modelPath');

    delete options.startingItem;
    delete options.perPage;
    delete options.modelPath;

    this.set('_perPage', perPage);
    this.set('_modelPath', modelPath);
    this.set('_extraParams', options);

    var requestPayloadBase = {};
    requestPayloadBase[this.get('perPageParam')] = perPage;
    requestPayloadBase[this.get('totalItemParam')] = startingItem;

    if (typeof boundParams === 'object') {
      this.set('_boundParams', boundParams);
      options = this._includeBoundParams(options, boundParams);
    }

    var params = Ember.merge(requestPayloadBase, options);

    let promise = store[this.get("_storeFindMethod")](modelName, params);

    promise.then(
      infinityModel => {
        let count = infinityModel.get("length");

        if (count === 0) {
          this.set("_canLoadMore", false);
        }

        let currentCount = this.get("_totalItemCount");
        let newTotal = currentCount + count;
        this.set("_totalItemCount", newTotal);

        infinityModel.set('reachedInfinity', !this.get('_canLoadMore'));

        if(this.infinityModelUpdated) {
          Ember.run.scheduleOnce('afterRender', this, 'infinityModelUpdated', {
            lastItemLoaded: newTotal,
            newObjects: infinityModel
          });
        }
      },
      () => {
        throw new Ember.Error("Ember Infinity: Could not fetch Infinity Model. Please check your serverside configuration.");
      }
    );

    return promise;
  },

  /**
   Trigger a load of the next page of results.

   @method infinityLoad
   @return {Boolean}
   */
  _infinityLoad() {
    var skipItems   = this.get("_totalItemCount");
    var perPage     = this.get('_perPage');
    var modelName   = this.get('_infinityModelName');
    var options     = this.get('_extraParams');
    var boundParams = this.get('_boundParams');
    let store = this.get('_storeService') || this.store;

    if (store === -1) return;

    if (!this.get('_loadingMore') && this.get('_canLoadMore')) {
      this.set('_loadingMore', true);
      this.set('reachedInfinity', false);

      var requestPayloadBase = {};
      requestPayloadBase[this.get('perPageParam')] = perPage;
      requestPayloadBase[this.get('totalItemParam')] = skipItems;

      options = this._includeBoundParams(options, boundParams);
      var params = Ember.merge(requestPayloadBase, this.get('_extraParams'));

      let promise = store[this.get('_storeFindMethod')](modelName, params);

      promise.then(
        newObjects => {

          let count = newObjects.get("length");

          if (count === 0) {
            this.set("_canLoadMore", false);
          }

          let currentCount = this.get("_totalItemCount");
          let newTotal = currentCount + count;
          this.set("_totalItemCount", newTotal);

          this.updateInfinityModel(newObjects);
          this.set('_loadingMore', false);

          if(this.infinityModelUpdated) {
            Ember.run.scheduleOnce('afterRender', this, 'infinityModelUpdated', {
              lastItemLoaded: newTotal,
              newObjects: newObjects
            });
          }
          if (!this.get('_canLoadMore')) {
            this.set(this.get('_modelPath') + '.reachedInfinity', true);
            if(this.infinityModelLoaded) {
              Ember.run.scheduleOnce('afterRender', this, 'infinityModelLoaded', {
                totalItems: newTotal
              });
            }
          }
        },
        () => {
          this.set('_loadingMore', false);
          throw new Ember.Error("Ember Infinity: Could not fetch Infinity Model. Please check your serverside configuration.");
        }
      );
    } else {
      if (!this.get('_canLoadMore')) {
        this.set(this.get('_modelPath') + '.reachedInfinity', true);
        if(this.infinityModelLoaded) {
          Ember.run.scheduleOnce('afterRender', this, 'infinityModelLoaded', { totalItems: skipItems });
        }
      }
    }
    return false;
  },

  /**
   include any bound params into the options object.

   @method includeBoundParams
   @param {Object} options, the object to include bound params into.
   @param {Object} boundParams, an object of properties to be included into options.
   @return {Object}
   */
  _includeBoundParams: function(options, boundParams) {
    if (!Ember.isEmpty(boundParams)) {
      keys(boundParams).forEach(k => options[k] = this.get(boundParams[k]));
    }

    return options;
  },

  /**
   Update the infinity model with new objects

   @method updateInfinityModel
   @param {Ember.Enumerable} newObjects The new objects to add to the model
   @return {Ember.Array} returns the updated infinity model
   */
  updateInfinityModel(newObjects) {
    var infinityModel = this.get(this.get('_modelPath'));

    return infinityModel.pushObjects(newObjects.get('content'));
  },

  actions: {
    infinityLoad() {
      this._infinityLoad();
    }
  }
});
<|endoftext|>


<commit_before>
FLOW.permControl = Ember.Controller.create({
  perms: [],

  init: function() {
    this._super();
    this.initPermissions();
    this.setUserPermissions();
    this.setCurrentPermissions();
  },

  initPermissions: function() {
    this.perms.push(Ember.Object.create({perm: 'createSurvey',value: false}));
    this.perms.push(Ember.Object.create({perm: 'editSurvey',value: false}));
    this.perms.push(Ember.Object.create({perm: 'uploadSurveyZipData',value: false}));
    this.perms.push(Ember.Object.create({perm: 'importDataReport',value: false}));
    this.perms.push(Ember.Object.create({perm: 'viewMessages',value: false}));
    this.perms.push(Ember.Object.create({perm: 'publishSurvey',value: false}));
    this.perms.push(Ember.Object.create({perm: 'mapData',value: false}));
    this.perms.push(Ember.Object.create({perm: 'setDataPrivacy',value: false}));
    this.perms.push(Ember.Object.create({perm: 'editRawData',value: false}));
    this.perms.push(Ember.Object.create({perm: 'deleteRawData',value: false}));
    this.perms.push(Ember.Object.create({perm: 'runReport',value: false}));
    this.perms.push(Ember.Object.create({perm: 'deleteSurvey',value: false}));
    this.perms.push(Ember.Object.create({perm: 'deleteSurveyGroup',value: false}));
    this.perms.push(Ember.Object.create({perm: 'addUser',value: false}));
    this.perms.push(Ember.Object.create({perm: 'editUser',value: false}));
    this.perms.push(Ember.Object.create({perm: 'deleteUser',value: false}));
    this.perms.push(Ember.Object.create({perm: 'approveSurvey',value: false}));
    this.perms.push(Ember.Object.create({perm: 'editEditorialContent',value: false}));
  },

  setUserPermissions: function() {
    var user=true;
    if(user === true) {
      this.perms.findProperty('perm','createSurvey').value = true;
      this.perms.findProperty('perm','editSurvey').value = true;
      this.perms.findProperty('perm','uploadSurveyZipData').value = true;
      this.perms.findProperty('perm','viewMessages').value = true;
      this.perms.findProperty('perm','publishSurvey').value = true;
      this.perms.findProperty('perm','mapData').value = true;
      this.perms.findProperty('perm','setDataPrivacy').value = true;
      this.perms.findProperty('perm','runReport').value = true;
    }

    if(user === true) {
      this.perms.findProperty('perm','createSurvey').value = true;
      this.perms.findProperty('perm','editSurvey').value = true;
      this.perms.findProperty('perm','uploadSurveyZipData').value = true;
      this.perms.findProperty('perm','importDataReport').value = true;
      this.perms.findProperty('perm','viewMessages').value = true;
      this.perms.findProperty('perm','publishSurvey').value = true;
      this.perms.findProperty('perm','mapData').value = true;
      this.perms.findProperty('perm','setDataPrivacy').value = true;
      this.perms.findProperty('perm','runReport').value = true;
      this.perms.findProperty('perm','editRawData').value = true;
      this.perms.findProperty('perm','deleteRawData').value = true;
      this.perms.findProperty('perm','approveSurvey').value = true;
    }

    if(user === true) {
      this.perms.findProperty('perm','createSurvey').value = true;
      this.perms.findProperty('perm','editSurvey').value = true;
      this.perms.findProperty('perm','uploadSurveyZipData').value = true;
      this.perms.findProperty('perm','importDataReport').value = true;
      this.perms.findProperty('perm','viewMessages').value = true;
      this.perms.findProperty('perm','publishSurvey').value = true;
      this.perms.findProperty('perm','mapData').value = true;
      this.perms.findProperty('perm','setDataPrivacy').value = true;
      this.perms.findProperty('perm','runReport').value = true;
      this.perms.findProperty('perm','editRawData').value = true;
      this.perms.findProperty('perm','deleteRawData').value = true;
      this.perms.findProperty('perm','approveSurvey').value = true;
      this.perms.findProperty('perm','deleteSurvey').value = true;
      this.perms.findProperty('perm','deleteSurveyGroup').value = true;
      this.perms.findProperty('perm','addUser').value = true;
      this.perms.findProperty('perm','editUser').value = true;
      this.perms.findProperty('perm','deleteUser').value = true;
    }

  },

  setCurrentPermissions:function () {
    this.perms.forEach(function(item){
      //this.set(item.perm,item.value);
    });
  }
});

FLOW.dialogControl = Ember.Object.create({
  delSG: "delSG",
  delS: "delS",
  delQG: "delQG",
  delQ: "delQ",
  delUser: "delUser",
  delAttr: "delAttr",
  delAssignment: "delAssignment",
  showDialog: false,
  message: null,
  header: null,
  activeView: null,
  activeAction: null,
  showOK: true,
  showCANCEL: true,

  confirm: function(event) {
    this.set('activeView', event.view);
    this.set('activeAction', event.context);
    this.set('showOK', true);
    this.set('showCANCEL', true);

    switch(this.get('activeAction')) {
    case "delSG":
      if(FLOW.surveyGroupControl.containsSurveys()) {
        this.set('activeAction', "ignore");
        this.set('header', Ember.String.loc('_sg_delete_not_possible_header'));
        this.set('message', Ember.String.loc('_sg_delete_not_possible_message'));
        this.set('showCANCEL', false);
        this.set('showDialog', true);
      } else {
        this.set('header', Ember.String.loc('_sg_delete_header'));
        this.set('message', Ember.String.loc('_this_cant_be_undo'));
        this.set('showDialog', true);
      }
      break;

    case "delS":
      this.set('header', Ember.String.loc('_s_delete_header'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    case "delQG":
      this.set('header', Ember.String.loc('_qg_delete_header'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    case "delQ":
      this.set('header', Ember.String.loc('_q_delete_header'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    case "delUser":
      this.set('header', Ember.String.loc('_are_you_sure_you_want_to_delete_this_user'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    case "delAttr":
      this.set('header', Ember.String.loc('_attr_delete_header'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    case "delAssignment":
      this.set('header', Ember.String.loc('_assignment_delete_header'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    default:
    }
  },

  doOK: function(event) {
    this.set('header', null);
    this.set('message', null);
    this.set('showCANCEL', true);
    this.set('showDialog', false);
    var view = this.get('activeView');
    switch(this.get('activeAction')) {
    case "delSG":
      view.deleteSurveyGroup.apply(view, arguments);
      break;

    case "delS":
      view.deleteSurvey.apply(view, arguments);
      break;

    case "delQG":
      view.deleteQuestionGroup.apply(view, arguments);
      break;

    case "delQ":
      this.set('showDialog', false);
      view.deleteQuestion.apply(view, arguments);
      break;

    case "delUser":
      this.set('showDialog', false);
      view.deleteUser.apply(view, arguments);
      break;

    case "delAttr":
      this.set('showDialog', false);
      view.deleteAttribute.apply(view, arguments);
      break;

    case "delAssignment":
      this.set('showDialog', false);
      view.deleteSurveyAssignment.apply(view, arguments);
      break;

    default:
    }
  },

  doCANCEL: function(event) {
    this.set('showDialog', false);
  }
});<commit_msg>add delete device group to dialogControl<commit_after>FLOW.permControl = Ember.Controller.create({
  perms: [],

  init: function() {
    this._super();
    this.initPermissions();
    this.setUserPermissions();
    this.setCurrentPermissions();
  },

  initPermissions: function() {
    this.perms.push(Ember.Object.create({
      perm: 'createSurvey',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'editSurvey',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'uploadSurveyZipData',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'importDataReport',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'viewMessages',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'publishSurvey',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'mapData',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'setDataPrivacy',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'editRawData',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'deleteRawData',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'runReport',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'deleteSurvey',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'deleteSurveyGroup',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'addUser',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'editUser',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'deleteUser',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'approveSurvey',
      value: false
    }));
    this.perms.push(Ember.Object.create({
      perm: 'editEditorialContent',
      value: false
    }));
  },

  setUserPermissions: function() {
    var user = true;
    if(user === true) {
      this.perms.findProperty('perm', 'createSurvey').value = true;
      this.perms.findProperty('perm', 'editSurvey').value = true;
      this.perms.findProperty('perm', 'uploadSurveyZipData').value = true;
      this.perms.findProperty('perm', 'viewMessages').value = true;
      this.perms.findProperty('perm', 'publishSurvey').value = true;
      this.perms.findProperty('perm', 'mapData').value = true;
      this.perms.findProperty('perm', 'setDataPrivacy').value = true;
      this.perms.findProperty('perm', 'runReport').value = true;
    }

    if(user === true) {
      this.perms.findProperty('perm', 'createSurvey').value = true;
      this.perms.findProperty('perm', 'editSurvey').value = true;
      this.perms.findProperty('perm', 'uploadSurveyZipData').value = true;
      this.perms.findProperty('perm', 'importDataReport').value = true;
      this.perms.findProperty('perm', 'viewMessages').value = true;
      this.perms.findProperty('perm', 'publishSurvey').value = true;
      this.perms.findProperty('perm', 'mapData').value = true;
      this.perms.findProperty('perm', 'setDataPrivacy').value = true;
      this.perms.findProperty('perm', 'runReport').value = true;
      this.perms.findProperty('perm', 'editRawData').value = true;
      this.perms.findProperty('perm', 'deleteRawData').value = true;
      this.perms.findProperty('perm', 'approveSurvey').value = true;
    }

    if(user === true) {
      this.perms.findProperty('perm', 'createSurvey').value = true;
      this.perms.findProperty('perm', 'editSurvey').value = true;
      this.perms.findProperty('perm', 'uploadSurveyZipData').value = true;
      this.perms.findProperty('perm', 'importDataReport').value = true;
      this.perms.findProperty('perm', 'viewMessages').value = true;
      this.perms.findProperty('perm', 'publishSurvey').value = true;
      this.perms.findProperty('perm', 'mapData').value = true;
      this.perms.findProperty('perm', 'setDataPrivacy').value = true;
      this.perms.findProperty('perm', 'runReport').value = true;
      this.perms.findProperty('perm', 'editRawData').value = true;
      this.perms.findProperty('perm', 'deleteRawData').value = true;
      this.perms.findProperty('perm', 'approveSurvey').value = true;
      this.perms.findProperty('perm', 'deleteSurvey').value = true;
      this.perms.findProperty('perm', 'deleteSurveyGroup').value = true;
      this.perms.findProperty('perm', 'addUser').value = true;
      this.perms.findProperty('perm', 'editUser').value = true;
      this.perms.findProperty('perm', 'deleteUser').value = true;
    }

  },

  setCurrentPermissions: function() {
    this.perms.forEach(function(item) {
      //this.set(item.perm,item.value);
    });
  }
});

FLOW.dialogControl = Ember.Object.create({
  delSG: "delSG",
  delS: "delS",
  delQG: "delQG",
  delQ: "delQ",
  delUser: "delUser",
  delAttr: "delAttr",
  delAssignment: "delAssignment",
  delDeviceGroup: "delDeviceGroup",
  showDialog: false,
  message: null,
  header: null,
  activeView: null,
  activeAction: null,
  showOK: true,
  showCANCEL: true,

  confirm: function(event) {
    this.set('activeView', event.view);
    this.set('activeAction', event.context);
    this.set('showOK', true);
    this.set('showCANCEL', true);

    switch(this.get('activeAction')) {
    case "delSG":
      if(FLOW.surveyGroupControl.containsSurveys()) {
        this.set('activeAction', "ignore");
        this.set('header', Ember.String.loc('_sg_delete_not_possible_header'));
        this.set('message', Ember.String.loc('_sg_delete_not_possible_message'));
        this.set('showCANCEL', false);
        this.set('showDialog', true);
      } else {
        this.set('header', Ember.String.loc('_sg_delete_header'));
        this.set('message', Ember.String.loc('_this_cant_be_undo'));
        this.set('showDialog', true);
      }
      break;

    case "delS":
      this.set('header', Ember.String.loc('_s_delete_header'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    case "delQG":
      this.set('header', Ember.String.loc('_qg_delete_header'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    case "delQ":
      this.set('header', Ember.String.loc('_q_delete_header'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    case "delUser":
      this.set('header', Ember.String.loc('_are_you_sure_you_want_to_delete_this_user'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    case "delAttr":
      this.set('header', Ember.String.loc('_attr_delete_header'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    case "delAssignment":
      this.set('header', Ember.String.loc('_assignment_delete_header'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    case "delDeviceGroup":
      this.set('header', Ember.String.loc('_device_group_delete_header'));
      this.set('message', Ember.String.loc('_this_cant_be_undo'));
      this.set('showDialog', true);
      break;

    default:
    }
  },

  doOK: function(event) {
    this.set('header', null);
    this.set('message', null);
    this.set('showCANCEL', true);
    this.set('showDialog', false);
    var view = this.get('activeView');
    switch(this.get('activeAction')) {
    case "delSG":
      view.deleteSurveyGroup.apply(view, arguments);
      break;

    case "delS":
      view.deleteSurvey.apply(view, arguments);
      break;

    case "delQG":
      view.deleteQuestionGroup.apply(view, arguments);
      break;

    case "delQ":
      this.set('showDialog', false);
      view.deleteQuestion.apply(view, arguments);
      break;

    case "delUser":
      this.set('showDialog', false);
      view.deleteUser.apply(view, arguments);
      break;

    case "delAttr":
      this.set('showDialog', false);
      view.deleteAttribute.apply(view, arguments);
      break;

    case "delAssignment":
      this.set('showDialog', false);
      view.deleteSurveyAssignment.apply(view, arguments);
      break;

    case "delDeviceGroup":
      this.set('showDialog', false);
      view.deleteDeviceGroup.apply(view, arguments);
      break;

    default:
    }
  },

  doCANCEL: function(event) {
    this.set('showDialog', false);
  }
});<|endoftext|>


<commit_before>(function () {
    angular
        .module('atlasDetail')
        .component('atlasDetail', {
            bindings: {
                endpoint: '@',
                isLoading: '='
            },
            templateUrl: 'modules/detail/components/detail/detail.html',
            controller: AtlasDetailController,
            controllerAs: 'vm'
        });

    AtlasDetailController.$inject = [
        '$scope',
        'store',
        'ACTIONS',
        'api',
        'endpointParser',
        'geometry',
        'geojson',
        'wgs84RdConverter'
    ];

    function AtlasDetailController (
        $scope,
        store,
        ACTIONS,
        api,
        endpointParser,
        geometry,
        geojson,
        wgs84RdConverter) {

        var vm = this;

        $scope.$watch('vm.endpoint', function (endpoint) {
            api.getByUrl(endpoint).then(function (data) {
                vm.apiData = {
                    results: data
                };

                vm.templateUrl = endpointParser.parseEndpoint(endpoint).templateUrl;

                geometry.getGeoJSON(endpoint).then(function (geometry) {
                    store.dispatch({
                        type: ACTIONS.SHOW_DETAIL,
                        payload: {
                            location: wgs84RdConverter.rdToWgs84(geojson.getCenter(geometry)),
                            highlight: geometry
                        }
                    });
                });
            });
        });
    }
})();<commit_msg>Merging with the new master, state variables are now consistent, and the geometry and panning works!<commit_after>(function () {
    angular
        .module('atlasDetail')
        .component('atlasDetail', {
            bindings: {
                endpoint: '@',
                isLoading: '='
            },
            templateUrl: 'modules/detail/components/detail/detail.html',
            controller: AtlasDetailController,
            controllerAs: 'vm'
        });

    AtlasDetailController.$inject = [
        '$scope',
        'store',
        'ACTIONS',
        'api',
        'endpointParser',
        'geometry',
        'geojson',
        'wgs84RdConverter'
    ];

    function AtlasDetailController (
        $scope,
        store,
        ACTIONS,
        api,
        endpointParser,
        geometry,
        geojson,
        wgs84RdConverter) {

        var vm = this;

        $scope.$watch('vm.endpoint', function (endpoint) {
            api.getByUrl(endpoint).then(function (data) {
                vm.apiData = {
                    results: data
                };

                vm.templateUrl = endpointParser.parseEndpoint(endpoint).templateUrl;

                geometry.getGeoJSON(endpoint).then(function (geometry) {
                    store.dispatch({
                        type: ACTIONS.SHOW_DETAIL,
                        payload: {
                            location: wgs84RdConverter.rdToWgs84(geojson.getCenter(geometry)),
                            geometry: geometry
                        }
                    });
                });
            });
        });
    }
})();<|endoftext|>


<commit_before>/**
 * ## Using Jasmine with node
 * The Jasmine node package contains helper code for developing and running Jasmine tests for node-based projects.
 */

/**
 * ### Install
 */

/**
 * You can install Jasmine using npm, locally in your project and globally to use the CLI tool.
 */

npm install jasmine

npm install -g jasmine

/**
 * ### Init a Project
 */

/**
 * Initialize a project for Jasmine by creating a spec directory and configuration json for you.
 */

jasmine init

/**
 * ### Generate examples
 */

/**
 * Generate example spec and source files
 */

jasmine examples

/**
 * ### Configuration
 */

/**
 * Customize `spec/support/jasmine.json` to enumerate the source files and spec files you would like the
 * Jasmine runner to include. You may use dir glob strings.
 *
 * `spec_dir` is used as a prefix for all `spec_files` and `helpers`.
 * Helpers are executed before specs.
 */

{
  // Spec directory path. Your spec_files must be relative to this path
  "spec_dir": "spec",
  // Array of filepaths (and globs) relative to spec_dir to include
  "spec_files": [
    "**/*[sS]pec.js"
  ],
  // Array of filepaths (and globs) relative spec_dir to include before jasmine specs
  "helpers": [
    "helpers/**/*.js"
  ],
  // Stop execution of a spec after the first expectation failure in it
  stopSpecOnExpectationFailure: false,
  // Run specs in semi-random order
  random: false
}

/**
 * ### Running tests
 */

/**
 * Once you have set up your `jasmine.json`, you can start Jasmine by running `jasmine` from the root of your project.
 *
 * Pass a relative path to a spec file to the jasmine command to only execute specs in a single file.
 */

jasmine

jasmine spec/appSpec.js

/**
 * #### CLI Options
 */

/**
 * `JASMINE_CONFIG_PATH=`
 * Specify a relative or absolute path to your configuration file. Can be used as an option or set as an environment variable.
 */

JASMINE_CONFIG_PATH=spec/config/jasmine.json jasmine

jasmine JASMINE_CONFIG_PATH=spec/config/jasmine.json

/**
 * `----no-color`
 * Turns off color in spec output
 */
jasmine --no-color

/**
 * `----filter=`
 * Only runs specs that match the given string
 */
jasmine --filter="a spec name"

/**
 * `----stop-on-failure=[true|false]`
 * Stops execution of a spec after the first expectation failure when set to `true`
 */
jasmine --stop-on-failure=true

/**
 * `----random=[true|false]`
 * Tells jasmine to run specs in semi random order or not for this run, overriding `jasmine.json`
 */
jasmine --random=true

/**
 * `----seed=`
 * Sets the randomization seed if randomization is turned on
 */
jasmine --seed=4321

/**
 * ### Using the library
 */

/**
 * If you want more granular control over the configuration, Jasmine can also be used as a library in your project.
 * This allows you to load multiple config files or control your configuration in different ways.
 */

var Jasmine = require('jasmine');
var jasmine = new Jasmine();

/**
 * #### Load configuration from a file or from an object.
 */

jasmine.loadConfigFile('spec/support/jasmine.json');

jasmine.loadConfig({
    spec_dir: 'spec',
    spec_files: [
        'appSpec.js',
        'requests/**/*[sS]pec.js',
        'utils/**/*[sS]pec.js'
    ],
    helpers: [
        'helpers/**/*.js'
    ]
});

/**
 * #### Custom onComplete
 */

/**
 * Optionally specify a custom `onComplete` callback. The callback is given a boolean of whether all of the specs passed or not. This is often used to message a status to task runners like grunt.
 */

jasmine.onComplete(function(passed) {
    if(passed) {
        console.log('All specs have passed');
    }
    else {
        console.log('At least one spec has failed');
    }
});

/**
 * #### Reporters
 */

/**
 * A ConsoleReporter is included if no other reporters are added.
 * You can configure the default reporter with `configureDefaultReporter`.
 * The default values are shown in the example.
 */

jasmine.configureDefaultReporter({
    /** The `timer` passed to the reporter will determine the mechanism for seeing how long the suite takes to run. */
    timer: new jasmine.jasmine.Timer(),
    /** The `print` function passed the reporter will be called to print its results. */
    print: function() {
        process.stdout.write(arguments);
    },
    /** `showColors` determines whether or not the reporter should use ANSI color codes. */
    showColors: true
});

/**
 * You can add a custom reporter with `addReporter`. If you add a reporter through `addReporter`, the default ConsoleReporter will not be added.
 * Multiple reporters can be added.
 */

var CustomReporter = require('./myCustomReporter');
var customReporter = new CustomReporter();

jasmine.addReporter(customReporter);

/**
 * #### Run the tests
 */

/**
 * Calling `execute` will run the specs.
 */

jasmine.execute();

/**
 * `execute` can optionally be called with a list of spec file paths to execute relative to your project root and a string to filter by spec name.
 */

jasmine.execute(['fooSpec.js'], 'a spec name');

/**
 * #### A simple example using the library
 */

var Jasmine = require('jasmine');
var jasmine = new Jasmine();

jasmine.loadConfigFile('spec/support/jasmine.json');
jasmine.configureDefaultReporter({
    showColors: false
});
jasmine.execute();
<commit_msg>Add missing quotes in json config example<commit_after>/**
 * ## Using Jasmine with node
 * The Jasmine node package contains helper code for developing and running Jasmine tests for node-based projects.
 */

/**
 * ### Install
 */

/**
 * You can install Jasmine using npm, locally in your project and globally to use the CLI tool.
 */

npm install jasmine

npm install -g jasmine

/**
 * ### Init a Project
 */

/**
 * Initialize a project for Jasmine by creating a spec directory and configuration json for you.
 */

jasmine init

/**
 * ### Generate examples
 */

/**
 * Generate example spec and source files
 */

jasmine examples

/**
 * ### Configuration
 */

/**
 * Customize `spec/support/jasmine.json` to enumerate the source files and spec files you would like the
 * Jasmine runner to include. You may use dir glob strings.
 *
 * `spec_dir` is used as a prefix for all `spec_files` and `helpers`.
 * Helpers are executed before specs.
 */

{
  // Spec directory path. Your spec_files must be relative to this path
  "spec_dir": "spec",
  // Array of filepaths (and globs) relative to spec_dir to include
  "spec_files": [
    "**/*[sS]pec.js"
  ],
  // Array of filepaths (and globs) relative spec_dir to include before jasmine specs
  "helpers": [
    "helpers/**/*.js"
  ],
  // Stop execution of a spec after the first expectation failure in it
  "stopSpecOnExpectationFailure": false,
  // Run specs in semi-random order
  "random": false
}

/**
 * ### Running tests
 */

/**
 * Once you have set up your `jasmine.json`, you can start Jasmine by running `jasmine` from the root of your project.
 *
 * Pass a relative path to a spec file to the jasmine command to only execute specs in a single file.
 */

jasmine

jasmine spec/appSpec.js

/**
 * #### CLI Options
 */

/**
 * `JASMINE_CONFIG_PATH=`
 * Specify a relative or absolute path to your configuration file. Can be used as an option or set as an environment variable.
 */

JASMINE_CONFIG_PATH=spec/config/jasmine.json jasmine

jasmine JASMINE_CONFIG_PATH=spec/config/jasmine.json

/**
 * `----no-color`
 * Turns off color in spec output
 */
jasmine --no-color

/**
 * `----filter=`
 * Only runs specs that match the given string
 */
jasmine --filter="a spec name"

/**
 * `----stop-on-failure=[true|false]`
 * Stops execution of a spec after the first expectation failure when set to `true`
 */
jasmine --stop-on-failure=true

/**
 * `----random=[true|false]`
 * Tells jasmine to run specs in semi random order or not for this run, overriding `jasmine.json`
 */
jasmine --random=true

/**
 * `----seed=`
 * Sets the randomization seed if randomization is turned on
 */
jasmine --seed=4321

/**
 * ### Using the library
 */

/**
 * If you want more granular control over the configuration, Jasmine can also be used as a library in your project.
 * This allows you to load multiple config files or control your configuration in different ways.
 */

var Jasmine = require('jasmine');
var jasmine = new Jasmine();

/**
 * #### Load configuration from a file or from an object.
 */

jasmine.loadConfigFile('spec/support/jasmine.json');

jasmine.loadConfig({
    spec_dir: 'spec',
    spec_files: [
        'appSpec.js',
        'requests/**/*[sS]pec.js',
        'utils/**/*[sS]pec.js'
    ],
    helpers: [
        'helpers/**/*.js'
    ]
});

/**
 * #### Custom onComplete
 */

/**
 * Optionally specify a custom `onComplete` callback. The callback is given a boolean of whether all of the specs passed or not. This is often used to message a status to task runners like grunt.
 */

jasmine.onComplete(function(passed) {
    if(passed) {
        console.log('All specs have passed');
    }
    else {
        console.log('At least one spec has failed');
    }
});

/**
 * #### Reporters
 */

/**
 * A ConsoleReporter is included if no other reporters are added.
 * You can configure the default reporter with `configureDefaultReporter`.
 * The default values are shown in the example.
 */

jasmine.configureDefaultReporter({
    /** The `timer` passed to the reporter will determine the mechanism for seeing how long the suite takes to run. */
    timer: new jasmine.jasmine.Timer(),
    /** The `print` function passed the reporter will be called to print its results. */
    print: function() {
        process.stdout.write(arguments);
    },
    /** `showColors` determines whether or not the reporter should use ANSI color codes. */
    showColors: true
});

/**
 * You can add a custom reporter with `addReporter`. If you add a reporter through `addReporter`, the default ConsoleReporter will not be added.
 * Multiple reporters can be added.
 */

var CustomReporter = require('./myCustomReporter');
var customReporter = new CustomReporter();

jasmine.addReporter(customReporter);

/**
 * #### Run the tests
 */

/**
 * Calling `execute` will run the specs.
 */

jasmine.execute();

/**
 * `execute` can optionally be called with a list of spec file paths to execute relative to your project root and a string to filter by spec name.
 */

jasmine.execute(['fooSpec.js'], 'a spec name');

/**
 * #### A simple example using the library
 */

var Jasmine = require('jasmine');
var jasmine = new Jasmine();

jasmine.loadConfigFile('spec/support/jasmine.json');
jasmine.configureDefaultReporter({
    showColors: false
});
jasmine.execute();
<|endoftext|>


<commit_before>"use strict";

var graph = {

    levels: [
                [
                    {
                        name: "Projection of a vector on a subspace",
                        chapterIndex: 1,
                        id: 1
                    }
                ],

                [
                    {
                        name: "Projection of a vector on a subspace",
                        chapterIndex: 1,
                        id: 4
                    },
                    {
                        name: "Projection of a vector on a subspace. In another's arms. In another's arms. In another's arms.",
                        chapterIndex: 1,
                        id: 4
                    }
                ],

                [
                    {
                        name: "Projection of a vector on a another",
                        chapterIndex: 1,
                        id: 4
                    }
                ]
    ],

    edges: [
        {
            from: 1,
            to: 4
        },
        {
            from: 4,
            to: 8
        }
    ]
};

var getForeignObjectAttrs = function(levelIndex, position, levelConceptCount, levelWidth, levelHeight) {

    var y = levelHeight;

    var nodePlusMarginWidth = levelWidth/levelConceptCount;
    var leftMarginWidth, rightMarginWidth;
    leftMarginWidth = rightMarginWidth = nodePlusMarginWidth*0.2;
    var nodeWidth = nodePlusMarginWidth - leftMarginWidth - rightMarginWidth;

    var x = leftMarginWidth + position*nodePlusMarginWidth;

    var attrs = {
        x: x,
        y: y,
        width: nodeWidth
    };

    return attrs;
};

var drawNode = function(node, levelIndex, position, levelConceptCount, svgAttrs, levelHeight) {
    console.log("Drawing ", node, " at level ", levelIndex, " in position", position);

    var svg = svgAttrs.svg;
    var svgWidth = svgAttrs.width;

    var chapterClass = "chapter-box-" + node.chapterIndex;
    var h4 = $("<h4>").html(node.name);
    var p = $("<p>").addClass("concept-box").addClass(chapterClass).append(h4);

    //foreignObject does not work on IE #todo. But my initial technical audience does not use IE, I guess?
    //And making aligning svg text is a pain
    var attrs = getForeignObjectAttrs(levelIndex, position, levelConceptCount, svgWidth, levelHeight);
    var f = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject')

    f = $(f).append(p).attr(attrs);
    svg.append(f);
    var height = p.height();
    f.attr("height", height); //for safari
    return height;
};

var drawLevelNodes = function(level, levelIndex, svgAttrs, levelHeight) {

    var i, length;
    length = level.length;

    var maxHeight = 0;
    var levelNodes = [];
    for(i = 0; i < length; i++) {
        var node = level[i];
        var height = drawNode(node, levelIndex, i, length, svgAttrs, levelHeight);
        maxHeight = _.max([maxHeight, height]);
    }
    return maxHeight;
};

var drawAllNodes = function(levels, svgAttrs) {

    var i, length;
    length = levels.length;
    var cumulativeLevelHeight = 30;
    for(i = 0 ; i < length; i++) {
        var level = levels[i];
        var maxLevelHeight = drawLevelNodes(level, i, svgAttrs, cumulativeLevelHeight);
        cumulativeLevelHeight = cumulativeLevelHeight + maxLevelHeight + 30;
    }
};

var drawAllEdges = function(svg) {

};

var drawLine = function(attrs, svg) {

    var ns = 'http://www.w3.org/2000/svg'
    var line = document.createElementNS(ns, 'line');

    //#todo -> add a python like iteritems function to utils
    _.each(_.keys(attrs), function(key) {
        line.setAttribute(key, attrs[key]);
    });

    svg.append(line);
};

var testDrawLine = function(svg) {

    var attrs = {
        x1: 100,
        y1: 50,
        x2: 100,
        y2: 500,
        stroke: "black",
        "stroke-width": 3,
        "marker-end": "url(#Triangle)"
    };
    drawLine(attrs, svg);
}

var init = function() {

    //cache the width so that it can used be in calculations without querying the dom each time.
    var svg = $("svg");

    svg.find("foreignObject").remove();
    svg.find("line").remove();

    var svgAttrs = {
        svg: svg,
        width: svg[0].offsetWidth
    };

    drawAllNodes(graph.levels, svgAttrs);
    //drawAllEdges(svg);

    testDrawLine(svg);
};

$(document).ready(init);

window.onresize = init;

<commit_msg>First hacky version of edges between concepts where one is directly below another.<commit_after>"use strict";

var app = {
    levelGap: 50,
    ns: 'http://www.w3.org/2000/svg'
}

var graph = {

    levels: [
                [
                    {
                        name: "Projection of a vector on another",
                        chapterIndex: 1,
                        id: 1
                    }
                ],

                [
                    {
                        name: "Projection of a vector on a subspace",
                        chapterIndex: 1,
                        id: 2
                    },
                    {
                        name: "Least Squares.",
                        chapterIndex: 1,
                        id: 3
                    }
                ],

                [
                    {
                        name: "Projection of a vector on a subspace",
                        chapterIndex: 1,
                        id: 4
                    },
                    {
                        name: "Least Squares.",
                        chapterIndex: 1,
                        id: 5
                    }
                ],

                [
                    {
                        name: "Gram Schmidt",
                        chapterIndex: 1,
                        id: 6
                    }
                ]
    ],

    edges: [
        {
            from: 2,
            to: 4
        },
        {
            from: 1,
            to: 6
        }
    ]
};

var drawArrowEntry = function(nodeAttrs, svg) {
    var circle = document.createElementNS(app.ns, 'circle');
    var attrs = {
        cx: nodeAttrs.arrowEntry_x,
        cy: nodeAttrs.arrowEntry_y,
        r: 5,
        fill: "red"
    };

    //#todo -> add a python like iteritems function to utils
    _.each(_.keys(attrs), function(key) {
        circle.setAttribute(key, attrs[key]);
    });
    console.log("Circle", attrs);
    svg.append(circle);
};

var getForeignObjectAttrs = function(levelIndex, position, levelConceptCount, levelWidth, levelHeight) {

    var y = levelHeight;

    var nodePlusMarginWidth = levelWidth/levelConceptCount;
    var leftMarginWidth, rightMarginWidth;
    leftMarginWidth = rightMarginWidth = nodePlusMarginWidth*0.2;
    var nodeWidth = nodePlusMarginWidth - leftMarginWidth - rightMarginWidth;

    var x = leftMarginWidth + position*nodePlusMarginWidth;

    var attrs = {
        x: x,
        y: y,
        width: nodeWidth
    };

    return attrs;
};

var drawNode = function(node, levelIndex, position, levelConceptCount, svgAttrs, levelHeight) {

    var svg = svgAttrs.svg;
    var svgWidth = svgAttrs.width;

    var chapterClass = "chapter-box-" + node.chapterIndex;
    var h4 = $("<h4>").html(node.name);
    var p = $("<p>").addClass("concept-box").addClass(chapterClass).append(h4);

    //foreignObject does not work on IE #todo. But my initial technical audience does not use IE, I guess?
    //And making aligning svg text is a pain
    var attrs = getForeignObjectAttrs(levelIndex, position, levelConceptCount, svgWidth, levelHeight);
    var f = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject')
    console.log("Node", attrs, node, " at level ", levelIndex, " in position", position);

    f = $(f).append(p).attr(attrs);
    svg.append(f);
    var height = p.height();
    f.attr("height", height); //for safari

    return {
        height: height,
        arrowEntry_x: Math.floor( (attrs.x*2 + attrs.width)/2 ),
        arrowEntry_y: Math.floor( (attrs.y - app.levelGap/2) ),
        x: attrs.x,
        y: attrs.y,
        node: f,
        levelIndex: levelIndex,
        levelConceptCount: levelConceptCount,
        levelPosition: position
    };
};

var drawLevelNodes = function(level, levelIndex, svgAttrs, levelHeight) {

    var i, length;
    length = level.length;

    var maxHeight = 0;
    var levelNodes = {};
    for(i = 0; i < length; i++) {
        var node = level[i];
        var nodeAttrs = drawNode(node, levelIndex, i, length, svgAttrs, levelHeight);
        levelNodes[node.id] = nodeAttrs
        var height = nodeAttrs.height;
        maxHeight = _.max([maxHeight, height]);
        drawArrowEntry(nodeAttrs, svgAttrs.svg);
    }
    console.log("Max height at level ", levelIndex, " is ", maxHeight);
    console.log("-----------------------   End of level ", levelIndex,  "-------------------------------");
    return {
        levelNodes: levelNodes,
        maxHeight: maxHeight
    };
};

var drawLine = function(attrs, svg) {

    var line = document.createElementNS(app.ns, 'line');

    //#todo -> add a python like iteritems function to utils
    _.each(_.keys(attrs), function(key) {
        line.setAttribute(key, attrs[key]);
    });

    svg.append(line);
};

var testDrawLine = function(svg) {

    var attrs = {
        x1: 100,
        y1: 50,
        x2: 100,
        y2: 500,
        stroke: "black",
        "stroke-width": 3,
        "marker-end": "url(#Triangle)"
    };
    drawLine(attrs, svg);
}

var isTwoDirectlyBelowOne = function(one, two) {
    return one.levelIndex === two.levelIndex - 1 &&
           one.levelConceptCount === two.levelConceptCount &&
           one.levelPosition === two.levelPosition;
}

var drawAllEdges = function(edges, allNodes, svg) {
    console.info("Edges: ", edges);

    var i, length;
    length = edges.length;
    for(i = 0; i < length; i++) {
        var edge = edges[i];
        var one = allNodes[edge.from];
        var two = allNodes[edge.to];

        if(isTwoDirectlyBelowOne(one, two)) {
            var lineAttrs = {
                x1: one.arrowEntry_x,
                y1: one.y + one.height,
                x2: two.arrowEntry_x,
                y2: two.y - 15,
                stroke: "black",
                "stroke-width": 3,
                "marker-end": "url(#Triangle)"
            };
            drawLine(lineAttrs, svg);
        };

    }
};

var drawAllNodes = function(levels, svgAttrs) {

    var i, length;
    length = levels.length;

    //#todo -> find a way to do dependency injection for levelGap instead of using global variable
    //As of now I'm trying to minimize usage of global variable by making a local copy
    //and using the local variable everywhere else in this function, at least.
    var levelGap = app.levelGap;
    var cumulativeLevelHeight = levelGap;

    var allNodes = {};
    for(i = 0 ; i < length; i++) {
        var level = levels[i];
        var levelNodesAndMaxHeight = drawLevelNodes(level, i, svgAttrs, cumulativeLevelHeight);
        _.extend(allNodes, levelNodesAndMaxHeight.levelNodes);
        var maxLevelHeight = levelNodesAndMaxHeight.maxHeight;
        cumulativeLevelHeight = cumulativeLevelHeight + maxLevelHeight + levelGap;
    };
    return allNodes;
};

var init = function() {

    //cache the width so that it can used be in calculations without querying the dom each time.
    var svg = $("svg");

    svg.find("foreignObject").remove();
    svg.find("line").remove();

    var svgAttrs = {
        svg: svg,
        width: svg[0].offsetWidth
    };

    app.allNodes = drawAllNodes(graph.levels, svgAttrs);
    drawAllEdges(graph.edges, app.allNodes, svg);

    //testDrawLine(svg);
};

$(document).ready(init);

//window.onresize = init;

<|endoftext|>


<commit_before>/*
 * jQuery doTimeout: Like setTimeout, but better! - v1.0 - 3/3/2010
 * http://benalman.com/projects/jquery-dotimeout-plugin/
 *
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
(function($){var a={},c="doTimeout",d=Array.prototype.slice;$[c]=function(){return b.apply(window,[0].concat(d.call(arguments)))};$.fn[c]=function(){var f=d.call(arguments),e=b.apply(this,[c+f[0]].concat(f));return typeof f[0]==="number"||typeof f[1]==="number"?this:e};function b(l){var m=this,h,k={},g=l?$.fn:$,n=arguments,i=4,f=n[1],j=n[2],p=n[3];if(typeof f!=="string"){i--;f=l=0;j=n[1];p=n[2]}if(l){h=m.eq(0);h.data(l,k=h.data(l)||{})}else{if(f){k=a[f]||(a[f]={})}}k.id&&clearTimeout(k.id);delete k.id;function e(){if(l){h.removeData(l)}else{if(f){delete a[f]}}}function o(){k.id=setTimeout(function(){k.fn()},j)}if(p){k.fn=function(q){if(typeof p==="string"){p=g[p]}p.apply(m,d.call(n,i))===true&&!q?o():e()};o()}else{if(k.fn){j===undefined?e():k.fn(j===false);return true}else{e()}}}})(jQuery);

// Making sure that console.log does not throw errors on Firefox + IE etc.
if (typeof console == "undefined") var console = { log: function() {} };

var DiscoJuice = {};

/*
 * Country codes available here http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
 */
DiscoJuice.Constants = {
	"Countries": {
		'AT': 'Austria',
		'CZ': 'Czech',
		'DK': 'Denmark',
		'FI': 'Finland',
		'FR': 'France',
		'DE': 'Germany',
		'GR': 'Greece',
		'HR': 'Croatia',
		'IE': 'Ireland',
		'IT': 'Italy',
		'IS': 'Iceland',
		'JP': 'Japan',
		'HU': 'Hungary',
		'LU': 'Luxembourg',
		'NL': 'Netherlands',
		'NO': 'Norway',
		'PL': 'Poland',
		'PT': 'Portugal',
		'SI': 'Slovenia',
		'ES': 'Spain',
		'SE': 'Sweden',
		'CH': 'Switzerland',
		'TR': 'Turkey',
		'US': 'USA',
		'GB': 'UK',
		'XX': 'Experimental'
	},
	"Flags": {
		'AT': 'at.png',
		'CZ': 'cz.png',
		'DK': 'dk.png',
		'FI': 'fi.png',
		'FR': 'fr.png',
		'DE': 'de.png',
		'GR': 'gr.png',
		'HR': 'hr.png',
		'IE': 'ie.png',
		'IT': 'it.png',
		'IS': 'is.png',
		'JP': 'jp.png',
		'HU': 'hu.png',
		'LU': 'lu.png',
		'NL': 'nl.png',
		'NO': 'no.png',
		'PL': 'pl.png',
		'PT': 'pt.png',
		'SI': 'si.png',
		'ES': 'es.png',
		'SE': 'se.png',
		'CH': 'ch.png',
		'TR': 'tr.png',
		'GB': 'gb.png',
		'US': 'us.png'
	}
};

DiscoJuice.Utils = {
	"log": function(string) {
		console.log(string);
		// opera.postError(string);
	},
	"options": function() {
		var options;
		return {
			"get": function (key, def) {
	//			DiscoJuice.log(options);
	//			DiscoJuice.log('Getting [' + key + '] default [' + def + '] val [' + options[key] + ']');
				if (!options) return def;
				if (!options[key]) return def;
				return options[key];
			},
			"set": function(opts) {
				options = opts;
			},
			"update": function(key, value) {
				options[key] = value;
			}
		}
	}(),

	/* Functions for setting, reading and erasing cookies */
	"createCookie": function(value, type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		var days = 1825;
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+escape(value)+expires+"; path=/";
	},
	"readCookie": function(type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		var days = 1825;
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return unescape(c.substring(nameEQ.length,c.length));
		}
		return null;
	},
	"eraseCookie": function (type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		DiscoJuice.createCookie(name,"",-1);
	},
	/* ------ ------ ------ ------ ------ */

	/*
	 * Performs a search 'term' against an entity.
	 * If no match, return false.
	 * If match return the keyword that matches.
	 */
	"searchMatch": function(item, term) {
		if (item.title.toLowerCase().search(term.toLowerCase()) !== -1) return true;
		if (item.descr && item.descr.toLowerCase().search(term.toLowerCase()) !== -1) return true;
		var key, i, keyword;

		if (item.keywords) {
			for(key in item.keywords) {
				keyword = item.keywords[key];
				for(i = 0; i < keyword.length; i++) {
					if (keyword[i].toLowerCase().search(term.toLowerCase()) !== -1) return keyword[i];
				}
			}
		}
		return false;
	},

	// calculate distance between two locations
	"calculateDistance": function (lat1, lon1, lat2, lon2) {
		var R = 6371; // km
		var dLat = this.toRad(lat2-lat1);
		var dLon = this.toRad(lon2-lon1);
		var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
				Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) *
				Math.sin(dLon/2) * Math.sin(dLon/2);
		var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
		var d = R * c;
		return d;
	},

	"toRad": function (deg) {
		return deg * Math.PI/180;
	},

	"waiter": function (completed, waitSeconds) {

		var
		 	my = {},
			parallellActions = [],
			executed = false;

		function execute () {

			if (executed) {
				console.log('Execution cancelled. Already performed.');
				return;
			}

			executed = true;
			completed(my);
		}

		function runAction (act, tooLate) {
			var
				thisAction = {completed: false};

			parallellActions.push(thisAction);
			console.log('Running action ' + parallellActions.length);
			act(function () {
				var i;
				thisAction.completed = true;
				for (i = 0; i < parallellActions.length; i++) {
					if (!parallellActions[i].completed) {
						console.log('Cannot execute because we are waiting for another action to complete.');
						return;
					}
				}
				if (executed) {
					if (my.allowMultiple) {
						console.log('Slow response; but executing anyway!!');
						execute();
					} else if (typeof tooLate === 'function') {
						console.log('All actions completed. Too late for executing...');
						tooLate();
					}
					return;
				}
				console.log('All actions completed. Executing!');
				execute();
			});

		}

		function startTimer() {
			if (parallellActions.length === 0) {
				console.log('Executing because no action is scheduled....');
				if (!executed) execute();
				return;
			}

			setTimeout(function() {
				console.log('Action timeout!');
				if (!executed) execute();
			}, waitSeconds);

		}

		my.allowMultiple = false;

		my.startTimer = startTimer;
		my.runAction = runAction;
		return my;
	}

};

/*
	Plugin for JQuery.
	*/
(function($) {
	$.fn.DiscoJuice = function(options) {
		return this.each(function() {
			DiscoJuice.Utils.options.set(options);

			DiscoJuice.Control.ui = DiscoJuice.UI;
			DiscoJuice.UI.control = DiscoJuice.Control;

			DiscoJuice.UI.enable(this);

		});
	};
})(jQuery);

<commit_msg>Adding Australia to DiscoJuice<commit_after>/*
 * jQuery doTimeout: Like setTimeout, but better! - v1.0 - 3/3/2010
 * http://benalman.com/projects/jquery-dotimeout-plugin/
 *
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
(function($){var a={},c="doTimeout",d=Array.prototype.slice;$[c]=function(){return b.apply(window,[0].concat(d.call(arguments)))};$.fn[c]=function(){var f=d.call(arguments),e=b.apply(this,[c+f[0]].concat(f));return typeof f[0]==="number"||typeof f[1]==="number"?this:e};function b(l){var m=this,h,k={},g=l?$.fn:$,n=arguments,i=4,f=n[1],j=n[2],p=n[3];if(typeof f!=="string"){i--;f=l=0;j=n[1];p=n[2]}if(l){h=m.eq(0);h.data(l,k=h.data(l)||{})}else{if(f){k=a[f]||(a[f]={})}}k.id&&clearTimeout(k.id);delete k.id;function e(){if(l){h.removeData(l)}else{if(f){delete a[f]}}}function o(){k.id=setTimeout(function(){k.fn()},j)}if(p){k.fn=function(q){if(typeof p==="string"){p=g[p]}p.apply(m,d.call(n,i))===true&&!q?o():e()};o()}else{if(k.fn){j===undefined?e():k.fn(j===false);return true}else{e()}}}})(jQuery);

// Making sure that console.log does not throw errors on Firefox + IE etc.
if (typeof console == "undefined") var console = { log: function() {} };

var DiscoJuice = {};

/*
 * Country codes available here http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
 */
DiscoJuice.Constants = {
	"Countries": {
		'AU': 'Australia',
		'AT': 'Austria',
		'CZ': 'Czech',
		'DK': 'Denmark',
		'FI': 'Finland',
		'FR': 'France',
		'DE': 'Germany',
		'GR': 'Greece',
		'HR': 'Croatia',
		'IE': 'Ireland',
		'IT': 'Italy',
		'IS': 'Iceland',
		'JP': 'Japan',
		'HU': 'Hungary',
		'LU': 'Luxembourg',
		'NL': 'Netherlands',
		'NO': 'Norway',
		'PL': 'Poland',
		'PT': 'Portugal',
		'SI': 'Slovenia',
		'ES': 'Spain',
		'SE': 'Sweden',
		'CH': 'Switzerland',
		'TR': 'Turkey',
		'US': 'USA',
		'GB': 'UK',
		'XX': 'Experimental'
	},
	"Flags": {
		'AU': 'au.png',
		'AT': 'at.png',
		'CZ': 'cz.png',
		'DK': 'dk.png',
		'FI': 'fi.png',
		'FR': 'fr.png',
		'DE': 'de.png',
		'GR': 'gr.png',
		'HR': 'hr.png',
		'IE': 'ie.png',
		'IT': 'it.png',
		'IS': 'is.png',
		'JP': 'jp.png',
		'HU': 'hu.png',
		'LU': 'lu.png',
		'NL': 'nl.png',
		'NO': 'no.png',
		'PL': 'pl.png',
		'PT': 'pt.png',
		'SI': 'si.png',
		'ES': 'es.png',
		'SE': 'se.png',
		'CH': 'ch.png',
		'TR': 'tr.png',
		'GB': 'gb.png',
		'US': 'us.png'
	}
};

DiscoJuice.Utils = {
	"log": function(string) {
		console.log(string);
		// opera.postError(string);
	},
	"options": function() {
		var options;
		return {
			"get": function (key, def) {
	//			DiscoJuice.log(options);
	//			DiscoJuice.log('Getting [' + key + '] default [' + def + '] val [' + options[key] + ']');
				if (!options) return def;
				if (!options[key]) return def;
				return options[key];
			},
			"set": function(opts) {
				options = opts;
			},
			"update": function(key, value) {
				options[key] = value;
			}
		}
	}(),

	/* Functions for setting, reading and erasing cookies */
	"createCookie": function(value, type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		var days = 1825;
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+escape(value)+expires+"; path=/";
	},
	"readCookie": function(type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		var days = 1825;
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return unescape(c.substring(nameEQ.length,c.length));
		}
		return null;
	},
	"eraseCookie": function (type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		DiscoJuice.createCookie(name,"",-1);
	},
	/* ------ ------ ------ ------ ------ */

	/*
	 * Performs a search 'term' against an entity.
	 * If no match, return false.
	 * If match return the keyword that matches.
	 */
	"searchMatch": function(item, term) {
		if (item.title.toLowerCase().search(term.toLowerCase()) !== -1) return true;
		if (item.descr && item.descr.toLowerCase().search(term.toLowerCase()) !== -1) return true;
		var key, i, keyword;

		if (item.keywords) {
			for(key in item.keywords) {
				keyword = item.keywords[key];
				for(i = 0; i < keyword.length; i++) {
					if (keyword[i].toLowerCase().search(term.toLowerCase()) !== -1) return keyword[i];
				}
			}
		}
		return false;
	},

	// calculate distance between two locations
	"calculateDistance": function (lat1, lon1, lat2, lon2) {
		var R = 6371; // km
		var dLat = this.toRad(lat2-lat1);
		var dLon = this.toRad(lon2-lon1);
		var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
				Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) *
				Math.sin(dLon/2) * Math.sin(dLon/2);
		var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
		var d = R * c;
		return d;
	},

	"toRad": function (deg) {
		return deg * Math.PI/180;
	},

	"waiter": function (completed, waitSeconds) {

		var
		 	my = {},
			parallellActions = [],
			executed = false;

		function execute () {

			if (executed) {
				console.log('Execution cancelled. Already performed.');
				return;
			}

			executed = true;
			completed(my);
		}

		function runAction (act, tooLate) {
			var
				thisAction = {completed: false};

			parallellActions.push(thisAction);
			console.log('Running action ' + parallellActions.length);
			act(function () {
				var i;
				thisAction.completed = true;
				for (i = 0; i < parallellActions.length; i++) {
					if (!parallellActions[i].completed) {
						console.log('Cannot execute because we are waiting for another action to complete.');
						return;
					}
				}
				if (executed) {
					if (my.allowMultiple) {
						console.log('Slow response; but executing anyway!!');
						execute();
					} else if (typeof tooLate === 'function') {
						console.log('All actions completed. Too late for executing...');
						tooLate();
					}
					return;
				}
				console.log('All actions completed. Executing!');
				execute();
			});

		}

		function startTimer() {
			if (parallellActions.length === 0) {
				console.log('Executing because no action is scheduled....');
				if (!executed) execute();
				return;
			}

			setTimeout(function() {
				console.log('Action timeout!');
				if (!executed) execute();
			}, waitSeconds);

		}

		my.allowMultiple = false;

		my.startTimer = startTimer;
		my.runAction = runAction;
		return my;
	}

};

/*
	Plugin for JQuery.
	*/
(function($) {
	$.fn.DiscoJuice = function(options) {
		return this.each(function() {
			DiscoJuice.Utils.options.set(options);

			DiscoJuice.Control.ui = DiscoJuice.UI;
			DiscoJuice.UI.control = DiscoJuice.Control;

			DiscoJuice.UI.enable(this);

		});
	};
})(jQuery);

<|endoftext|>


<commit_before>/*
 * jQuery doTimeout: Like setTimeout, but better! - v1.0 - 3/3/2010
 * http://benalman.com/projects/jquery-dotimeout-plugin/
 *
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
(function($){var a={},c="doTimeout",d=Array.prototype.slice;$[c]=function(){return b.apply(window,[0].concat(d.call(arguments)))};$.fn[c]=function(){var f=d.call(arguments),e=b.apply(this,[c+f[0]].concat(f));return typeof f[0]==="number"||typeof f[1]==="number"?this:e};function b(l){var m=this,h,k={},g=l?$.fn:$,n=arguments,i=4,f=n[1],j=n[2],p=n[3];if(typeof f!=="string"){i--;f=l=0;j=n[1];p=n[2]}if(l){h=m.eq(0);h.data(l,k=h.data(l)||{})}else{if(f){k=a[f]||(a[f]={})}}k.id&&clearTimeout(k.id);delete k.id;function e(){if(l){h.removeData(l)}else{if(f){delete a[f]}}}function o(){k.id=setTimeout(function(){k.fn()},j)}if(p){k.fn=function(q){if(typeof p==="string"){p=g[p]}p.apply(m,d.call(n,i))===true&&!q?o():e()};o()}else{if(k.fn){j===undefined?e():k.fn(j===false);return true}else{e()}}}})(jQuery);

// Making sure that console.log does not throw errors on Firefox + IE etc.
if (typeof console == "undefined") var console = { log: function() {} };

var DiscoJuice = {};

/*
 * Country codes available here http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
 */
DiscoJuice.Constants = {
	"Countries": {
		'CZ': 'Czech',
		'DK': 'Denmark',
		'FI': 'Finland',
		'FR': 'France',
		'DE': 'Germany',
		'GR': 'Greece',
		'HR': 'Croatia',
		'IE': 'Ireland',
		'IT': 'Italy',
		'JP': 'Japan',
		'HU': 'Hungary',
		'LU': 'Luxembourg',
		'NL': 'Netherlands',
		'NO': 'Norway',
		'PL': 'Poland',
		'PT': 'Portugal',
		'SI': 'Slovenia',
		'ES': 'Spain',
		'SE': 'Sweden',
		'CH': 'Switzerland',
		'TR': 'Turkey',
		'US': 'USA',
		'GB': 'UK',
		'XX': 'Experimental'
	},
	"Flags": {
		'CZ': 'cz.png',
		'DK': 'dk.png',
		'FI': 'fi.png',
		'FR': 'fr.png',
		'DE': 'de.png',
		'GR': 'gr.png',
		'HR': 'hr.png',
		'IE': 'ie.png',
		'IT': 'it.png',
		'JP': 'jp.png',
		'HU': 'hu.png',
		'LU': 'lu.png',
		'NL': 'nl.png',
		'NO': 'no.png',
		'PL': 'pl.png',
		'PT': 'pt.png',
		'SI': 'si.png',
		'ES': 'es.png',
		'SE': 'se.png',
		'CH': 'ch.png',
		'TR': 'tr.png',
		'GB': 'gb.png',
		'US': 'us.png'
	}
};

DiscoJuice.Utils = {
	"log": function(string) {
		console.log(string);
		// opera.postError(string);
	},
	"options": function() {
		var options;
		return {
			"get": function (key, def) {
	//			DiscoJuice.log(options);
	//			DiscoJuice.log('Getting [' + key + '] default [' + def + '] val [' + options[key] + ']');
				if (!options) return def;
				if (!options[key]) return def;
				return options[key];
			},
			"set": function(opts) {
				options = opts;
			},
			"update": function(key, value) {
				options[key] = value;
			}
		}
	}(),

	/* Functions for setting, reading and erasing cookies */
	"createCookie": function(value, type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		var days = 1825;
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+escape(value)+expires+"; path=/";
	},
	"readCookie": function(type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		var days = 1825;
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return unescape(c.substring(nameEQ.length,c.length));
		}
		return null;
	},
	"eraseCookie": function (type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		DiscoJuice.createCookie(name,"",-1);
	},
	/* ------ ------ ------ ------ ------ */

	/*
	 * Performs a search 'term' against an entity.
	 * If no match, return false.
	 * If match return the keyword that matches.
	 */
	"searchMatch": function(item, term) {
		if (item.title.toLowerCase().search(term.toLowerCase()) !== -1) return true;
		if (item.descr && item.descr.toLowerCase().search(term.toLowerCase()) !== -1) return true;
		var key, i, keyword;

		if (item.keywords) {
			for(key in item.keywords) {
				keyword = item.keywords[key];
				for(i = 0; i < keyword.length; i++) {
					if (keyword[i].toLowerCase().search(term.toLowerCase()) !== -1) return keyword[i];
				}
			}
		}
		return false;
	},

	// calculate distance between two locations
	"calculateDistance": function (lat1, lon1, lat2, lon2) {
		var R = 6371; // km
		var dLat = this.toRad(lat2-lat1);
		var dLon = this.toRad(lon2-lon1);
		var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
				Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) *
				Math.sin(dLon/2) * Math.sin(dLon/2);
		var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
		var d = R * c;
		return d;
	},

	"toRad": function (deg) {
		return deg * Math.PI/180;
	}

};

/*
	Plugin for JQuery.
	*/
(function($) {
	$.fn.DiscoJuice = function(options) {
		return this.each(function() {
			DiscoJuice.Utils.options.set(options);

			DiscoJuice.Control.ui = DiscoJuice.UI;
			DiscoJuice.UI.control = DiscoJuice.Control;

			DiscoJuice.UI.enable(this);

		});
	};
})(jQuery);

<commit_msg>adding a helper function for parallell processing<commit_after>/*
 * jQuery doTimeout: Like setTimeout, but better! - v1.0 - 3/3/2010
 * http://benalman.com/projects/jquery-dotimeout-plugin/
 *
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */
(function($){var a={},c="doTimeout",d=Array.prototype.slice;$[c]=function(){return b.apply(window,[0].concat(d.call(arguments)))};$.fn[c]=function(){var f=d.call(arguments),e=b.apply(this,[c+f[0]].concat(f));return typeof f[0]==="number"||typeof f[1]==="number"?this:e};function b(l){var m=this,h,k={},g=l?$.fn:$,n=arguments,i=4,f=n[1],j=n[2],p=n[3];if(typeof f!=="string"){i--;f=l=0;j=n[1];p=n[2]}if(l){h=m.eq(0);h.data(l,k=h.data(l)||{})}else{if(f){k=a[f]||(a[f]={})}}k.id&&clearTimeout(k.id);delete k.id;function e(){if(l){h.removeData(l)}else{if(f){delete a[f]}}}function o(){k.id=setTimeout(function(){k.fn()},j)}if(p){k.fn=function(q){if(typeof p==="string"){p=g[p]}p.apply(m,d.call(n,i))===true&&!q?o():e()};o()}else{if(k.fn){j===undefined?e():k.fn(j===false);return true}else{e()}}}})(jQuery);

// Making sure that console.log does not throw errors on Firefox + IE etc.
if (typeof console == "undefined") var console = { log: function() {} };

var DiscoJuice = {};

/*
 * Country codes available here http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
 */
DiscoJuice.Constants = {
	"Countries": {
		'CZ': 'Czech',
		'DK': 'Denmark',
		'FI': 'Finland',
		'FR': 'France',
		'DE': 'Germany',
		'GR': 'Greece',
		'HR': 'Croatia',
		'IE': 'Ireland',
		'IT': 'Italy',
		'JP': 'Japan',
		'HU': 'Hungary',
		'LU': 'Luxembourg',
		'NL': 'Netherlands',
		'NO': 'Norway',
		'PL': 'Poland',
		'PT': 'Portugal',
		'SI': 'Slovenia',
		'ES': 'Spain',
		'SE': 'Sweden',
		'CH': 'Switzerland',
		'TR': 'Turkey',
		'US': 'USA',
		'GB': 'UK',
		'XX': 'Experimental'
	},
	"Flags": {
		'CZ': 'cz.png',
		'DK': 'dk.png',
		'FI': 'fi.png',
		'FR': 'fr.png',
		'DE': 'de.png',
		'GR': 'gr.png',
		'HR': 'hr.png',
		'IE': 'ie.png',
		'IT': 'it.png',
		'JP': 'jp.png',
		'HU': 'hu.png',
		'LU': 'lu.png',
		'NL': 'nl.png',
		'NO': 'no.png',
		'PL': 'pl.png',
		'PT': 'pt.png',
		'SI': 'si.png',
		'ES': 'es.png',
		'SE': 'se.png',
		'CH': 'ch.png',
		'TR': 'tr.png',
		'GB': 'gb.png',
		'US': 'us.png'
	}
};

DiscoJuice.Utils = {
	"log": function(string) {
		console.log(string);
		// opera.postError(string);
	},
	"options": function() {
		var options;
		return {
			"get": function (key, def) {
	//			DiscoJuice.log(options);
	//			DiscoJuice.log('Getting [' + key + '] default [' + def + '] val [' + options[key] + ']');
				if (!options) return def;
				if (!options[key]) return def;
				return options[key];
			},
			"set": function(opts) {
				options = opts;
			},
			"update": function(key, value) {
				options[key] = value;
			}
		}
	}(),

	/* Functions for setting, reading and erasing cookies */
	"createCookie": function(value, type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		var days = 1825;
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+escape(value)+expires+"; path=/";
	},
	"readCookie": function(type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		var days = 1825;
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return unescape(c.substring(nameEQ.length,c.length));
		}
		return null;
	},
	"eraseCookie": function (type) {
		var type = type || 'EntityID';
		var name = '_DiscoJuice_' + type;
		DiscoJuice.createCookie(name,"",-1);
	},
	/* ------ ------ ------ ------ ------ */

	/*
	 * Performs a search 'term' against an entity.
	 * If no match, return false.
	 * If match return the keyword that matches.
	 */
	"searchMatch": function(item, term) {
		if (item.title.toLowerCase().search(term.toLowerCase()) !== -1) return true;
		if (item.descr && item.descr.toLowerCase().search(term.toLowerCase()) !== -1) return true;
		var key, i, keyword;

		if (item.keywords) {
			for(key in item.keywords) {
				keyword = item.keywords[key];
				for(i = 0; i < keyword.length; i++) {
					if (keyword[i].toLowerCase().search(term.toLowerCase()) !== -1) return keyword[i];
				}
			}
		}
		return false;
	},

	// calculate distance between two locations
	"calculateDistance": function (lat1, lon1, lat2, lon2) {
		var R = 6371; // km
		var dLat = this.toRad(lat2-lat1);
		var dLon = this.toRad(lon2-lon1);
		var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
				Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) *
				Math.sin(dLon/2) * Math.sin(dLon/2);
		var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
		var d = R * c;
		return d;
	},

	"toRad": function (deg) {
		return deg * Math.PI/180;
	},

	"waiter": function (completed, waitSeconds) {

		var
		 	my = {},
			parallellActions = [],
			executed = false;

		function execute () {

			if (executed) {
				console.log('Execution cancelled. Already performed.');
				return;
			}

			executed = true;
			completed(my);
		}

		function runAction (act, tooLate) {
			var
				thisAction = {completed: false};

			parallellActions.push(thisAction);
			console.log('Running action ' + parallellActions.length);
			act(function () {
				var i;
				thisAction.completed = true;
				for (i = 0; i < parallellActions.length; i++) {
					if (!parallellActions[i].completed) {
						console.log('Cannot execute because we are waiting for another action to complete.');
						return;
					}
				}
				if (executed) {
					console.log('All actions completed. Too late for executing...');
					tooLate();
					return;
				}
				console.log('All actions completed. Executing!');
				execute();
			});

		}

		function startTimer() {
			setTimeout(function() {
				console.log('Action timeout!');
				if (!executed) execute();
			}, waitSeconds);
		}

		my.startTimer = startTimer;
		my.runAction = runAction;
		return my;
	}

};

/*
	Plugin for JQuery.
	*/
(function($) {
	$.fn.DiscoJuice = function(options) {
		return this.each(function() {
			DiscoJuice.Utils.options.set(options);

			DiscoJuice.Control.ui = DiscoJuice.UI;
			DiscoJuice.UI.control = DiscoJuice.Control;

			DiscoJuice.UI.enable(this);

		});
	};
})(jQuery);

<|endoftext|>


<commit_before>var version = '1.2.1';

var decodeEntities = (function() {
    // this prevents any overhead from creating the object each time
    var element = document.createElement('div');

    function decodeHTMLEntities (str) {
        if(str && typeof str === 'string') {
            // strip script/html tags
            str = str.replace(/