<commit_before>import * as utils from 'utils'
import Model from 'model'
import Component from 'component'
import Layout from 'layout'
import { DefaultEvent } from 'events'
import { warn as warnIcon } from 'iconset'
import Promise from 'base/promise';

var class_loading_first = 'vzb-loading-first';
var class_loading_data = 'vzb-loading-data';
var class_loading_error = 'vzb-loading-error';
var class_placeholder = 'vzb-placeholder';
var class_buttons_off = 'vzb-buttonlist-off';

var templates = {};
var toolsList = {};
//tool model is quite simple and doesn't need to be registered
var ToolModel = Model.extend({
  /**
   * Initializes the tool model.
   * @param {Object} values The initial values of this model
   * @param {Object} binds contains initial bindings for the model
   * @param {Function|Array} validade validate rules
   */
  init: function(name, values, defaults, binds, validate) {
    this._id = utils.uniqueId('tm');
    this._type = 'tool';
    //generate validation function
    this.validate = generateValidate(this, validate);
    //default submodels
    values = values || {};
    defaults = defaults || {};
    values = defaultModel(values, defaults);
    //constructor is similar to model
    this.default_model = defaults;
    this._super(name, values, null, binds);
  }
});
//tool
var Tool = Component.extend({
  /**
   * Initializes the tool
   * @param {Object} placeholder object
   * @param {Object} external_model External model such as state, data, etc
   */
  init: function(placeholder, external_model) {
    this._id = utils.uniqueId('t');
    this.template = this.template ||
      '
' +
        '
' +
          '
' +
          '
' +
          '
' +
          '
' +
        '
' +
        '
' +
          '
' +
          '
' +
          '
' +
          '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
      '
';
    this.model_binds = this.model_binds || {};

    external_model = external_model || {}; //external model can be undefined
    external_model.bind = external_model.bind || {}; //bind functions can be undefined

    //bind the validation function with the tool
    var validate = this.validate.bind(this);
    var _this = this;

    // callbacks has to be an array so that it will not be turned into a submodel when the toolmodel is made.
    var callbacks = {
      'change': function(evt, path) {
        if(_this._ready) {
          _this.model.validate();

          if (evt.source.persistent)
            _this.model.trigger(new DefaultEvent(evt.source, 'persistentChange'), _this.getPersistentModel());
        }
      },
      'hook_change': function() {
        if (!_this.model.state.time.splash) { // not block when it initial splash screen
          _this.beforeLoading(true);
        }
      },
      'change:ui.presentation': function() {
        _this.layout.updatePresentation();
        _this.trigger('resize');
      },
      'translate:language': function() {
        _this.translateStrings();
      },
      'load_start': function() {
        _this.beforeLoading();
      },
      'ready': function(evt) {
        if(_this._ready) {
          _this.afterLoading();
        }
      }
    };
    utils.extend(callbacks, this.model_binds, external_model.bind);
    delete external_model.bind;

    this.model = new ToolModel(this.name, external_model, this.default_model, callbacks, validate);

    // default model is the model set in the tool
    this.default_model = utils.deepClone(this.default_model) || {};

    this.ui = this.model.ui || {};

    this.layout = new Layout(this.ui);
    //splash
    this.ui.splash = this.model && this.model.data && this.model.data.splash;
    this._super({
      name: this.name || this._id,
      placeholder: placeholder
    }, this);

    this.render();
    this._setUIModel();
  },

  ready: function(){
    this.checkTimeLimits();
  },

  checkTimeLimits: function() {
    if(!this.model.state.time) return;

    var time = this.model.state.time;

    if(this.model.state.marker) {
      var tLimits = this.model.state.marker.getTimeLimits(time.getDimension());

      if(!tLimits || !utils.isDate(tLimits.min) || !utils.isDate(tLimits.max))
          return utils.warn("checkTimeLimits(): min-max look wrong: " + tLimits.min + " " + tLimits.max + ". Expecting Date objects");

      // change start and end (but keep startOrigin and endOrigin for furhter requests)
      var newTime = {}
      if(time.start - tLimits.min != 0) newTime['start'] = d3.max([tLimits.min, time.parseToUnit(time.startOrigin)]);
      if(time.end - tLimits.max != 0) newTime['end'] = d3.min([tLimits.max, time.parseToUnit(time.endOrigin)]);
      if(time.value == null) newTime['value'] = time.parseToUnit(time.format(new Date())); // default to current date. Other option: newTime['start'] || newTime['end'] || time.start || time.end;

      time.set(newTime, false, false);
    }

    //force time validation because time.value might now fall outside of start-end
    time.validate();
  },

  getDefaultModel: function() {
    var defaultToolModel = this.default_model;
    var defaultsFromModels = this.model.getDefaults();
    var result = utils.deepExtend({}, defaultsFromModels, defaultToolModel);
    return result;
  },

  getPersistentModel: function() {
    //try to find functions in properties of model.
    var removeFunctions = function(model) {
      for(var childKey in model) {
        if(typeof model[childKey] === 'function') {
          delete model[childKey];
          utils.warn('minModel validation. Function found in enumerable properties of ' + childKey + ". This key is deleted from minModel");
        }
        else if(typeof model[childKey] === 'object')
          removeFunctions(model[childKey]);
      }
    }

    var currentToolModel = this.model.getPlainObject(true); // true = get only persistent model values
    var result = utils.flattenDates(currentToolModel, this.model.state.time.timeFormat);

    removeFunctions(result);
    return result;
  },

  getPersistentMinimalModel: function(diffModel) {
    var defaultModel = this.getDefaultModel();
    var currentPersistentModel = this.getPersistentModel();
    var redundantModel = Vizabi.utils.deepExtend(defaultModel, diffModel);
    return Vizabi.utils.diffObject(currentPersistentModel, redundantModel);
  },

  /**
   * Clears a tool
   */

  clear: function() {
    this.layout.clear();
    this.setModel = this.getModel = function() {
      return;
    };
    this._super();
  },

  /**
   * Visually display errors
   */
  error: function(opts) {

    var msg = (opts && opts.type === "data") ? "Error loading chart data.
Please, try again soon." : "Error loading chart";

    this.placeholder.innerHTML = '

'+warnIcon+'

'+msg+'

';
  },

  /**
   * Sets model from external page
   * @param {Object} JSONModel new model in JSON format
   * @param {Boolean} overwrite overwrite everything instead of extending
   */
  setModel: function(newModelJSON, overwrite) {
    if(overwrite) {
      this.model.reset(newModelJSON);
    } else {
      this.model.set(newModelJSON);
    }
    this._setUIModel();
  },

  /**
   * get model
   * @return {Object} JSON object of model
   */
  getModel: function() {
    return this.model.getPlainObject() || {};
  },
  /**
   * Displays loading class
   */
  beforeLoading: function(loadingData) {
    if(!this._readyOnce) {
        utils.addClass(this.placeholder, class_loading_first);
    }
    if(loadingData) {
        utils.addClass(this.placeholder, class_loading_data);
    }
  },
  /**
   * Removes loading class
   */
  afterLoading: function() {
    utils.removeClass(this.placeholder, class_loading_first);
    utils.removeClass(this.placeholder, class_loading_data);
  },
  /**
   * Adds loading error class
   */
  errorLoading: function() {
    utils.addClass(this.placeholder, class_loading_error);
  },
  /* ==========================
   * Validation and query
   * ==========================
   */
  /**
   * Validating the tool model
   * @param model the current tool model to be validated
   */
  validate: function(model) {
    model = this.model || model;

    if(!model || !model.state) return utils.warn("tool validation aborted: model.state looks wrong: " + model);
  },

  _setUIModel: function() {
    //add placeholder class
    utils.addClass(this.placeholder, class_placeholder);
    //add-remove buttonlist class
    if(!this.ui || !this.ui.buttons || !this.ui.buttons.length) {
      utils.addClass(this.element, class_buttons_off);
    } else {
      utils.removeClass(this.element, class_buttons_off);
    }
  },

  preloadLanguage: function() {
    return Promise.resolve();
  }
});

/* ==========================
 * Validation methods
 * ==========================
 */

/**
 * Generates a validation function based on specific model validation
 * @param {Object} m model
 * @param {Function} validate validation function
 * @returns {Function} validation
 */
function generateValidate(m, validate) {
  var max = 10;

  function validate_func() {
    var model = JSON.stringify(m.getPlainObject());
    var c = arguments[0] || 0;
    //TODO: remove validation hotfix
    //while setting this.model is not available
    if(!this._readyOnce) {
      validate(this);
    } else {
      validate();
    }
    var model2 = JSON.stringify(m.getPlainObject());
    if(c >= max) {
      utils.error('Max validation loop.');
    } else if(model !== model2) {
      validate_func.call(this, [c += 1]);
    }
  }

  return validate_func;
}

/* ==========================
 * Default model methods
 * ==========================
 */

/**
 * Generates a valid state based on default model
 */
function defaultModel(values, defaults) {
  var keys = Object.keys(defaults);
  var size = keys.length;
  var field;
  var blueprint;
  var original;
  var type;
  for(var i = 0; i < size; i += 1) {
    field = keys[i];
    if(field === '_defs_') {
      continue;
    }
    blueprint = defaults[field];
    original = values[field];
    type = typeof blueprint;
    if(type === 'object') {
      type = utils.isPlainObject(blueprint) && blueprint._defs_ ? 'object' : utils.isArray(blueprint) ? 'array' :
        'model';
    }
    if(typeof original === 'undefined') {
      if(type !== 'object' && type !== 'model') {
        values[field] = blueprint;
      } else {
        values[field] = defaultModel({}, blueprint);
      }
    }
    original = values[field];
    if(type === 'number' && isNaN(original)) {
      values[field] = 0;
    } else if(type === 'string' && typeof original !== 'string') {
      values[field] = '';
    } else if(type === 'array' && !utils.isArray(original)) {
      values[field] = [];
    } else if(type === 'model') {
      if(!utils.isObject(original)) {
        values[field] = {};
      }
      values[field] = defaultModel(values[field], blueprint);
    } else if(type === 'object') {
      if(!utils.isObject(original) || Object.keys(original).length === 0) {
        original = false; //will be overwritten
      }
      if(!utils.isObject(blueprint._defs_)) {
        blueprint._defs_ = {};
      }
      values[field] = original || blueprint._defs_;
    }
  }
  return values;
}

/**
 * Outputs the difference between two objects
 * @param {Object} obj prevailing object
 * @param {Object} compare comparison object
 * @returns {Object} resulting diff object
 */
function changedObj(obj, compare) {
  var acc = {};
  utils.forEach(obj, function(val, name) {
    if(!(name in compare)) {
      acc[name] = val;
      return true;
    }
    //if the same, no need to check deeper
    if(JSON.stringify(val) === JSON.stringify(compare[name])) return true;
    else if(utils.isArray(val)) {
      acc[name] = val;
    } else if(utils.isObject(val)) {
      acc[name] = changedObj(val, compare[name]);
    } else if(utils.isDate(compare[name])) {
      var comp1 = val.toString();
      //TODO: workaround for years only
      var comp2 = compare[name].getUTCFullYear().toString();
      if(comp1 !== comp2) {
        acc[name] = val;
      }
    } else {
      acc[name] = val;
    }
  });
  return acc;
}

//utility function to check if a component is a tool
//TODO: Move to utils?
Tool.isTool = function(c) {
  return c._id && c._id[0] === 't';
};

export default Tool;
<commit_msg>Fix the syntax for using the utils<commit_after>import * as utils from 'utils'
import Model from 'model'
import Component from 'component'
import Layout from 'layout'
import { DefaultEvent } from 'events'
import { warn as warnIcon } from 'iconset'
import Promise from 'base/promise';

var class_loading_first = 'vzb-loading-first';
var class_loading_data = 'vzb-loading-data';
var class_loading_error = 'vzb-loading-error';
var class_placeholder = 'vzb-placeholder';
var class_buttons_off = 'vzb-buttonlist-off';

var templates = {};
var toolsList = {};
//tool model is quite simple and doesn't need to be registered
var ToolModel = Model.extend({
  /**
   * Initializes the tool model.
   * @param {Object} values The initial values of this model
   * @param {Object} binds contains initial bindings for the model
   * @param {Function|Array} validade validate rules
   */
  init: function(name, values, defaults, binds, validate) {
    this._id = utils.uniqueId('tm');
    this._type = 'tool';
    //generate validation function
    this.validate = generateValidate(this, validate);
    //default submodels
    values = values || {};
    defaults = defaults || {};
    values = defaultModel(values, defaults);
    //constructor is similar to model
    this.default_model = defaults;
    this._super(name, values, null, binds);
  }
});
//tool
var Tool = Component.extend({
  /**
   * Initializes the tool
   * @param {Object} placeholder object
   * @param {Object} external_model External model such as state, data, etc
   */
  init: function(placeholder, external_model) {
    this._id = utils.uniqueId('t');
    this.template = this.template ||
      '
' +
        '
' +
          '
' +
          '
' +
          '
' +
          '
' +
        '
' +
        '
' +
          '
' +
          '
' +
          '
' +
          '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
' +
      '
';
    this.model_binds = this.model_binds || {};

    external_model = external_model || {}; //external model can be undefined
    external_model.bind = external_model.bind || {}; //bind functions can be undefined

    //bind the validation function with the tool
    var validate = this.validate.bind(this);
    var _this = this;

    // callbacks has to be an array so that it will not be turned into a submodel when the toolmodel is made.
    var callbacks = {
      'change': function(evt, path) {
        if(_this._ready) {
          _this.model.validate();

          if (evt.source.persistent)
            _this.model.trigger(new DefaultEvent(evt.source, 'persistentChange'), _this.getPersistentModel());
        }
      },
      'hook_change': function() {
        if (!_this.model.state.time.splash) { // not block when it initial splash screen
          _this.beforeLoading(true);
        }
      },
      'change:ui.presentation': function() {
        _this.layout.updatePresentation();
        _this.trigger('resize');
      },
      'translate:language': function() {
        _this.translateStrings();
      },
      'load_start': function() {
        _this.beforeLoading();
      },
      'ready': function(evt) {
        if(_this._ready) {
          _this.afterLoading();
        }
      }
    };
    utils.extend(callbacks, this.model_binds, external_model.bind);
    delete external_model.bind;

    this.model = new ToolModel(this.name, external_model, this.default_model, callbacks, validate);

    // default model is the model set in the tool
    this.default_model = utils.deepClone(this.default_model) || {};

    this.ui = this.model.ui || {};

    this.layout = new Layout(this.ui);
    //splash
    this.ui.splash = this.model && this.model.data && this.model.data.splash;
    this._super({
      name: this.name || this._id,
      placeholder: placeholder
    }, this);

    this.render();
    this._setUIModel();
  },

  ready: function(){
    this.checkTimeLimits();
  },

  checkTimeLimits: function() {
    if(!this.model.state.time) return;

    var time = this.model.state.time;

    if(this.model.state.marker) {
      var tLimits = this.model.state.marker.getTimeLimits(time.getDimension());

      if(!tLimits || !utils.isDate(tLimits.min) || !utils.isDate(tLimits.max))
          return utils.warn("checkTimeLimits(): min-max look wrong: " + tLimits.min + " " + tLimits.max + ". Expecting Date objects");

      // change start and end (but keep startOrigin and endOrigin for furhter requests)
      var newTime = {}
      if(time.start - tLimits.min != 0) newTime['start'] = d3.max([tLimits.min, time.parseToUnit(time.startOrigin)]);
      if(time.end - tLimits.max != 0) newTime['end'] = d3.min([tLimits.max, time.parseToUnit(time.endOrigin)]);
      if(time.value == null) newTime['value'] = time.parseToUnit(time.format(new Date())); // default to current date. Other option: newTime['start'] || newTime['end'] || time.start || time.end;

      time.set(newTime, false, false);
    }

    //force time validation because time.value might now fall outside of start-end
    time.validate();
  },

  getDefaultModel: function() {
    var defaultToolModel = this.default_model;
    var defaultsFromModels = this.model.getDefaults();
    var result = utils.deepExtend({}, defaultsFromModels, defaultToolModel);
    return result;
  },

  getPersistentModel: function() {
    //try to find functions in properties of model.
    var removeFunctions = function(model) {
      for(var childKey in model) {
        if(typeof model[childKey] === 'function') {
          delete model[childKey];
          utils.warn('minModel validation. Function found in enumerable properties of ' + childKey + ". This key is deleted from minModel");
        }
        else if(typeof model[childKey] === 'object')
          removeFunctions(model[childKey]);
      }
    }

    var currentToolModel = this.model.getPlainObject(true); // true = get only persistent model values
    var result = utils.flattenDates(currentToolModel, this.model.state.time.timeFormat);

    removeFunctions(result);
    return result;
  },

  getPersistentMinimalModel: function(diffModel) {
    var defaultModel = this.getDefaultModel();
    var currentPersistentModel = this.getPersistentModel();
    var redundantModel = utils.deepExtend(defaultModel, diffModel);
    return utils.diffObject(currentPersistentModel, redundantModel);
  },

  /**
   * Clears a tool
   */

  clear: function() {
    this.layout.clear();
    this.setModel = this.getModel = function() {
      return;
    };
    this._super();
  },

  /**
   * Visually display errors
   */
  error: function(opts) {

    var msg = (opts && opts.type === "data") ? "Error loading chart data.
Please, try again soon." : "Error loading chart";

    this.placeholder.innerHTML = '

'+warnIcon+'

'+msg+'

';
  },

  /**
   * Sets model from external page
   * @param {Object} JSONModel new model in JSON format
   * @param {Boolean} overwrite overwrite everything instead of extending
   */
  setModel: function(newModelJSON, overwrite) {
    if(overwrite) {
      this.model.reset(newModelJSON);
    } else {
      this.model.set(newModelJSON);
    }
    this._setUIModel();
  },

  /**
   * get model
   * @return {Object} JSON object of model
   */
  getModel: function() {
    return this.model.getPlainObject() || {};
  },
  /**
   * Displays loading class
   */
  beforeLoading: function(loadingData) {
    if(!this._readyOnce) {
        utils.addClass(this.placeholder, class_loading_first);
    }
    if(loadingData) {
        utils.addClass(this.placeholder, class_loading_data);
    }
  },
  /**
   * Removes loading class
   */
  afterLoading: function() {
    utils.removeClass(this.placeholder, class_loading_first);
    utils.removeClass(this.placeholder, class_loading_data);
  },
  /**
   * Adds loading error class
   */
  errorLoading: function() {
    utils.addClass(this.placeholder, class_loading_error);
  },
  /* ==========================
   * Validation and query
   * ==========================
   */
  /**
   * Validating the tool model
   * @param model the current tool model to be validated
   */
  validate: function(model) {
    model = this.model || model;

    if(!model || !model.state) return utils.warn("tool validation aborted: model.state looks wrong: " + model);
  },

  _setUIModel: function() {
    //add placeholder class
    utils.addClass(this.placeholder, class_placeholder);
    //add-remove buttonlist class
    if(!this.ui || !this.ui.buttons || !this.ui.buttons.length) {
      utils.addClass(this.element, class_buttons_off);
    } else {
      utils.removeClass(this.element, class_buttons_off);
    }
  },

  preloadLanguage: function() {
    return Promise.resolve();
  }
});

/* ==========================
 * Validation methods
 * ==========================
 */

/**
 * Generates a validation function based on specific model validation
 * @param {Object} m model
 * @param {Function} validate validation function
 * @returns {Function} validation
 */
function generateValidate(m, validate) {
  var max = 10;

  function validate_func() {
    var model = JSON.stringify(m.getPlainObject());
    var c = arguments[0] || 0;
    //TODO: remove validation hotfix
    //while setting this.model is not available
    if(!this._readyOnce) {
      validate(this);
    } else {
      validate();
    }
    var model2 = JSON.stringify(m.getPlainObject());
    if(c >= max) {
      utils.error('Max validation loop.');
    } else if(model !== model2) {
      validate_func.call(this, [c += 1]);
    }
  }

  return validate_func;
}

/* ==========================
 * Default model methods
 * ==========================
 */

/**
 * Generates a valid state based on default model
 */
function defaultModel(values, defaults) {
  var keys = Object.keys(defaults);
  var size = keys.length;
  var field;
  var blueprint;
  var original;
  var type;
  for(var i = 0; i < size; i += 1) {
    field = keys[i];
    if(field === '_defs_') {
      continue;
    }
    blueprint = defaults[field];
    original = values[field];
    type = typeof blueprint;
    if(type === 'object') {
      type = utils.isPlainObject(blueprint) && blueprint._defs_ ? 'object' : utils.isArray(blueprint) ? 'array' :
        'model';
    }
    if(typeof original === 'undefined') {
      if(type !== 'object' && type !== 'model') {
        values[field] = blueprint;
      } else {
        values[field] = defaultModel({}, blueprint);
      }
    }
    original = values[field];
    if(type === 'number' && isNaN(original)) {
      values[field] = 0;
    } else if(type === 'string' && typeof original !== 'string') {
      values[field] = '';
    } else if(type === 'array' && !utils.isArray(original)) {
      values[field] = [];
    } else if(type === 'model') {
      if(!utils.isObject(original)) {
        values[field] = {};
      }
      values[field] = defaultModel(values[field], blueprint);
    } else if(type === 'object') {
      if(!utils.isObject(original) || Object.keys(original).length === 0) {
        original = false; //will be overwritten
      }
      if(!utils.isObject(blueprint._defs_)) {
        blueprint._defs_ = {};
      }
      values[field] = original || blueprint._defs_;
    }
  }
  return values;
}

/**
 * Outputs the difference between two objects
 * @param {Object} obj prevailing object
 * @param {Object} compare comparison object
 * @returns {Object} resulting diff object
 */
function changedObj(obj, compare) {
  var acc = {};
  utils.forEach(obj, function(val, name) {
    if(!(name in compare)) {
      acc[name] = val;
      return true;
    }
    //if the same, no need to check deeper
    if(JSON.stringify(val) === JSON.stringify(compare[name])) return true;
    else if(utils.isArray(val)) {
      acc[name] = val;
    } else if(utils.isObject(val)) {
      acc[name] = changedObj(val, compare[name]);
    } else if(utils.isDate(compare[name])) {
      var comp1 = val.toString();
      //TODO: workaround for years only
      var comp2 = compare[name].getUTCFullYear().toString();
      if(comp1 !== comp2) {
        acc[name] = val;
      }
    } else {
      acc[name] = val;
    }
  });
  return acc;
}

//utility function to check if a component is a tool
//TODO: Move to utils?
Tool.isTool = function(c) {
  return c._id && c._id[0] === 't';
};

export default Tool;
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Service.extend({
  sitemapEntryFilter: null,
  dynamicSegmentResolver: null,
  rootUrl: null,
  allRoutes: null,

  init() {
    this._super(...arguments);

    const envSettings = Ember.getOwner(this).resolveRegistration('config:environment');
    if (envSettings) {
      this.setSettings(envSettings.sitemap);
    }

    const router = Ember.getOwner(this).lookup('router:main');
    const allRoutes = router.get('_routerMicrolib.recognizer.names');
    if (allRoutes) {
      this.setRoutes(allRoutes);
    }
  },

  setRoutes: function(allRoutes) {
    this.set('allRoutes', allRoutes);
  },

  setSettings: function(settings) {
    if (settings) {
      if ('rootUrl' in settings) {
        this.set('rootUrl', settings.rootUrl);
      }
    }
  },

  setSitemapEntryFilter: function(sitemapEntryFilter) {
    this.set('sitemapEntryFilter', sitemapEntryFilter);
  },

  setDynamicSegmentResolver: function(dynamicSegmentResolver) {
    this.set('dynamicSegmentResolver', dynamicSegmentResolver);
  },

  getModel() {
    this._validate();

    const sitemapEntriesPromise = this._routesToSitemapEntries(this.get('allRoutes'));

    return sitemapEntriesPromise;
  },

  _validate() {
    if (!this.get('rootUrl')) {
      throw new Error(`sitemap.rootUrl is required`);
    }
    if (!this.get('allRoutes')) {
      throw new Error(`allRoutes are required`);
    }
  },

  _routesToSitemapEntries(routes) {
    const ignore = [
      'error',
      'loading',
      'application',
      'sitemap-txt',
      'sitemap-xml',
    ];

    let sitemapEntriesPromise = Promise.resolve([]);

    Object.keys(routes).forEach((key) => {
      if (ignore.find(ignoredKey => key.indexOf(ignoredKey) > -1)) {
        return;
      }

      const dynamicSegmentsKeys = this._routeToDynamicSegments(routes[key]);

      sitemapEntriesPromise = sitemapEntriesPromise.then((entries) => {
        if (dynamicSegmentsKeys.length === 0) { // No dynamic segments in route
          return this._filterSitemapEntry(
              { loc: this._routeToPath(routes[key]) },
              this._routeToSegments(routes[key])
            )
            .then(entry => entry ? [...entries, entry] : entries);
        } else {
          return this._dynamicSegmentsToPermutations(
            dynamicSegmentsKeys, this._routeToSegments(routes[key])
          ).then((permutations) => {
            let permutationsPromise = Promise.resolve(entries);

            permutations.forEach((permutation) => {
              permutationsPromise = permutationsPromise.then((entries) =>
                this._filterSitemapEntry(
                  { loc: this._routeToPath(routes[key], permutation) },
                  this._routeToSegments(routes[key]),
                  permutation
                )
                .then(entry => entry ? [...entries, entry] : entries)
              );
            });

            return permutationsPromise;
          });
        }
      });
    });

    return sitemapEntriesPromise
      .then(entries =>
        // Remove duplications caused by indexes
        this._removeDuplicateEntries(entries)
      )
      .then(entries =>
        // Map entry.loc from relative paths to absolute URLs
        entries.map((entry) => {
          entry.loc = this._relativeToAbsoluteUrl(entry.loc);
          return entry;
        })
      );
  },

  _filterSitemapEntry(entry, segments, dynamicSegments) {
    const result = this.get('sitemapEntryFilter')(entry, segments, dynamicSegments);

    return Promise.resolve(result);
  },

  _routeToSegments(route) {
    return route.segments
      .filter(segment => [0, 1].includes(segment.type))
      .map(({type, value}) => (type === 1) ? `:${value}` : value); // Prefix dynamic segments with colon
  },

  _relativeToAbsoluteUrl(relativeUrl) {
    return this.get('rootUrl') + relativeUrl;
  },

  _removeDuplicateEntries(entries) {
    const newEntries = [];
    entries.forEach((entry) => {
      if (!newEntries.find(newEntry => newEntry.loc === entry.loc)) {
        newEntries.push(entry);
      }
    });
    return newEntries;
  },

  _routeToPath(route, dynamicSegments) {
    return route.segments
      .filter(segment => segment.type === 0 || segment.type === 1)
      .map(segment => {
        if (segment.type === 0) { // static
          return segment.value;
        } else if (segment.type === 1) { // dynamic
          if (segment.value in dynamicSegments) {
            return dynamicSegments[segment.value];
          } else {
            throw new Error(`The value for dynamic segment '${segment.value}' is not set.`);
          }
        }
      })
      .join('/');
  },

  _routeToDynamicSegments(route) {
    return route.segments
      .filter(segment => segment.type === 1)
      .map(segment => segment.value);
  },

  _resolveDynamicSegment(dynamicSegmentKey, allSegments, otherDynamicSegments) {
    const result = this.get('dynamicSegmentResolver')(dynamicSegmentKey, allSegments, otherDynamicSegments);

    return Promise.resolve(result)
      .then(values => {
        if (!Array.isArray(values)) {
          throw new Error(`The dynamic segment resolver returned a '${typeof values}'` +
          ` instead of an array for '${dynamicSegmentKey}'.`);
        }

        return values;
      });
  },

  _dynamicSegmentsToPermutations(dynamicSegmentsKeys, segments, permutation = {}) {
    if (dynamicSegmentsKeys.length === 0) {
      return Promise.resolve([]);
    }

    const firstDynamicSegmentKey = dynamicSegmentsKeys[0];
    const remainingDynamicSegmentsKeys = dynamicSegmentsKeys.slice(1);

    return this._resolveDynamicSegment(firstDynamicSegmentKey, segments, permutation)
      .then((firstDynamicSegmentValues) => {
        const firstDynamicSegmentPermutations = firstDynamicSegmentValues.map(val => ({
          [firstDynamicSegmentKey]: val,
        }));

        let permutationsPromise = Promise.resolve([]);

        firstDynamicSegmentPermutations.forEach((firstDynamicSegmentPermutation) => {
          permutationsPromise = permutationsPromise.then(permutations => {
            if (remainingDynamicSegmentsKeys.length > 0) {
              return this._dynamicSegmentsToPermutations(
                remainingDynamicSegmentsKeys,
                segments,
                Object.assign({}, permutation, firstDynamicSegmentPermutation)
              ).then((remainingDynamicSegmentsPermutations) => {
                if (remainingDynamicSegmentsPermutations.length > 0) {
                  remainingDynamicSegmentsPermutations.forEach(remainingDynamicSegmentsPermutation => {
                    permutations.push(Object.assign({}, firstDynamicSegmentPermutation, remainingDynamicSegmentsPermutation));
                  });
                } else {
                  permutations = [];
                }
                return permutations;
              })
            } else {
              permutations.push(firstDynamicSegmentPermutation);
              return permutations;
            }
          })
        });

        return permutationsPromise;
      });
  },
});
<commit_msg>Made sitemapEntryFilter optional<commit_after>import Ember from 'ember';

export default Ember.Service.extend({
  sitemapEntryFilter: null,
  dynamicSegmentResolver: null,
  rootUrl: null,
  allRoutes: null,

  init() {
    this._super(...arguments);

    const envSettings = Ember.getOwner(this).resolveRegistration('config:environment');
    if (envSettings) {
      this.setSettings(envSettings.sitemap);
    }

    const router = Ember.getOwner(this).lookup('router:main');
    const allRoutes = router.get('_routerMicrolib.recognizer.names');
    if (allRoutes) {
      this.setRoutes(allRoutes);
    }
  },

  setSettings: function(settings) {
    if (settings) {
      if ('rootUrl' in settings) {
        this.set('rootUrl', settings.rootUrl);
      }
    }
  },

  setRoutes: function(allRoutes) {
    this.set('allRoutes', allRoutes);
  },

  setSitemapEntryFilter: function(sitemapEntryFilter) {
    this.set('sitemapEntryFilter', sitemapEntryFilter);
  },

  setDynamicSegmentResolver: function(dynamicSegmentResolver) {
    this.set('dynamicSegmentResolver', dynamicSegmentResolver);
  },

  getModel() {
    this._validate();

    const sitemapEntriesPromise = this._routesToSitemapEntries(this.get('allRoutes'));

    return sitemapEntriesPromise;
  },

  _validate() {
    if (!this.get('rootUrl')) {
      throw new Error(`sitemap.rootUrl is required`);
    }
    if (!this.get('allRoutes')) {
      throw new Error(`allRoutes are required`);
    }
  },

  _routesToSitemapEntries(routes) {
    const ignore = [
      'error',
      'loading',
      'application',
      'sitemap-txt',
      'sitemap-xml',
    ];

    let sitemapEntriesPromise = Promise.resolve([]);

    Object.keys(routes).forEach((key) => {
      if (ignore.find(ignoredKey => key.indexOf(ignoredKey) > -1)) {
        return;
      }

      const dynamicSegmentsKeys = this._routeToDynamicSegments(routes[key]);

      sitemapEntriesPromise = sitemapEntriesPromise.then((entries) => {
        if (dynamicSegmentsKeys.length === 0) { // No dynamic segments in route
          return this._filterSitemapEntry(
              { loc: this._routeToPath(routes[key]) },
              this._routeToSegments(routes[key])
            )
            .then(entry => entry ? [...entries, entry] : entries);
        } else {
          return this._dynamicSegmentsToPermutations(
            dynamicSegmentsKeys, this._routeToSegments(routes[key])
          ).then((permutations) => {
            let permutationsPromise = Promise.resolve(entries);

            permutations.forEach((permutation) => {
              permutationsPromise = permutationsPromise.then((entries) =>
                this._filterSitemapEntry(
                  { loc: this._routeToPath(routes[key], permutation) },
                  this._routeToSegments(routes[key]),
                  permutation
                )
                .then(entry => entry ? [...entries, entry] : entries)
              );
            });

            return permutationsPromise;
          });
        }
      });
    });

    return sitemapEntriesPromise
      .then(entries =>
        // Remove duplications caused by indexes
        this._removeDuplicateEntries(entries)
      )
      .then(entries =>
        // Map entry.loc from relative paths to absolute URLs
        entries.map((entry) => {
          entry.loc = this._relativeToAbsoluteUrl(entry.loc);
          return entry;
        })
      );
  },

  _filterSitemapEntry(entry, segments, dynamicSegments) {
    const sitemapEntryFilter = this.get('sitemapEntryFilter');

    const result = sitemapEntryFilter ? sitemapEntryFilter(entry, segments, dynamicSegments) : entry;

    return Promise.resolve(result);
  },

  _routeToSegments(route) {
    return route.segments
      .filter(segment => [0, 1].includes(segment.type))
      .map(({type, value}) => (type === 1) ? `:${value}` : value); // Prefix dynamic segments with colon
  },

  _relativeToAbsoluteUrl(relativeUrl) {
    return this.get('rootUrl') + relativeUrl;
  },

  _removeDuplicateEntries(entries) {
    const newEntries = [];
    entries.forEach((entry) => {
      if (!newEntries.find(newEntry => newEntry.loc === entry.loc)) {
        newEntries.push(entry);
      }
    });
    return newEntries;
  },

  _routeToPath(route, dynamicSegments) {
    return route.segments
      .filter(segment => segment.type === 0 || segment.type === 1)
      .map(segment => {
        if (segment.type === 0) { // static
          return segment.value;
        } else if (segment.type === 1) { // dynamic
          if (segment.value in dynamicSegments) {
            return dynamicSegments[segment.value];
          } else {
            throw new Error(`The value for dynamic segment '${segment.value}' is not set.`);
          }
        }
      })
      .join('/');
  },

  _routeToDynamicSegments(route) {
    return route.segments
      .filter(segment => segment.type === 1)
      .map(segment => segment.value);
  },

  _resolveDynamicSegment(dynamicSegmentKey, allSegments, otherDynamicSegments) {
    const result = this.get('dynamicSegmentResolver')(dynamicSegmentKey, allSegments, otherDynamicSegments);

    return Promise.resolve(result)
      .then(values => {
        if (!Array.isArray(values)) {
          throw new Error(`The dynamic segment resolver returned a '${typeof values}'` +
          ` instead of an array for '${dynamicSegmentKey}'.`);
        }

        return values;
      });
  },

  _dynamicSegmentsToPermutations(dynamicSegmentsKeys, segments, permutation = {}) {
    if (dynamicSegmentsKeys.length === 0) {
      return Promise.resolve([]);
    }

    const firstDynamicSegmentKey = dynamicSegmentsKeys[0];
    const remainingDynamicSegmentsKeys = dynamicSegmentsKeys.slice(1);

    return this._resolveDynamicSegment(firstDynamicSegmentKey, segments, permutation)
      .then((firstDynamicSegmentValues) => {
        const firstDynamicSegmentPermutations = firstDynamicSegmentValues.map(val => ({
          [firstDynamicSegmentKey]: val,
        }));

        let permutationsPromise = Promise.resolve([]);

        firstDynamicSegmentPermutations.forEach((firstDynamicSegmentPermutation) => {
          permutationsPromise = permutationsPromise.then(permutations => {
            if (remainingDynamicSegmentsKeys.length > 0) {
              return this._dynamicSegmentsToPermutations(
                remainingDynamicSegmentsKeys,
                segments,
                Object.assign({}, permutation, firstDynamicSegmentPermutation)
              ).then((remainingDynamicSegmentsPermutations) => {
                if (remainingDynamicSegmentsPermutations.length > 0) {
                  remainingDynamicSegmentsPermutations.forEach(remainingDynamicSegmentsPermutation => {
                    permutations.push(Object.assign({}, firstDynamicSegmentPermutation, remainingDynamicSegmentsPermutation));
                  });
                } else {
                  permutations = [];
                }
                return permutations;
              })
            } else {
              permutations.push(firstDynamicSegmentPermutation);
              return permutations;
            }
          })
        });

        return permutationsPromise;
      });
  },
});
<|endoftext|>


<commit_before>import {EditorState} from "prosemirror-state"
import {EditorView} from "prosemirror-view"
import {history, redo, undo} from "prosemirror-history"
import {baseKeymap} from "prosemirror-commands"
import {keymap} from "prosemirror-keymap"
import {suggestionsPlugin, triggerCharacter} from "prosemirror-suggestions"
import {commentSchema} from "./schema"
import {notifyMentionedUser} from "./notify"
import {escapeText, findTarget} from "../../../common"

export class CommentEditor {
    constructor(mod, id, dom, text, options = {}) {
        this.mod = mod
        this.id = id
        this.dom = dom
        this.text = text
        this.options = options

        this.selectedTag = 0
        this.userTaggerList = []
        this.plugins = [
            history(),
            suggestionsPlugin({
                escapeOnSelectionChange: true,
                matcher: triggerCharacter('@'),
                onEnter: (args) => {
                    this.selectedTag = 0
                    this.tagRange = args.range
                    const search = args.text.slice(1)
                    if (search.length) {
                        this.setUserTaggerList(search)
                        this.showUserTagger()
                    }
                },
                onChange: (args) => {
                    this.selectedTag = 0
                    this.tagRange = args.range
                    const search = args.text.slice(1)
                    if (search.length) {
                        this.setUserTaggerList(search)
                        this.showUserTagger()
                    }
                },
                onExit: (_args) => {
                    this.selectedTag = 0
                    this.removeTagger()
                },
                onKeyDown: ({event}) => {
                    if (event.key === 'ArrowDown') {
                        if (this.userTaggerList.length > this.selectedTag + 1) {
                            this.selectedTag += 1
                            this.showUserTagger()
                        }
                        return true
                    } else if (event.key === 'ArrowUp') {
                        if (this.selectedTag > 0) {
                            this.selectedTag -= 1
                            this.showUserTagger()
                        }
                        return true
                    } else if (event.key === 'Enter') {
                        return this.selectUserTag()
                    }
                    return false
                },
                escapeKeys: ['Escape', 'ArrowRight', 'ArrowLeft']
            }),
            keymap(baseKeymap),
            keymap({
                "Mod-z": undo,
                "Mod-shift-z": undo,
                "Mod-y": redo,
                "Ctrl-Enter": () => this.submit()
            })
        ]
    }

    init() {
        this.initViewDOM()
        this.initView()
    }

    initViewDOM() {
        this.viewDOM = document.createElement('div')
        this.viewDOM.classList.add('ProseMirror-wrapper')
        this.dom.appendChild(this.viewDOM)
        this.dom.insertAdjacentHTML(
            'beforeend',
            `<input class="comment-is-major" type="checkbox" name="isMajor"
                ${this.options.isMajor ? 'checked' : ''}/>
            ${gettext("High priority")}

                <button class="submit fw-button fw-dark" type="submit">
                    ${this.id !== '-1' ? gettext("Edit") : gettext("Submit")}
                </button>
                <button class="cancel fw-button fw-orange" type="submit">
                    ${gettext("Cancel")}
                </button>

`
        )
    }

    initView() {
        this.view = new EditorView(this.viewDOM, {
            state: EditorState.create({
                schema: commentSchema,
                doc: commentSchema.nodeFromJSON({
                    type: 'doc',
                    content: this.text
                }),
                plugins: this.plugins
            }),
            dispatchTransaction: tr => {
                const newState = this.view.state.apply(tr)
                this.view.updateState(newState)
            }
        })
        this.oldUserTags = this.getUserTags()
        this.bind()
    }

    bind() {
        this.dom.addEventListener('click', event => {
            const el = {}
            switch (true) {
            case findTarget(event, 'button.submit:not(.disabled)', el):
                this.submit()
                this.scrollToBottom()
                break
            case findTarget(event, 'button.cancel', el):
                this.mod.interactions.cancelSubmit()
                break
            case findTarget(event, '.ProseMirror-wrapper', el):
                this.view.focus()
                break
            case findTarget(event, '.tag-user', el):
                this.selectedTag = parseInt(el.target.dataset.index)
                this.selectUserTag()
                this.view.focus()
                break
            }
        })

    }

    submit() {
        const comment = this.view.state.doc.toJSON().content,
            isMajor = this.dom.querySelector('.comment-is-major').checked
        if (comment?.length > 0) {
            this.mod.interactions.updateComment({id: this.id, comment, isMajor})
            this.sendNotifications()
        } else {
            this.mod.interactions.deleteComment(this.id)
        }
    }

    sendNotifications() {
        const newUserTags = this.getUserTags().filter(id => !this.oldUserTags.includes(id))
        if (newUserTags.length) {
            const comment = this.view.state.doc,
                docId = this.mod.editor.docInfo.id
            newUserTags.forEach(userId => notifyMentionedUser(docId, userId, comment))
        }
    }

    setUserTaggerList(search) {
        const owner = this.mod.editor.docInfo.owner
        this.userTaggerList = owner.team_members.concat(owner).filter(
            user => user.name.includes(search) || user.username.includes(search)
        )
    }

    showUserTagger() {
        if (!this.userTaggerList.length) {
            return
        }
        this.dom.querySelector('div.tagger').innerHTML = this.userTaggerList.map((user, index) =>
            `

                ${user ? user.avatar.html : `<span class="fw-string-avatar"></span>`}

${escapeText(user.name)}

`
        ).join('')
    }

    selectUserTag() {
        const user = this.userTaggerList[this.selectedTag]
        if (!user || !this.tagRange) {
            return false
        }
        const tr = this.view.state.tr.replaceRangeWith(
            this.tagRange.from,
            this.tagRange.to,
            this.view.state.schema.nodes.collaborator.create({id: user.id, name: user.name})
        )
        this.view.dispatch(tr)
        return true
    }

    getUserTags() {
        const users = []
        this.view.state.doc.descendants(node => {
            if (node.type.name === 'collaborator') {
                users.push(node.attrs.id)
            }
        })
        return [...new Set(users)] // only unique values.
    }

    removeTagger() {
        this.dom.querySelector('div.tagger').innerHTML = ''
        this.tagRange = false
        this.userTaggerList = []
    }

    scrollToBottom() {
        const activeMarginBox = document.querySelector(".margin-box.comment.active .comment-answer-container")
        if (activeMarginBox) {
            activeMarginBox.scrollTop = activeMarginBox.scrollHeight
        }

        // Code below executed for screens with width less than 1024px. At this resolution the the comments
        // are at the bottom of screen
        const currentScreenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
        if (currentScreenWidth < 1024){
            const activeMarginBoxContainer = document.querySelector("#margin-box-container")
            if(activeMarginBoxContainer){
                activeMarginBoxContainer.scrollTop = activeMarginBoxContainer.scrollHeight
            }
        }
    }
}
<commit_msg>linting fixes<commit_after>import {EditorState} from "prosemirror-state"
import {EditorView} from "prosemirror-view"
import {history, redo, undo} from "prosemirror-history"
import {baseKeymap} from "prosemirror-commands"
import {keymap} from "prosemirror-keymap"
import {suggestionsPlugin, triggerCharacter} from "prosemirror-suggestions"
import {commentSchema} from "./schema"
import {notifyMentionedUser} from "./notify"
import {escapeText, findTarget} from "../../../common"

export class CommentEditor {
    constructor(mod, id, dom, text, options = {}) {
        this.mod = mod
        this.id = id
        this.dom = dom
        this.text = text
        this.options = options

        this.selectedTag = 0
        this.userTaggerList = []
        this.plugins = [
            history(),
            suggestionsPlugin({
                escapeOnSelectionChange: true,
                matcher: triggerCharacter('@'),
                onEnter: (args) => {
                    this.selectedTag = 0
                    this.tagRange = args.range
                    const search = args.text.slice(1)
                    if (search.length) {
                        this.setUserTaggerList(search)
                        this.showUserTagger()
                    }
                },
                onChange: (args) => {
                    this.selectedTag = 0
                    this.tagRange = args.range
                    const search = args.text.slice(1)
                    if (search.length) {
                        this.setUserTaggerList(search)
                        this.showUserTagger()
                    }
                },
                onExit: (_args) => {
                    this.selectedTag = 0
                    this.removeTagger()
                },
                onKeyDown: ({event}) => {
                    if (event.key === 'ArrowDown') {
                        if (this.userTaggerList.length > this.selectedTag + 1) {
                            this.selectedTag += 1
                            this.showUserTagger()
                        }
                        return true
                    } else if (event.key === 'ArrowUp') {
                        if (this.selectedTag > 0) {
                            this.selectedTag -= 1
                            this.showUserTagger()
                        }
                        return true
                    } else if (event.key === 'Enter') {
                        return this.selectUserTag()
                    }
                    return false
                },
                escapeKeys: ['Escape', 'ArrowRight', 'ArrowLeft']
            }),
            keymap(baseKeymap),
            keymap({
                "Mod-z": undo,
                "Mod-shift-z": undo,
                "Mod-y": redo,
                "Ctrl-Enter": () => this.submit()
            })
        ]
    }

    init() {
        this.initViewDOM()
        this.initView()
    }

    initViewDOM() {
        this.viewDOM = document.createElement('div')
        this.viewDOM.classList.add('ProseMirror-wrapper')
        this.dom.appendChild(this.viewDOM)
        this.dom.insertAdjacentHTML(
            'beforeend',
            `<input class="comment-is-major" type="checkbox" name="isMajor"
                ${this.options.isMajor ? 'checked' : ''}/>
            ${gettext("High priority")}

                <button class="submit fw-button fw-dark" type="submit">
                    ${this.id !== '-1' ? gettext("Edit") : gettext("Submit")}
                </button>
                <button class="cancel fw-button fw-orange" type="submit">
                    ${gettext("Cancel")}
                </button>

`
        )
    }

    initView() {
        this.view = new EditorView(this.viewDOM, {
            state: EditorState.create({
                schema: commentSchema,
                doc: commentSchema.nodeFromJSON({
                    type: 'doc',
                    content: this.text
                }),
                plugins: this.plugins
            }),
            dispatchTransaction: tr => {
                const newState = this.view.state.apply(tr)
                this.view.updateState(newState)
            }
        })
        this.oldUserTags = this.getUserTags()
        this.bind()
    }

    bind() {
        this.dom.addEventListener('click', event => {
            const el = {}
            switch (true) {
            case findTarget(event, 'button.submit:not(.disabled)', el):
                this.submit()
                this.scrollToBottom()
                break
            case findTarget(event, 'button.cancel', el):
                this.mod.interactions.cancelSubmit()
                break
            case findTarget(event, '.ProseMirror-wrapper', el):
                this.view.focus()
                break
            case findTarget(event, '.tag-user', el):
                this.selectedTag = parseInt(el.target.dataset.index)
                this.selectUserTag()
                this.view.focus()
                break
            }
        })

    }

    submit() {
        const comment = this.view.state.doc.toJSON().content,
            isMajor = this.dom.querySelector('.comment-is-major').checked
        if (comment?.length > 0) {
            this.mod.interactions.updateComment({id: this.id, comment, isMajor})
            this.sendNotifications()
        } else {
            this.mod.interactions.deleteComment(this.id)
        }
    }

    sendNotifications() {
        const newUserTags = this.getUserTags().filter(id => !this.oldUserTags.includes(id))
        if (newUserTags.length) {
            const comment = this.view.state.doc,
                docId = this.mod.editor.docInfo.id
            newUserTags.forEach(userId => notifyMentionedUser(docId, userId, comment))
        }
    }

    setUserTaggerList(search) {
        const owner = this.mod.editor.docInfo.owner
        this.userTaggerList = owner.team_members.concat(owner).filter(
            user => user.name.includes(search) || user.username.includes(search)
        )
    }

    showUserTagger() {
        if (!this.userTaggerList.length) {
            return
        }
        this.dom.querySelector('div.tagger').innerHTML = this.userTaggerList.map((user, index) =>
            `

                ${user ? user.avatar.html : `<span class="fw-string-avatar"></span>`}

${escapeText(user.name)}

`
        ).join('')
    }

    selectUserTag() {
        const user = this.userTaggerList[this.selectedTag]
        if (!user || !this.tagRange) {
            return false
        }
        const tr = this.view.state.tr.replaceRangeWith(
            this.tagRange.from,
            this.tagRange.to,
            this.view.state.schema.nodes.collaborator.create({id: user.id, name: user.name})
        )
        this.view.dispatch(tr)
        return true
    }

    getUserTags() {
        const users = []
        this.view.state.doc.descendants(node => {
            if (node.type.name === 'collaborator') {
                users.push(node.attrs.id)
            }
        })
        return [...new Set(users)] // only unique values.
    }

    removeTagger() {
        this.dom.querySelector('div.tagger').innerHTML = ''
        this.tagRange = false
        this.userTaggerList = []
    }

    scrollToBottom() {
        const activeMarginBox = document.querySelector(".margin-box.comment.active .comment-answer-container")
        if (activeMarginBox) {
            activeMarginBox.scrollTop = activeMarginBox.scrollHeight
        }

        // scroll to bottom of the margin-box-container when new comments are added when the screens width is less
        // than 1024px
        const currentScreenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
        if (currentScreenWidth < 1024) {
            const activeMarginBoxContainer = document.querySelector("#margin-box-container")
            if (activeMarginBoxContainer) {
                activeMarginBoxContainer.scrollTop = activeMarginBoxContainer.scrollHeight
            }
        }
    }
}
<|endoftext|>


<commit_before>exports = module.exports = function(ats, logger, container) {
  var oauth2orize = require('oauth2orize');

  // TODO: require('oauth2orize-idpiframerm')

  var components = container.components('http://i.authnomicon.org/oauth2/authorization/http/ResponseMode');
  return Promise.all(components.map(function(comp) { return comp.create(); } ))
    .then(function(plugins) {
      var modes = {}
        , name;
      plugins.forEach(function(mode, i) {
        name = components[i].a['@mode'];
        if (name == 'query') {
          // The default response mode of this response type is the fragment
          // encoding.  In accordance with security considerations, this
          // response type must not use query encoding, in order to avoid
          // leaking sensitive information such as access tokens.
          //
          // For more information, refer to:
          // https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Security
          return;
        }

        modes[name] = mode;
        logger.info('Loaded response mode for OAuth 2.0 implicit grant: ' + name);
      });

      return oauth2orize.grant.token({
        modes: modes
      }, function(client, user, ares, areq, locals, cb) {
        var msg = {};
        msg.client = client;
        msg.user = user;
        // TODO: Put a grant ID in here somehere
        // maybe not, since there's no refresh token here?
        //msg.grant = ares;
        if (ares.scope) { msg.scope = ares.scope; }
        // TODO: Pass some indicator that this is an implicit flow, so token lifetimes
        //. can be constrained accordingly

        ats.issue(msg, function(err, token) {
          if (err) { return cb(err); }
          return cb(null, token);
        });
      });
    });
};

exports['@implements'] = 'http://i.authnomicon.org/oauth2/authorization/http/ResponseType';
exports['@type'] = 'token';
exports['@require'] = [
  '!container',
  'http://i.authnomicon.org/oauth2/AccessTokenService',
  'http://i.bixbyjs.org/Logger'
];
<commit_msg>Rearrange arguments to token response type component.<commit_after>exports = module.exports = function(ats, logger, container) {
  var oauth2orize = require('oauth2orize');

  // TODO: require('oauth2orize-idpiframerm')

  var components = container.components('http://i.authnomicon.org/oauth2/authorization/http/ResponseMode');
  return Promise.all(components.map(function(comp) { return comp.create(); } ))
    .then(function(plugins) {
      var modes = {}
        , name;
      plugins.forEach(function(mode, i) {
        name = components[i].a['@mode'];
        if (name == 'query') {
          // The default response mode of this response type is the fragment
          // encoding.  In accordance with security considerations, this
          // response type must not use query encoding, in order to avoid
          // leaking sensitive information such as access tokens.
          //
          // For more information, refer to:
          // https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#Security
          return;
        }

        modes[name] = mode;
        logger.info('Loaded response mode for OAuth 2.0 implicit grant: ' + name);
      });

      return oauth2orize.grant.token({
        modes: modes
      }, function(client, user, ares, areq, locals, cb) {
        var msg = {};
        msg.client = client;
        msg.user = user;
        // TODO: Put a grant ID in here somehere
        // maybe not, since there's no refresh token here?
        //msg.grant = ares;
        if (ares.scope) { msg.scope = ares.scope; }
        // TODO: Pass some indicator that this is an implicit flow, so token lifetimes
        //. can be constrained accordingly

        ats.issue(msg, function(err, token) {
          if (err) { return cb(err); }
          return cb(null, token);
        });
      });
    });
};

exports['@implements'] = 'http://i.authnomicon.org/oauth2/authorization/http/ResponseType';
exports['@type'] = 'token';
exports['@require'] = [
  'http://i.authnomicon.org/oauth2/AccessTokenService',
  'http://i.bixbyjs.org/Logger',
  '!container'
];
<|endoftext|>


<commit_before>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide("frappe.ui.toolbar");
frappe.provide('frappe.search');

frappe.ui.toolbar.Toolbar = Class.extend({
	init: function() {
		$('header').append(frappe.render_template("navbar", {
			avatar: frappe.avatar(frappe.session.user)
		}));
		$('.dropdown-toggle').dropdown();

		let awesome_bar = new frappe.search.AwesomeBar();
		awesome_bar.setup("#navbar-search");
		awesome_bar.setup("#modal-search");

		this.make();
	},

	make: function() {
		this.setup_sidebar();
		this.setup_help();

		// frappe.chat (added to toolbar as per rushabh@frappe.io request)
		this.setup_frappe_chat()
		// end frappe.chat

		this.setup_progress_dialog();
		this.bind_events();

		$(document).trigger('toolbar_setup');
	},

	setup_frappe_chat ( ) {
		// TODO: frappe.chat: Handle realtime System Settings update.
		// TODO: frappe.chat: frappe.chat.<object> requires a storage.

		// Create/Get Chat Profile for session User, retrieve enable_chat
		frappe.chat.profile.create("enable_chat").then(({ enable_chat }) => {
			const should_render = frappe.sys_defaults.enable_chat && enable_chat;
			this.render_frappe_chat(should_render);
		});

		// Triggered when a User updates his/her Chat Profile.
		// Don't worry, enable_chat is broadcasted to this user only. No overhead. :)
		frappe.chat.profile.on.update((user, profile) => {
			if ( user === frappe.session.user && 'enable_chat' in profile ) {
				const should_render = frappe.sys_defaults.enable_chat && profile.enable_chat;
				this.render_frappe_chat(should_render);
			}
		});
	},

	render_frappe_chat (render = true, force = false)
	{
		// With the assumption, that there's only one navbar.
		const $placeholder = $('.navbar .frappe-chat-dropdown');

		// Render if frappe-chat-toggle doesn't exist.
		if ( frappe.utils.is_empty($placeholder.has('.frappe-chat-toggle')) ) {
			const $template = $(`

			`);

			$placeholder.addClass('dropdown hidden');
			$placeholder.html($template);
		}

		if ( render ) {
			$placeholder.removeClass('hidden');
		} else {
			$placeholder.addClass('hidden');
		}

		// Avoid re-renders. Once is enough.
		if ( !frappe.chatter || force )
		{
			frappe.chatter = new frappe.Chat({ target: '.navbar .frappe-chat-toggle' });
			frappe.chatter.render()
		}
	},

	bind_events: function() {
		$(document).on("notification-update", function() {
			frappe.ui.notifications.update_notifications();
		});

		// clear all custom menus on page change
		$(document).on("page-change", function() {
			$("header .navbar .custom-menu").remove();
		});

		//focus search-modal on show in mobile view
		$('#search-modal').on('shown.bs.modal', function () {
			var search_modal = $(this);
			setTimeout(function() {
				search_modal.find('#modal-search').focus();
			}, 300);
		});
	},

	setup_sidebar: function () {
		var header = $('header');
		header.find(".toggle-sidebar").on("click", function () {
			var layout_side_section = $('.layout-side-section');
			var overlay_sidebar = layout_side_section.find('.overlay-sidebar');

			overlay_sidebar.addClass('opened');
			overlay_sidebar.find('.reports-dropdown')
				.removeClass('dropdown-menu')
				.addClass('list-unstyled');
			overlay_sidebar.find('.dropdown-toggle')
				.addClass('text-muted').find('.caret')
				.addClass('hidden-xs hidden-sm');

			$('
').hide().appendTo(layout_side_section).fadeIn();

			var scroll_container = $('html');
			scroll_container.css("overflow-y", "hidden");

			layout_side_section.find(".close-sidebar").on('click', close_sidebar);
			layout_side_section.on("click", "a", close_sidebar);

			function close_sidebar(e) {
				scroll_container.css("overflow-y", "");

				layout_side_section.find("div.close-sidebar").fadeOut(function() {
					overlay_sidebar.removeClass('opened')
						.find('.dropdown-toggle')
						.removeClass('text-muted');
					overlay_sidebar.find('.reports-dropdown')
						.addClass('dropdown-menu');
				});
			}
		});
	},

	setup_help: function () {
		frappe.provide('frappe.help');
		frappe.help.show_results = show_results;

		this.search = new frappe.search.SearchDialog();
		frappe.provide('frappe.searchdialog');
		frappe.searchdialog.search = this.search;

		$(".dropdown-help .dropdown-toggle").on("click", function () {
			$(".dropdown-help input").focus();
		});

		$(".dropdown-help .dropdown-menu").on("click", "input, button", function (e) {
			e.stopPropagation();
		});

		$("#input-help").on("keydown", function (e) {
			if(e.which == 13) {
				var keywords = $(this).val();
				show_help_results(keywords);
				$(this).val("");
			}
		});

		$("#input-help + span").on("click", function () {
			var keywords = $("#input-help").val();
			show_help_results(keywords);
			$(this).val("");
		});

		$(document).on("page-change", function () {
			var $help_links = $(".dropdown-help #help-links");
			$help_links.html("");

			var route = frappe.get_route_str();
			var breadcrumbs = route.split("/");

			var links = [];
			for (var i = 0; i < breadcrumbs.length; i++) {
				var r = route.split("/", i + 1);
				var key = r.join("/");
				var help_links = frappe.help.help_links[key] || [];
				links = $.merge(links, help_links);
			}

			if(links.length === 0) {
				$help_links.next().hide();
			}
			else {
				$help_links.next().show();
			}

			for (var i = 0; i < links.length; i++) {
				var link = links[i];
				var url = link.url;
				var app_name = url.split('//', 2)[1].split('/', 2)[1];
				var data_path = url.slice(url.indexOf('/user'));
				if(data_path.lastIndexOf('.')){
					data_path = data_path.slice(0, data_path.lastIndexOf('.'));
				}
				data_path = data_path.replace('user', app_name);

				$("", {
					href: link.url,
					text: link.label,
					target: "_blank",
					"data-path": data_path
				}).appendTo($help_links);
			}

			$('.dropdown-help .dropdown-menu').on('click', 'a', show_results);
		});

		var $result_modal = frappe.get_modal("", "");
		$result_modal.addClass("help-modal");

		$(document).on("click", ".help-modal a", show_results);

		var me = this;
		function show_help_results(keywords) {
			me.search.init_search(keywords, "help");
		}

		function show_results(e) {
			//edit links
			var href = e.target.href;
			if(href.indexOf('blob') > 0) {
				window.open(href, '_blank');
			}
			var converter = new Showdown.converter();
			var path = $(e.target).attr("data-path");
			if(path) {
				e.preventDefault();
				frappe.call({
					method: "frappe.utils.help.get_help_content",
					args: {
						path: path
					},
					callback: function (r) {
						if(r.message && r.message.title) {
							$result_modal.find('.modal-title').html(""
								+ r.message.title + "");
							$result_modal.find('.modal-body').html(r.message.content);
							$result_modal.modal('show');
						}
					}
				});
			}
		}
	},

	setup_progress_dialog: function() {
		var me = this;
		frappe.call({
			method: "frappe.desk.user_progress.get_user_progress_slides",
			callback: function(r) {
				if(r.message) {
					let slides = r.message;
					if(slides.length && slides.map(s => parseInt(s.done)).includes(0)) {
						frappe.require("assets/frappe/js/frappe/ui/toolbar/user_progress_dialog.js", function() {
							me.progress_dialog = new frappe.setup.UserProgressDialog({
								slides: slides
							});
							$('.user-progress').removeClass('hide');
							$('.user-progress .dropdown-toggle').on('click', () => {
								me.progress_dialog.show();
							});

							if (frappe.boot.is_first_startup) {
								me.progress_dialog.show();
								frappe.call({
									method: "frappe.desk.page.setup_wizard.setup_wizard.reset_is_first_startup",
									args: {},
									callback: () => {}
								});
							}

						});
					}
				}
			},
			freeze: false
		});
	}
});

$.extend(frappe.ui.toolbar, {
	add_dropdown_button: function(parent, label, click, icon) {
		var menu = frappe.ui.toolbar.get_menu(parent);
		if(menu.find("li:not(.custom-menu)").length && !menu.find(".divider").length) {
			frappe.ui.toolbar.add_menu_divider(menu);
		}

		return $('
 '+label+'
')
			.insertBefore(menu.find(".divider"))
			.find("a")
			.click(function() {
				click.apply(this);
			});
	},
	get_menu: function(label) {
		return $("#navbar-" + label.toLowerCase());
	},
	add_menu_divider: function(menu) {
		menu = typeof menu == "string" ?
			frappe.ui.toolbar.get_menu(menu) : menu;

		$('

').prependTo(menu);
	}
});

frappe.ui.toolbar.clear_cache = function() {
	frappe.assets.clear_local_storage();
	frappe.call({
		method: 'frappe.sessions.clear',
		callback: function(r) {
			if(!r.exc) {
				frappe.show_alert({message:r.message, indicator:'green'});
				location.reload(true);
			}
		}
	})
	return false;
}

frappe.ui.toolbar.download_backup = function() {
	frappe.msgprint(__("Your download is being built, this may take a few moments..."));
	$c('frappe.utils.backups.get_backup',{},function(r,rt) {});
	return false;
}

frappe.ui.toolbar.show_about = function() {
	try {
		frappe.ui.misc.about();
	} catch(e) {
		console.log(e);
	}
	return false;
}
<commit_msg>fixed codacy<commit_after>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

frappe.provide("frappe.ui.toolbar");
frappe.provide('frappe.search');

frappe.ui.toolbar.Toolbar = Class.extend({
	init: function() {
		$('header').append(frappe.render_template("navbar", {
			avatar: frappe.avatar(frappe.session.user)
		}));
		$('.dropdown-toggle').dropdown();

		let awesome_bar = new frappe.search.AwesomeBar();
		awesome_bar.setup("#navbar-search");
		awesome_bar.setup("#modal-search");

		this.make();
	},

	make: function() {
		this.setup_sidebar();
		this.setup_help();

		// frappe.chat (added to toolbar as per rushabh@frappe.io request)
		this.setup_frappe_chat()
		// end frappe.chat

		this.setup_progress_dialog();
		this.bind_events();

		$(document).trigger('toolbar_setup');
	},

	setup_frappe_chat ( ) {
		// TODO: frappe.chat: Handle realtime System Settings update.
		// TODO: frappe.chat: frappe.chat.<object> requires a storage.

		// Create/Get Chat Profile for session User, retrieve enable_chat
		frappe.chat.profile.create("enable_chat").then(({ enable_chat }) => {
			const should_render = frappe.sys_defaults.enable_chat && enable_chat;
			this.render_frappe_chat(should_render);
		});

		// Triggered when a User updates his/her Chat Profile.
		// Don't worry, enable_chat is broadcasted to this user only. No overhead. :)
		frappe.chat.profile.on.update((user, profile) => {
			if ( user === frappe.session.user && 'enable_chat' in profile ) {
				const should_render = frappe.sys_defaults.enable_chat && profile.enable_chat;
				this.render_frappe_chat(should_render);
			}
		});
	},

	render_frappe_chat (render = true, force = false)
	{
		// With the assumption, that there's only one navbar.
		const $placeholder = $('.navbar .frappe-chat-dropdown');

		// Render if frappe-chat-toggle doesn't exist.
		if ( frappe.utils.is_empty($placeholder.has('.frappe-chat-toggle')) ) {
			const $template = $(`

			`);

			$placeholder.addClass('dropdown hidden');
			$placeholder.html($template);
		}

		if ( render ) {
			$placeholder.removeClass('hidden');
		} else {
			$placeholder.addClass('hidden');
		}

		// Avoid re-renders. Once is enough.
		if ( !frappe.chatter || force )
		{
			frappe.chatter = new frappe.Chat({ target: '.navbar .frappe-chat-toggle' });
			frappe.chatter.render();
		}
	},

	bind_events: function() {
		$(document).on("notification-update", function() {
			frappe.ui.notifications.update_notifications();
		});

		// clear all custom menus on page change
		$(document).on("page-change", function() {
			$("header .navbar .custom-menu").remove();
		});

		//focus search-modal on show in mobile view
		$('#search-modal').on('shown.bs.modal', function () {
			var search_modal = $(this);
			setTimeout(function() {
				search_modal.find('#modal-search').focus();
			}, 300);
		});
	},

	setup_sidebar: function () {
		var header = $('header');
		header.find(".toggle-sidebar").on("click", function () {
			var layout_side_section = $('.layout-side-section');
			var overlay_sidebar = layout_side_section.find('.overlay-sidebar');

			overlay_sidebar.addClass('opened');
			overlay_sidebar.find('.reports-dropdown')
				.removeClass('dropdown-menu')
				.addClass('list-unstyled');
			overlay_sidebar.find('.dropdown-toggle')
				.addClass('text-muted').find('.caret')
				.addClass('hidden-xs hidden-sm');

			$('
').hide().appendTo(layout_side_section).fadeIn();

			var scroll_container = $('html');
			scroll_container.css("overflow-y", "hidden");

			layout_side_section.find(".close-sidebar").on('click', close_sidebar);
			layout_side_section.on("click", "a", close_sidebar);

			function close_sidebar(e) {
				scroll_container.css("overflow-y", "");

				layout_side_section.find("div.close-sidebar").fadeOut(function() {
					overlay_sidebar.removeClass('opened')
						.find('.dropdown-toggle')
						.removeClass('text-muted');
					overlay_sidebar.find('.reports-dropdown')
						.addClass('dropdown-menu');
				});
			}
		});
	},

	setup_help: function () {
		frappe.provide('frappe.help');
		frappe.help.show_results = show_results;

		this.search = new frappe.search.SearchDialog();
		frappe.provide('frappe.searchdialog');
		frappe.searchdialog.search = this.search;

		$(".dropdown-help .dropdown-toggle").on("click", function () {
			$(".dropdown-help input").focus();
		});

		$(".dropdown-help .dropdown-menu").on("click", "input, button", function (e) {
			e.stopPropagation();
		});

		$("#input-help").on("keydown", function (e) {
			if(e.which == 13) {
				var keywords = $(this).val();
				show_help_results(keywords);
				$(this).val("");
			}
		});

		$("#input-help + span").on("click", function () {
			var keywords = $("#input-help").val();
			show_help_results(keywords);
			$(this).val("");
		});

		$(document).on("page-change", function () {
			var $help_links = $(".dropdown-help #help-links");
			$help_links.html("");

			var route = frappe.get_route_str();
			var breadcrumbs = route.split("/");

			var links = [];
			for (var i = 0; i < breadcrumbs.length; i++) {
				var r = route.split("/", i + 1);
				var key = r.join("/");
				var help_links = frappe.help.help_links[key] || [];
				links = $.merge(links, help_links);
			}

			if(links.length === 0) {
				$help_links.next().hide();
			}
			else {
				$help_links.next().show();
			}

			for (var i = 0; i < links.length; i++) {
				var link = links[i];
				var url = link.url;
				var app_name = url.split('//', 2)[1].split('/', 2)[1];
				var data_path = url.slice(url.indexOf('/user'));
				if(data_path.lastIndexOf('.')){
					data_path = data_path.slice(0, data_path.lastIndexOf('.'));
				}
				data_path = data_path.replace('user', app_name);

				$("", {
					href: link.url,
					text: link.label,
					target: "_blank",
					"data-path": data_path
				}).appendTo($help_links);
			}

			$('.dropdown-help .dropdown-menu').on('click', 'a', show_results);
		});

		var $result_modal = frappe.get_modal("", "");
		$result_modal.addClass("help-modal");

		$(document).on("click", ".help-modal a", show_results);

		var me = this;
		function show_help_results(keywords) {
			me.search.init_search(keywords, "help");
		}

		function show_results(e) {
			//edit links
			var href = e.target.href;
			if(href.indexOf('blob') > 0) {
				window.open(href, '_blank');
			}
			var converter = new Showdown.converter();
			var path = $(e.target).attr("data-path");
			if(path) {
				e.preventDefault();
				frappe.call({
					method: "frappe.utils.help.get_help_content",
					args: {
						path: path
					},
					callback: function (r) {
						if(r.message && r.message.title) {
							$result_modal.find('.modal-title').html(""
								+ r.message.title + "");
							$result_modal.find('.modal-body').html(r.message.content);
							$result_modal.modal('show');
						}
					}
				});
			}
		}
	},

	setup_progress_dialog: function() {
		var me = this;
		frappe.call({
			method: "frappe.desk.user_progress.get_user_progress_slides",
			callback: function(r) {
				if(r.message) {
					let slides = r.message;
					if(slides.length && slides.map(s => parseInt(s.done)).includes(0)) {
						frappe.require("assets/frappe/js/frappe/ui/toolbar/user_progress_dialog.js", function() {
							me.progress_dialog = new frappe.setup.UserProgressDialog({
								slides: slides
							});
							$('.user-progress').removeClass('hide');
							$('.user-progress .dropdown-toggle').on('click', () => {
								me.progress_dialog.show();
							});

							if (frappe.boot.is_first_startup) {
								me.progress_dialog.show();
								frappe.call({
									method: "frappe.desk.page.setup_wizard.setup_wizard.reset_is_first_startup",
									args: {},
									callback: () => {}
								});
							}

						});
					}
				}
			},
			freeze: false
		});
	}
});

$.extend(frappe.ui.toolbar, {
	add_dropdown_button: function(parent, label, click, icon) {
		var menu = frappe.ui.toolbar.get_menu(parent);
		if(menu.find("li:not(.custom-menu)").length && !menu.find(".divider").length) {
			frappe.ui.toolbar.add_menu_divider(menu);
		}

		return $('
 '+label+'
')
			.insertBefore(menu.find(".divider"))
			.find("a")
			.click(function() {
				click.apply(this);
			});
	},
	get_menu: function(label) {
		return $("#navbar-" + label.toLowerCase());
	},
	add_menu_divider: function(menu) {
		menu = typeof menu == "string" ?
			frappe.ui.toolbar.get_menu(menu) : menu;

		$('

').prependTo(menu);
	}
});

frappe.ui.toolbar.clear_cache = function() {
	frappe.assets.clear_local_storage();
	frappe.call({
		method: 'frappe.sessions.clear',
		callback: function(r) {
			if(!r.exc) {
				frappe.show_alert({message:r.message, indicator:'green'});
				location.reload(true);
			}
		}
	})
	return false;
}

frappe.ui.toolbar.download_backup = function() {
	frappe.msgprint(__("Your download is being built, this may take a few moments..."));
	$c('frappe.utils.backups.get_backup',{},function(r,rt) {});
	return false;
}

frappe.ui.toolbar.show_about = function() {
	try {
		frappe.ui.misc.about();
	} catch(e) {
		console.log(e);
	}
	return false;
}
<|endoftext|>


<commit_before>function HandsontableDateEditorClass(instance) {
  this.isCellEdited = false;
  this.instance = instance;
  this.createElements();
  this.bindEvents();
}

Handsontable.helper.inherit(HandsontableDateEditorClass, HandsontableTextEditorClass);

/**
 * @see HandsontableTextEditorClass.prototype.createElements
 */
HandsontableDateEditorClass.prototype.createElements = function () {
  HandsontableTextEditorClass.prototype.createElements.call(this);

  this.datePickerdiv = $("<div>");
  this.datePickerdiv[0].style.position = 'absolute';
  this.datePickerdiv[0].style.top = 0;
  this.datePickerdiv[0].style.left = 0;
  this.datePickerdiv[0].style.zIndex = 99;
  this.instance.rootElement[0].appendChild(this.datePickerdiv[0]);

  var that = this;
  var defaultOptions = {
    dateFormat: "yy-mm-dd",
    showButtonPanel: true,
    changeMonth: true,
    changeYear: true,
    altField: this.TEXTAREA,
    onSelect: function () {
      that.finishEditing(false);
    }
  };
  this.datePickerdiv.datepicker(defaultOptions);
  this.datePickerdiv.hide();
}

/**
 * @see HandsontableTextEditorClass.prototype.beginEditing
 */
HandsontableDateEditorClass.prototype.beginEditing = function (row, col, prop, useOriginalValue, suffix) {
  HandsontableTextEditorClass.prototype.beginEditing.call(this, row, col, prop, useOriginalValue, suffix);
  this.showDatepicker();
}
/**
 * @see HandsontableTextEditorClass.prototype.finishEditing
 */
HandsontableDateEditorClass.prototype.finishEditing = function (isCancelled, ctrlDown) {
  this.hideDatepicker();
  HandsontableTextEditorClass.prototype.finishEditing.call(this, isCancelled, ctrlDown);
}

HandsontableDateEditorClass.prototype.showDatepicker = function () {
  var $td = $(this.instance.dateEditor.TD);
  var position = $td.position();
  this.datePickerdiv[0].style.top = (position.top + $td.height()) + 'px';
  this.datePickerdiv[0].style.left = position.left + 'px';

  var dateOptions = {
    defaultDate: this.originalValue || void 0
  };
  $.extend(dateOptions, this.cellProperties);
  this.datePickerdiv.datepicker("option", dateOptions);
  if (this.originalValue) {
    this.datePickerdiv.datepicker("setDate", this.originalValue);
  }
  this.datePickerdiv.show();
}

HandsontableDateEditorClass.prototype.hideDatepicker = function () {
  this.datePickerdiv.hide();
}

/**
 * Date editor (uses jQuery UI Datepicker)
 * @param {Object} instance Handsontable instance
 * @param {Element} td Table cell where to render
 * @param {Number} row
 * @param {Number} col
 * @param {String|Number} prop Row object property name
 * @param value Original value (remember to escape unsafe HTML before inserting to DOM!)
 * @param {Object} cellProperties Cell properites (shared by cell renderer and editor)
 */
Handsontable.DateEditor = function (instance, td, row, col, prop, value, cellProperties) {
  if (!instance.dateEditor) {
    instance.dateEditor = new HandsontableDateEditorClass(instance);
  }
  instance.dateEditor.bindTemporaryEvents(td, row, col, prop, value, cellProperties);
  return function (isCancelled) {
    instance.dateEditor.finishEditing(isCancelled);
  }
};<commit_msg>add missing semicolons<commit_after>function HandsontableDateEditorClass(instance) {
  this.isCellEdited = false;
  this.instance = instance;
  this.createElements();
  this.bindEvents();
}

Handsontable.helper.inherit(HandsontableDateEditorClass, HandsontableTextEditorClass);

/**
 * @see HandsontableTextEditorClass.prototype.createElements
 */
HandsontableDateEditorClass.prototype.createElements = function () {
  HandsontableTextEditorClass.prototype.createElements.call(this);

  this.datePickerdiv = $("<div>");
  this.datePickerdiv[0].style.position = 'absolute';
  this.datePickerdiv[0].style.top = 0;
  this.datePickerdiv[0].style.left = 0;
  this.datePickerdiv[0].style.zIndex = 99;
  this.instance.rootElement[0].appendChild(this.datePickerdiv[0]);

  var that = this;
  var defaultOptions = {
    dateFormat: "yy-mm-dd",
    showButtonPanel: true,
    changeMonth: true,
    changeYear: true,
    altField: this.TEXTAREA,
    onSelect: function () {
      that.finishEditing(false);
    }
  };
  this.datePickerdiv.datepicker(defaultOptions);
  this.datePickerdiv.hide();
};

/**
 * @see HandsontableTextEditorClass.prototype.beginEditing
 */
HandsontableDateEditorClass.prototype.beginEditing = function (row, col, prop, useOriginalValue, suffix) {
  HandsontableTextEditorClass.prototype.beginEditing.call(this, row, col, prop, useOriginalValue, suffix);
  this.showDatepicker();
};

/**
 * @see HandsontableTextEditorClass.prototype.finishEditing
 */
HandsontableDateEditorClass.prototype.finishEditing = function (isCancelled, ctrlDown) {
  this.hideDatepicker();
  HandsontableTextEditorClass.prototype.finishEditing.call(this, isCancelled, ctrlDown);
};

HandsontableDateEditorClass.prototype.showDatepicker = function () {
  var $td = $(this.instance.dateEditor.TD);
  var position = $td.position();
  this.datePickerdiv[0].style.top = (position.top + $td.height()) + 'px';
  this.datePickerdiv[0].style.left = position.left + 'px';

  var dateOptions = {
    defaultDate: this.originalValue || void 0
  };
  $.extend(dateOptions, this.cellProperties);
  this.datePickerdiv.datepicker("option", dateOptions);
  if (this.originalValue) {
    this.datePickerdiv.datepicker("setDate", this.originalValue);
  }
  this.datePickerdiv.show();
};

HandsontableDateEditorClass.prototype.hideDatepicker = function () {
  this.datePickerdiv.hide();
};

/**
 * Date editor (uses jQuery UI Datepicker)
 * @param {Object} instance Handsontable instance
 * @param {Element} td Table cell where to render
 * @param {Number} row
 * @param {Number} col
 * @param {String|Number} prop Row object property name
 * @param value Original value (remember to escape unsafe HTML before inserting to DOM!)
 * @param {Object} cellProperties Cell properites (shared by cell renderer and editor)
 */
Handsontable.DateEditor = function (instance, td, row, col, prop, value, cellProperties) {
  if (!instance.dateEditor) {
    instance.dateEditor = new HandsontableDateEditorClass(instance);
  }
  instance.dateEditor.bindTemporaryEvents(td, row, col, prop, value, cellProperties);
  return function (isCancelled) {
    instance.dateEditor.finishEditing(isCancelled);
  }
};<|endoftext|>


<commit_before>/*global define, esri, Proj4js*/
(function () {
	"use strict";

	function projectNumberPair(geometry, sourcePrj, destPrj) {
		/// Projects an array containing two numbers (representing a point).
		/// <param name="geometry" type="Number[]">An array containing two numbers.</param>
		/// <param name="sourcePrj" type="Proj4js.Proj">Source Projection</param>
		/// <param name="destPrj" type="Proj4js.Proj">Destination Projection</param>
		/// <returns type="Number[]">Returns a projected copy of the original array.</returns>
		var g, output;
		// Convert the input geometry (a coordinate pair array) into an object that can be converted by Proj4js.
		g = { x: geometry[0], y: geometry[1] };
		g = Proj4js.transform(sourcePrj, destPrj, g);
		output = [g.x, g.y];
		return output;
	}

	function projectArrays(points, sourcePrj, destPrj) {
		/// Given an array containing points (a point is represented by an array of two numbers), this function will project all points to a new geometry.
		/// <param name="points" type="Array">An jagged array of numbers.</param>
		/// <param name="sourcePrj" type="Proj4js.Proj">A Proj4js projection of the source spatial reference.</param>
		/// <param name="destPrj" type="Proj4js.Proj">A Proj4js projection of the destination spatial reference.</param>
		var output, i, l, point;
		output = [];

		for (i = 0, l = points.length; i < l; i += 1) {
			point = points[i];

			if (point[0] instanceof Array) {
				output.push(projectArrays(point, sourcePrj, destPrj));
			} else {
				output.push(projectNumberPair(point, sourcePrj, destPrj));
			}
		}

		return output;
	}

	function projectEsriGeometry(geometry, sourcePrj, destPrj) {
		/// Projects an esri.Geometry from one projection to another using Proj4js.
		/// <param name="geometry" type="esri.Geometry|object">An esri.Geometry object (or a JSON object that can be passed to a geometry constructor).</param>
		/// <param name="sourcePrj" type="Proj4js.Proj">Source Projection</param>
		/// <param name="destPrj" type="Proj4js.Proj">Destination Projection</param>
		/// <returns type="esri.Geometry">Returns a projected copy of the input geometry.</returns>

		var output;

		// Get the source projection from the geometry if not provided via parameter...
		if (!sourcePrj && geometry.spatialReference && geometry.spatialReference.wkid) {
			sourcePrj = new Proj4js.Proj(["EPSG", geometry.spatialReference.wkid].join(":"));
		}

		if (geometry.x !== undefined && geometry.y !== undefined) {
			output = Proj4js.transform(sourcePrj, destPrj, { x: geometry.x, y: geometry.y });
		} else if (geometry instanceof Array && geometry.length >= 2) {
			output = projectNumberPair(geometry, sourcePrj, destPrj);
		} else if (geometry.points !== undefined) { // multipoint
			output = { points: projectArrays(geometry.points, sourcePrj, destPrj) };
		} else if (geometry.paths !== undefined) { // polyline
			output = { paths: projectArrays(geometry.paths, sourcePrj, destPrj) };
		} else if (geometry.rings !== undefined) { // polygon
			output = { rings: projectArrays(geometry.rings, sourcePrj, destPrj) };
		}

		// Set the spatialReference property if the input geometry had this property defined...
		if (geometry.spatialReference) {
			output.spatialReference = { wkid: Number(destPrj.srsProjNumber) };
		}

		// Convert the output object into an esri.Geometry if that class is available.

		if (esri !== undefined && esri.geometry !== undefined && esri.geometry.fromJson !== undefined) {
			output = esri.geometry.fromJson(output);
		}

		return output;
	}

	// Add the projectEsriGeometry method to the Proj4js module.
	Proj4js.projectEsriGeometry = projectEsriGeometry;

	function Projector(inputProj, outputProj) {
		/// A class that can project an esri.Geometry from one projection to another.
		/// <param name="inputProj" type="Proj4js.Proj">The input projection system.</param>
		/// <param name="outputProj" type="Proj4js.Proj">The output projection system.</param>
		if (inputProj === null || inputProj === undefined || !(inputProj instanceof Proj4js.Proj)) {
			throw new Error("inputProj not defined");
		}
		if (outputProj === null || outputProj === undefined || !(outputProj instanceof Proj4js.Proj)) {
			throw new Error("outputProj not defined");
		}
		this.inputProj = inputProj;
		this.outputProj = outputProj;
	}

	Projector.prototype.project = function (geometry) {
		/// Projects a geometry from one projection to another.
		return projectEsriGeometry(geometry, this.inputProj, this.outputProj);
	};

	Projector.prototype.projectGeometries = function (geometries) {
		/// Projects an array of geometries.  Does not modify original array.
		/// <param name="geometries" type="esri.geometry.Geometry[]">An array of geometries.</param>
		/// <returns type="esri.geometry.Geometry[]" />

		var output = [], i, l, inGeometry, outGeometry;

		for (i = 0, l = geometries.length; i < l; i += 1) {
			inGeometry = geometries[i];
			outGeometry = this.project(inGeometry);
			output.push(outGeometry);
		}

		return output;
	};

	Projector.prototype.projectGraphics = function (graphics, perGraphicFunction) {
		/// Projects the geometries in an array of graphics.  Does not modify the original array.
		/// <param name="graphics" type="esri.Graphic[]">An array of graphics.</param>
		/// <param name="perGraphicFunction" type="function">
		/// Optional.  A function that will be executed for each graphic.  This function takes a single esri.Graphic parameter.
		/// </param>
		/// <returns type="esri.Graphic[]" />

		var output, inGraphic, outGraphic, i, l, perGraphicDefined = typeof (perGraphicFunction) === "function";

		if (graphics !== null && graphics !== undefined) {
			output = [];
			if (graphics.length) {
				for (i = 0, l = graphics.length; i < l; i += 1) {
					inGraphic = graphics[i];
					outGraphic = inGraphic.toJson();
					if (outGraphic.geometry) {
						outGraphic.geometry = this.project(outGraphic.geometry);
					}
					outGraphic = new esri.Graphic(outGraphic);
					if (perGraphicDefined) {
						perGraphicFunction(outGraphic);
					}

					output.push(outGraphic);
				}
			}
		}

		return output;
	};

	Proj4js.EsriProjector = Projector;
}());<commit_msg>clientProjection now supports envelopes.<commit_after>/*global define, esri, Proj4js*/
(function () {
	"use strict";

	function projectNumberPair(geometry, sourcePrj, destPrj) {
		/// Projects an array containing two numbers (representing a point).
		/// <param name="geometry" type="Number[]">An array containing two numbers.</param>
		/// <param name="sourcePrj" type="Proj4js.Proj">Source Projection</param>
		/// <param name="destPrj" type="Proj4js.Proj">Destination Projection</param>
		/// <returns type="Number[]">Returns a projected copy of the original array.</returns>
		var g, output;
		// Convert the input geometry (a coordinate pair array) into an object that can be converted by Proj4js.
		g = { x: geometry[0], y: geometry[1] };
		g = Proj4js.transform(sourcePrj, destPrj, g);
		output = [g.x, g.y];
		return output;
	}

	function projectArrays(points, sourcePrj, destPrj) {
		/// Given an array containing points (a point is represented by an array of two numbers), this function will project all points to a new geometry.
		/// <param name="points" type="Array">An jagged array of numbers.</param>
		/// <param name="sourcePrj" type="Proj4js.Proj">A Proj4js projection of the source spatial reference.</param>
		/// <param name="destPrj" type="Proj4js.Proj">A Proj4js projection of the destination spatial reference.</param>
		var output, i, l, point;
		output = [];

		for (i = 0, l = points.length; i < l; i += 1) {
			point = points[i];

			if (point[0] instanceof Array) {
				output.push(projectArrays(point, sourcePrj, destPrj));
			} else {
				output.push(projectNumberPair(point, sourcePrj, destPrj));
			}
		}

		return output;
	}

	function projectEnvelope(envelope, sourcePrj, destPrj) {
		var output, min, max;
		if (envelope.xmin !== undefined && envelope.ymin !== undefined && envelope.xmax !== undefined && envelope.ymax !== undefined) {
			// Create min and max points.
			min = { x: envelope.xmin, y: envelope.ymin };
			max = { x: envelope.xmax, y: envelope.ymax };

			// Project the min and max points.
			min = Proj4js.transform(sourcePrj, destPrj, min);
			max = Proj4js.transform(sourcePrj, destPrj, max);

			output = {
				xmin: min.x,
				ymin: min.y,
				xmax: max.x,
				ymax: max.y
			};
		} else {
			throw new Error("Invalid envelope object.");
		}

		return output;
	}

	function projectEsriGeometry(geometry, sourcePrj, destPrj) {
		/// Projects an esri.Geometry from one projection to another using Proj4js.
		/// <param name="geometry" type="esri.Geometry|object">An esri.Geometry object (or a JSON object that can be passed to a geometry constructor).</param>
		/// <param name="sourcePrj" type="Proj4js.Proj">Source Projection</param>
		/// <param name="destPrj" type="Proj4js.Proj">Destination Projection</param>
		/// <returns type="esri.Geometry">Returns a projected copy of the input geometry.</returns>

		var output;

		// Get the source projection from the geometry if not provided via parameter...
		if (!sourcePrj && geometry.spatialReference && geometry.spatialReference.wkid) {
			sourcePrj = new Proj4js.Proj(["EPSG", geometry.spatialReference.wkid].join(":"));
		}

		if (geometry.x !== undefined && geometry.y !== undefined) {
			output = Proj4js.transform(sourcePrj, destPrj, { x: geometry.x, y: geometry.y });
		} else if (geometry.xmin !== undefined && geometry.ymin !== undefined && geometry.xmax !== undefined && geometry.ymax !== undefined) {
			output = projectEnvelope(geometry, sourcePrj, destPrj);
		} else if (geometry instanceof Array && geometry.length >= 2) {
			output = projectNumberPair(geometry, sourcePrj, destPrj);
		} else if (geometry.points !== undefined) { // multipoint
			output = { points: projectArrays(geometry.points, sourcePrj, destPrj) };
		} else if (geometry.paths !== undefined) { // polyline
			output = { paths: projectArrays(geometry.paths, sourcePrj, destPrj) };
		} else if (geometry.rings !== undefined) { // polygon
			output = { rings: projectArrays(geometry.rings, sourcePrj, destPrj) };
		}

		// Set the spatialReference property if the input geometry had this property defined...
		if (geometry.spatialReference) {
			output.spatialReference = { wkid: Number(destPrj.srsProjNumber) };
		}

		// Convert the output object into an esri.Geometry if that class is available.

		if (esri !== undefined && esri.geometry !== undefined && esri.geometry.fromJson !== undefined) {
			output = esri.geometry.fromJson(output);
		}

		return output;
	}

	// Add the projectEsriGeometry method to the Proj4js module.
	Proj4js.projectEsriGeometry = projectEsriGeometry;

	function Projector(inputProj, outputProj) {
		/// A class that can project an esri.Geometry from one projection to another.
		/// <param name="inputProj" type="Proj4js.Proj">The input projection system.</param>
		/// <param name="outputProj" type="Proj4js.Proj">The output projection system.</param>
		if (inputProj === null || inputProj === undefined || !(inputProj instanceof Proj4js.Proj)) {
			throw new Error("inputProj not defined");
		}
		if (outputProj === null || outputProj === undefined || !(outputProj instanceof Proj4js.Proj)) {
			throw new Error("outputProj not defined");
		}
		this.inputProj = inputProj;
		this.outputProj = outputProj;
	}

	Projector.prototype.project = function (geometry) {
		/// Projects a geometry from one projection to another.
		return projectEsriGeometry(geometry, this.inputProj, this.outputProj);
	};

	Projector.prototype.projectGeometries = function (geometries) {
		/// Projects an array of geometries.  Does not modify original array.
		/// <param name="geometries" type="esri.geometry.Geometry[]">An array of geometries.</param>
		/// <returns type="esri.geometry.Geometry[]" />

		var output = [], i, l, inGeometry, outGeometry;

		for (i = 0, l = geometries.length; i < l; i += 1) {
			inGeometry = geometries[i];
			outGeometry = this.project(inGeometry);
			output.push(outGeometry);
		}

		return output;
	};

	Projector.prototype.projectGraphics = function (graphics, perGraphicFunction) {
		/// Projects the geometries in an array of graphics.  Does not modify the original array.
		/// <param name="graphics" type="esri.Graphic[]">An array of graphics.</param>
		/// <param name="perGraphicFunction" type="function">
		/// Optional.  A function that will be executed for each graphic.  This function takes a single esri.Graphic parameter.
		/// </param>
		/// <returns type="esri.Graphic[]" />

		var output, inGraphic, outGraphic, i, l, perGraphicDefined = typeof (perGraphicFunction) === "function";

		if (graphics !== null && graphics !== undefined) {
			output = [];
			if (graphics.length) {
				for (i = 0, l = graphics.length; i < l; i += 1) {
					inGraphic = graphics[i];
					outGraphic = inGraphic.toJson();
					if (outGraphic.geometry) {
						outGraphic.geometry = this.project(outGraphic.geometry);
					}
					outGraphic = new esri.Graphic(outGraphic);
					if (perGraphicDefined) {
						perGraphicFunction(outGraphic);
					}

					output.push(outGraphic);
				}
			}
		}

		return output;
	};

	Proj4js.EsriProjector = Projector;
} ());<|endoftext|>


<commit_before>// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt

frappe.ui.form.on('Blog Post', {
	refresh: function(frm) {
		generate_google_search_preview(frm);
	},
	title: function(frm) {
		generate_google_search_preview(frm);
	},
	meta_description: function(frm) {
		generate_google_search_preview(frm);
	},
	blog_intro: function(frm) {
		generate_google_search_preview(frm);
	}
});

function generate_google_search_preview(frm) {
	let google_preview = frm.get_field("google_preview");
	let seo_title = (frm.doc.title).slice(0, 60);
	let seo_description =  (frm.doc.meta_description || frm.doc.blog_intro || "").slice(0, 160);
	let date = frm.doc.published_on ? new frappe.datetime.datetime(frm.doc.published_on).moment.format('ll') + ' - ' : '';
	let route_array = frm.doc.route.split('/');
	route_array.pop();

	google_preview.html(`
		<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400&display=swap" rel="stylesheet">

					${frappe.boot.sitename}
					 › ${route_array.join(' › ')}

						${ seo_title }

					${ date } ${ seo_description }

	`);
}
<commit_msg>fix: Console error in generate_google_search_preview() when route is not set<commit_after>// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt

frappe.ui.form.on('Blog Post', {
	refresh: function(frm) {
		generate_google_search_preview(frm);
	},
	title: function(frm) {
		generate_google_search_preview(frm);
	},
	meta_description: function(frm) {
		generate_google_search_preview(frm);
	},
	blog_intro: function(frm) {
		generate_google_search_preview(frm);
	}
});

function generate_google_search_preview(frm) {
	let google_preview = frm.get_field("google_preview");
	let seo_title = (frm.doc.title).slice(0, 60);
	let seo_description =  (frm.doc.meta_description || frm.doc.blog_intro || "").slice(0, 160);
	let date = frm.doc.published_on ? new frappe.datetime.datetime(frm.doc.published_on).moment.format('ll') + ' - ' : '';
	let route_array = (frm.doc.route) ? frm.doc.route.split('/') : [];
	route_array.pop();

	google_preview.html(`
		<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400&display=swap" rel="stylesheet">

					${frappe.boot.sitename}
					 › ${route_array.join(' › ')}

						${ seo_title }

					${ date } ${ seo_description }

	`);
}
<|endoftext|>