<commit_before>'use strict';
const logger = require('logger');
const SubscriptionModel = require('models/subscription');
const StadisticModel = require('models/stadistic');
const GenericError = require('errors/genericError');
const ctRegisterMicroservice = require('ct-register-microservice-node');

var JSONAPIDeserializer = require('jsonapi-serializer').Deserializer;
var deserializer = function (obj) {
  return function (callback) {
    new JSONAPIDeserializer({
      keyForAttribute: 'camelCase'
    }).deserialize(obj, callback);
  };
};

class StatisticsService {

  static * getUsers(startDate, endDate) {
    try {
      const result = yield ctRegisterMicroservice.requestToMicroservice({
        uri: `/user/obtain/all-users?start=${startDate.toISOString().substring(0, 10)}&end=${endDate.toISOString().substring(0, 10)}`,
        method: 'GET',
        json: true
      });

      return yield deserializer(result);
    } catch (err) {
      logger.error('Error obtaining users:', err);
      throw new GenericError(500, 'Error obtaining data');
    }

  }

  static * getTopSubscriptions(startDate, endDate) {
    logger.debug(`Obtaining getTopSubscriptions with startDate ${startDate} and endDate ${endDate}`);
    const topSubs = {};
    const defaultFilter = {
      createdAt: {
        $gte: startDate,
        $lt: endDate
      }
    };
    topSubs.geostore = yield SubscriptionModel.count(Object.assign({}, defaultFilter, {
      'params.geostore': {
        $ne: null
      }
    }));
    topSubs.country = yield SubscriptionModel.count(Object.assign({}, defaultFilter, {
      'params.iso.country': {
        $ne: null
      },
      'params.iso.region': null
    }));
    topSubs.region = yield SubscriptionModel.count(Object.assign({}, defaultFilter, {
      'params.iso.region': {
        $ne: null
      }
    }));
    topSubs.wdpa = yield SubscriptionModel.count(Object.assign({}, defaultFilter, {
      'params.wdpaid': {
        $ne: null
      }
    }));
    topSubs.use = yield SubscriptionModel.count(Object.assign({}, defaultFilter, {
      'params.use': {
        $ne: null
      }
    }));

    return topSubs;
  }

  static * infoGroupSubscriptions(startDate, endDate, application) {
    logger.debug(`Obtaining group subscriptions with startDate ${startDate} and endDate ${endDate} and application ${application}`);
    const info = {};

    const filter = {
      createdAt: {
        $gte: startDate,
        $lt: endDate
      },
      datasets: {
        $ne: []
      },
      application
    };

    const subscriptions = yield SubscriptionModel.find(filter);
    const data = {};
    logger.debug('Subscriptions', subscriptions.length);
    subscriptions.forEach(sub => {
      logger.debug('Iterating subs', sub);
      sub.datasets.forEach(dat => {
        logger.debug('Iterating dataset', data);
        if (!data[dat]) {
          data[dat] = {
            country: 0,
            region: 0,
            use: 0,
            wdpa: 0,
            geostore: 0,
            countries: {},
            regions: {},
            wdpas: {},
            countryTop: {
              name: null,
              value: 0
            },
            regionTop: {
              nameRegion: null,
              nameCountry: null,
              value: 0
            },
            wdpaTop: {
              name: null,
              value: 0
            }
          };
        }
        if (sub.params.geostore) {
          data[dat].geostore = data[dat].geostore + 1;
        } else if (sub.params.iso && (sub.params.iso.country || sub.params.iso.region)) {
          if (sub.params.iso.region) {
            data[dat].region = data[dat].region + 1;
            if (!data[dat].regions[sub.params.iso.region]) {
              data[dat].regions[sub.params.iso.region] = 0;
            }
            data[dat].regions[sub.params.iso.region] = data[dat].regions[sub.params.iso.region] +1;
            if (data[dat].regions[sub.params.iso.region] > data[dat].regionTop.value) {
              data[dat].regionTop.nameRegion = sub.params.iso.region;
              data[dat].regionTop.nameCountry = sub.params.iso.country;
              data[dat].regionTop.value = data[dat].regions[sub.params.iso.region];
            }
          } else {
            data[dat].country = data[dat].country + 1;
            if (!data[dat].countries[sub.params.iso.country]) {
              data[dat].countries[sub.params.iso.country] = 0;
            }
            data[dat].countries[sub.params.iso.country] = data[dat].countries[sub.params.iso.country] +1;
            if (data[dat].countries[sub.params.iso.country] > data[dat].countryTop.value) {
              data[dat].countryTop.name = sub.params.iso.country;
              data[dat].countryTop.value = data[dat].countries[sub.params.iso.country];
            }
          }
        } else if(sub.params.wdpaid) {
          data[dat].wdpa = data[dat].wdpa + 1;
          if (!data[dat].wdpas[sub.params.wdpaid]) {
            data[dat].wdpas[sub.params.wdpaid] = 0;
          }
          data[dat].wdpas[sub.params.wdpaid] = data[dat].wdpas[sub.params.wdpaid] +1;
          if (data[dat].wdpas[sub.params.wdpaid] > data[dat].wdpaTop.value) {
            data[dat].wdpaTop.id = sub.params.wdpaid;
            data[dat].wdpaTop.value = data[dat].wdpas[sub.params.wdpaid];
          }
        } else {
          data[dat].use = data[dat].use + 1;
        }
      });

    });

    return data;
  }

  static * infoSubscriptions(startDate, endDate) {
    logger.debug(`Obtaining infoSubscriptions with startDate ${startDate} and endDate ${endDate}`);
    const info = {};

    const defaultFilter = {
      createdAt: {
        $gte: startDate,
        $lt: endDate
      }
    };

    info.numSubscriptions = yield SubscriptionModel.count(defaultFilter);
    info.totalSubscriptions = yield SubscriptionModel.count();
    logger.debug(SubscriptionModel.aggregate([{
      $group: {
        _id: '$userId'
      }
    }, {
      $group: {
        _id: 1,
        count: {
          $sum: 1
        }
      }
    }]));
    info.usersWithSubscriptions = yield SubscriptionModel.aggregate([{
      $group: {
        _id: '$userId'
      }
    }, {
      $group: {
        _id: 1,
        count: {
          $sum: 1
        }
      }
    }]).exec();
    if (info.usersWithSubscriptions) {
      info.usersWithSubscriptions = info.usersWithSubscriptions[0].count;
    }
    info.totalEmailsSentInThisQ = yield StadisticModel.count(defaultFilter);
    info.totalEmailsSended = yield StadisticModel.count();

    return info;
  }

  static * getNewUsersWithSubs(startDate, endDate, users) {
    let usersCount = 0;
    if (users)  {
      for (let i = 0, length = users.length; i < length; i++) {
        usersCount += yield SubscriptionModel.count({
          userId: users[i].id
        });
      }
    }
    return usersCount;
  }

  static * getStatistics(startDate, endDate, application='gfw') {
    const users = yield StatisticsService.getUsers(startDate, endDate);
    const topSubs = yield StatisticsService.getTopSubscriptions(startDate, endDate);
    const info = yield StatisticsService.infoSubscriptions(startDate, endDate);
    const groupStatistics = yield StatisticsService.infoGroupSubscriptions(startDate, endDate, application);
    const usersWithSubscription = yield StatisticsService.getNewUsersWithSubs(startDate, endDate, users);
    return {
      topSubscriptions: topSubs,
      info,
      usersWithSubscription,
      newUsers: users ? users.length : 0,
      groupStatistics
    };
  }

}

module.exports = StatisticsService;
<commit_msg>change name att<commit_after>'use strict';
const logger = require('logger');
const SubscriptionModel = require('models/subscription');
const StadisticModel = require('models/stadistic');
const GenericError = require('errors/genericError');
const ctRegisterMicroservice = require('ct-register-microservice-node');

var JSONAPIDeserializer = require('jsonapi-serializer').Deserializer;
var deserializer = function (obj) {
  return function (callback) {
    new JSONAPIDeserializer({
      keyForAttribute: 'camelCase'
    }).deserialize(obj, callback);
  };
};

class StatisticsService {

  static * getUsers(startDate, endDate) {
    try {
      const result = yield ctRegisterMicroservice.requestToMicroservice({
        uri: `/user/obtain/all-users?start=${startDate.toISOString().substring(0, 10)}&end=${endDate.toISOString().substring(0, 10)}`,
        method: 'GET',
        json: true
      });

      return yield deserializer(result);
    } catch (err) {
      logger.error('Error obtaining users:', err);
      throw new GenericError(500, 'Error obtaining data');
    }

  }

  static * getTopSubscriptions(startDate, endDate) {
    logger.debug(`Obtaining getTopSubscriptions with startDate ${startDate} and endDate ${endDate}`);
    const topSubs = {};
    const defaultFilter = {
      createdAt: {
        $gte: startDate,
        $lt: endDate
      }
    };
    topSubs.geostore = yield SubscriptionModel.count(Object.assign({}, defaultFilter, {
      'params.geostore': {
        $ne: null
      }
    }));
    topSubs.country = yield SubscriptionModel.count(Object.assign({}, defaultFilter, {
      'params.iso.country': {
        $ne: null
      },
      'params.iso.region': null
    }));
    topSubs.region = yield SubscriptionModel.count(Object.assign({}, defaultFilter, {
      'params.iso.region': {
        $ne: null
      }
    }));
    topSubs.wdpa = yield SubscriptionModel.count(Object.assign({}, defaultFilter, {
      'params.wdpaid': {
        $ne: null
      }
    }));
    topSubs.use = yield SubscriptionModel.count(Object.assign({}, defaultFilter, {
      'params.use': {
        $ne: null
      }
    }));

    return topSubs;
  }

  static * infoGroupSubscriptions(startDate, endDate, application) {
    logger.debug(`Obtaining group subscriptions with startDate ${startDate} and endDate ${endDate} and application ${application}`);
    const info = {};

    const filter = {
      createdAt: {
        $gte: startDate,
        $lt: endDate
      },
      datasets: {
        $ne: []
      },
      application
    };

    const subscriptions = yield SubscriptionModel.find(filter);
    const data = {};
    logger.debug('Subscriptions', subscriptions.length);
    subscriptions.forEach(sub => {
      logger.debug('Iterating subs', sub);
      sub.datasets.forEach(dat => {
        logger.debug('Iterating dataset', data);
        if (!data[dat]) {
          data[dat] = {
            country: 0,
            region: 0,
            use: 0,
            wdpa: 0,
            geostore: 0,
            countries: {},
            regions: {},
            wdpas: {},
            countryTop: {
              name: null,
              value: 0
            },
            regionTop: {
              nameRegion: null,
              nameCountry: null,
              value: 0
            },
            wdpaTop: {
              id: null,
              value: 0
            }
          };
        }
        if (sub.params.geostore) {
          data[dat].geostore = data[dat].geostore + 1;
        } else if (sub.params.iso && (sub.params.iso.country || sub.params.iso.region)) {
          if (sub.params.iso.region) {
            data[dat].region = data[dat].region + 1;
            if (!data[dat].regions[sub.params.iso.region]) {
              data[dat].regions[sub.params.iso.region] = 0;
            }
            data[dat].regions[sub.params.iso.region] = data[dat].regions[sub.params.iso.region] +1;
            if (data[dat].regions[sub.params.iso.region] > data[dat].regionTop.value) {
              data[dat].regionTop.nameRegion = sub.params.iso.region;
              data[dat].regionTop.nameCountry = sub.params.iso.country;
              data[dat].regionTop.value = data[dat].regions[sub.params.iso.region];
            }
          } else {
            data[dat].country = data[dat].country + 1;
            if (!data[dat].countries[sub.params.iso.country]) {
              data[dat].countries[sub.params.iso.country] = 0;
            }
            data[dat].countries[sub.params.iso.country] = data[dat].countries[sub.params.iso.country] +1;
            if (data[dat].countries[sub.params.iso.country] > data[dat].countryTop.value) {
              data[dat].countryTop.name = sub.params.iso.country;
              data[dat].countryTop.value = data[dat].countries[sub.params.iso.country];
            }
          }
        } else if(sub.params.wdpaid) {
          data[dat].wdpa = data[dat].wdpa + 1;
          if (!data[dat].wdpas[sub.params.wdpaid]) {
            data[dat].wdpas[sub.params.wdpaid] = 0;
          }
          data[dat].wdpas[sub.params.wdpaid] = data[dat].wdpas[sub.params.wdpaid] +1;
          if (data[dat].wdpas[sub.params.wdpaid] > data[dat].wdpaTop.value) {
            data[dat].wdpaTop.id = sub.params.wdpaid;
            data[dat].wdpaTop.value = data[dat].wdpas[sub.params.wdpaid];
          }
        } else {
          data[dat].use = data[dat].use + 1;
        }
      });

    });

    return data;
  }

  static * infoSubscriptions(startDate, endDate) {
    logger.debug(`Obtaining infoSubscriptions with startDate ${startDate} and endDate ${endDate}`);
    const info = {};

    const defaultFilter = {
      createdAt: {
        $gte: startDate,
        $lt: endDate
      }
    };

    info.numSubscriptions = yield SubscriptionModel.count(defaultFilter);
    info.totalSubscriptions = yield SubscriptionModel.count();
    logger.debug(SubscriptionModel.aggregate([{
      $group: {
        _id: '$userId'
      }
    }, {
      $group: {
        _id: 1,
        count: {
          $sum: 1
        }
      }
    }]));
    info.usersWithSubscriptions = yield SubscriptionModel.aggregate([{
      $group: {
        _id: '$userId'
      }
    }, {
      $group: {
        _id: 1,
        count: {
          $sum: 1
        }
      }
    }]).exec();
    if (info.usersWithSubscriptions) {
      info.usersWithSubscriptions = info.usersWithSubscriptions[0].count;
    }
    info.totalEmailsSentInThisQ = yield StadisticModel.count(defaultFilter);
    info.totalEmailsSended = yield StadisticModel.count();

    return info;
  }

  static * getNewUsersWithSubs(startDate, endDate, users) {
    let usersCount = 0;
    if (users)  {
      for (let i = 0, length = users.length; i < length; i++) {
        usersCount += yield SubscriptionModel.count({
          userId: users[i].id
        });
      }
    }
    return usersCount;
  }

  static * getStatistics(startDate, endDate, application='gfw') {
    const users = yield StatisticsService.getUsers(startDate, endDate);
    const topSubs = yield StatisticsService.getTopSubscriptions(startDate, endDate);
    const info = yield StatisticsService.infoSubscriptions(startDate, endDate);
    const groupStatistics = yield StatisticsService.infoGroupSubscriptions(startDate, endDate, application);
    const usersWithSubscription = yield StatisticsService.getNewUsersWithSubs(startDate, endDate, users);
    return {
      topSubscriptions: topSubs,
      info,
      usersWithSubscription,
      newUsers: users ? users.length : 0,
      groupStatistics
    };
  }

}

module.exports = StatisticsService;
<|endoftext|>


<commit_before>module.exports = {

//---------------------------------------------------------------------
// Action Name
//
// This is the name of the action displayed in the editor.
//---------------------------------------------------------------------

name: "Check Variable",

//---------------------------------------------------------------------
// Action Section
//
// This is the section the action will fall into.
//---------------------------------------------------------------------

section: "Conditions",

//---------------------------------------------------------------------
// Action Subtitle
//
// This function generates the subtitle displayed next to the name.
//---------------------------------------------------------------------

subtitle: function(data) {
	const comparisons = ["Exists", "Equals", "Equals Exactly", "Less Than", "Greater Than", "Includes", "Matches Regex", "Length is Bigger Than", "Length is Smaller Than", "Length is Equals", "Starts With", "Ends With", "Matches Full Regex"];
	const results = ["Continue Actions", "Stop Action Sequence", "Jump To Action", "Jump Forward Actions"];
	return `${comparisons[parseInt(data.comparison)]} | If True: ${results[parseInt(data.iftrue)]} ~ If False: ${results[parseInt(data.iffalse)]}`;
},

//---------------------------------------------------------------------
	 // DBM Mods Manager Variables (Optional but nice to have!)
	 //
	 // These are variables that DBM Mods Manager uses to show information
	 // about the mods for people to see in the list.
	 //---------------------------------------------------------------------

	 // Who made the mod (If not set, defaults to "DBM Mods")
	 author: "DBM, EGGSY, MrGold, Lasse, ZockerNico", //UI fixed by MrGold

	 // The version of the mod (Defaults to 1.0.0)
	 version: "1.9.6", //Added in 1.9.1

	 // A short description to show on the mod line for this mod (Must be on a single line)
	 short_description: "Added more options to default action.",

	 // If it depends on any other mods by name, ex: WrexMODS if the mod uses something from WrexMods

	 //---------------------------------------------------------------------

//---------------------------------------------------------------------
// Action Fields
//
// These are the fields for the action. These fields are customized
// by creating elements with corresponding IDs in the HTML. These
// are also the names of the fields stored in the action's JSON data.
//---------------------------------------------------------------------

fields: ["storage", "varName", "comparison", "value", "iftrue", "iftrueVal", "iffalse", "iffalseVal"],

//---------------------------------------------------------------------
// Command HTML
//
// This function returns a string containing the HTML used for
// editting actions.
//
// The "isEvent" parameter will be true if this action is being used
// for an event. Due to their nature, events lack certain information,
// so edit the HTML to reflect this.
//
// The "data" parameter stores constants for select elements to use.
// Each is an array: index 0 for commands, index 1 for events.
// The names are: sendTargets, members, roles, channels,
//                messages, servers, variables
//---------------------------------------------------------------------

html: function(isEvent, data) {
	return `

This action has been modified by DBM Mods.

		Source Variable:

		<select id="storage" class="round" onchange="glob.refreshVariableList(this)">
			${data.variables[1]}
		</select>

		Variable Name:

		<input id="varName" class="round" type="text" list="variableList">

		Comparison Type:

		<select id="comparison" class="round" onchange="glob.onChange1(this)">
			<option value="0" selected>Exists</option>
			<option value="1">Equals</option>
			<option value="2">Equals Exactly</option>
			<option value="3">Less Than</option>
			<option value="4">Greater Than</option>
			<option value="5">Includes</option>
			<option value="6">Matches Regex</option>
			<option value="12">Matches Full Regex</option>
			<option value="7">Length is Bigger Than</option>
			<option value="8">Length is Smaller Than</option>
			<option value="9">Length is Equals</option>
			<option value="10">Starts With</option>
			<option value="11">Ends With</option>
		</select>

		Value to Compare to:

		<input id="value" class="round" type="text" name="is-eval" placeholder="">

	${data.conditions[0]}

`
},

//---------------------------------------------------------------------
// Action Editor Init Code
//
// When the HTML is first applied to the action editor, this code
// is also run. This helps add modifications or setup reactionary
// functions for the DOM elements.
//---------------------------------------------------------------------

init: function() {
	const {glob, document} = this;

	glob.onChange1 = function(event) {
		if(parseInt(event.value) == 0) {
			document.getElementById('directValue').style.display = 'none';
		} else {
			document.getElementById('directValue').style.display = null;
		};
		switch(parseInt(event.value)) {
			case 6:
				document.getElementById('value').placeholder = "('My'|'Regex')";
				break;
			case 12:
				document.getElementById('value').placeholder = "/('My'|'Regex')\\w+/igm";
				break;
			default:
				document.getElementById('value').placeholder = "";
		};
	};

	glob.onChange1(document.getElementById('comparison'));
	glob.refreshVariableList(document.getElementById('storage'));
	glob.onChangeTrue(document.getElementById('iftrue'));
	glob.onChangeFalse(document.getElementById('iffalse'));
},

//---------------------------------------------------------------------
// Action Bot Function
//
// This is the function for the action within the Bot's Action class.
// Keep in mind event calls won't have access to the "msg" parameter,
// so be sure to provide checks for variable existance.
//---------------------------------------------------------------------

action: function(cache) {
	const data = cache.actions[cache.index];
	const type = parseInt(data.storage);
	const varName = this.evalMessage(data.varName, cache);
	const variable = this.getVariable(type, varName, cache);
	let result = false;
	if(variable) {
		const val1 = variable;
		const compare = parseInt(data.comparison);
		let val2 = this.evalMessage(data.value, cache);
		if(compare !== 6) val2 = this.eval(val2, cache);
		if(val2 === false) val2 = this.evalMessage(data.value, cache);
		switch(compare) {
			case 0:
				result = Boolean(val1 !== undefined);
				break;
			case 1:
				result = Boolean(val1 == val2);
				break;
			case 2:
				result = Boolean(val1 === val2);
				break;
			case 3:
				result = Boolean(val1 < val2);
				break;
			case 4:
				result = Boolean(val1 > val2);
				break;
			case 5:
				if(typeof(val1.includes) === 'function') {
					result = Boolean(val1.includes(val2));
				}
				break;
			case 6:
				result = Boolean(val1.match(new RegExp('^' + val2 + '$', 'i')));
				break;
			case 7:
				result = Boolean(val1.length > val2);
				break;
			case 8:
				result = Boolean(val1.length < val2);
				break;
			case 9: //Added by Lasse
			  	result = Boolean(val1.length == val2);
			  	break;
			case 10: //Added by MrGold
			  	result = Boolean(val1.startsWith(val2));
			  	break;
			case 11: //Added by MrGold
			  	result = Boolean(val1.endsWith(val2));
			  	break;
			case 12: //Added by ZockerNico
				result = Boolean(val1.match(new RegExp(val2)));
				break;
		}
	}
	this.executeResults(result, data, cache);
},

//---------------------------------------------------------------------
// Action Bot Mod
//
// Upon initialization of the bot, this code is run. Using the bot's
// DBM namespace, one can add/modify existing functions if necessary.
// In order to reduce conflictions between mods, be sure to alias
// functions you wish to overwrite.
//---------------------------------------------------------------------

mod: function(DBM) {
}

}; // End of module
<commit_msg>Added <= and >= options<commit_after>module.exports = {

//---------------------------------------------------------------------
// Action Name
//
// This is the name of the action displayed in the editor.
//---------------------------------------------------------------------

name: "Check Variable",

//---------------------------------------------------------------------
// Action Section
//
// This is the section the action will fall into.
//---------------------------------------------------------------------

section: "Conditions",

//---------------------------------------------------------------------
// Action Subtitle
//
// This function generates the subtitle displayed next to the name.
//---------------------------------------------------------------------

subtitle: function(data) {
	const comparisons = ["Exists", "Equals", "Equals Exactly", "Less Than", "Less Than or Equal to", "Greater Than", "Greater Than or Equal to", "Includes", "Matches Regex", "Length is Bigger Than", "Length is Smaller Than", "Length is Equals", "Starts With", "Ends With", "Matches Full Regex"];
	const results = ["Continue Actions", "Stop Action Sequence", "Jump To Action", "Jump Forward Actions"];
	return `${comparisons[parseInt(data.comparison)]} | If True: ${results[parseInt(data.iftrue)]} ~ If False: ${results[parseInt(data.iffalse)]}`;
},

//---------------------------------------------------------------------
	 // DBM Mods Manager Variables (Optional but nice to have!)
	 //
	 // These are variables that DBM Mods Manager uses to show information
	 // about the mods for people to see in the list.
	 //---------------------------------------------------------------------

	 // Who made the mod (If not set, defaults to "DBM Mods")
	 author: "DBM, EGGSY, MrGold, Lasse, ZockerNico, TheMonDon", //UI fixed by MrGold

	 // The version of the mod (Defaults to 1.0.0)
	 version: "1.9.6", //Added in 1.9.1

	 // A short description to show on the mod line for this mod (Must be on a single line)
	 short_description: "Added more options to default action.",

	 // If it depends on any other mods by name, ex: WrexMODS if the mod uses something from WrexMods

	 //---------------------------------------------------------------------

//---------------------------------------------------------------------
// Action Fields
//
// These are the fields for the action. These fields are customized
// by creating elements with corresponding IDs in the HTML. These
// are also the names of the fields stored in the action's JSON data.
//---------------------------------------------------------------------

fields: ["storage", "varName", "comparison", "value", "iftrue", "iftrueVal", "iffalse", "iffalseVal"],

//---------------------------------------------------------------------
// Command HTML
//
// This function returns a string containing the HTML used for
// editting actions.
//
// The "isEvent" parameter will be true if this action is being used
// for an event. Due to their nature, events lack certain information,
// so edit the HTML to reflect this.
//
// The "data" parameter stores constants for select elements to use.
// Each is an array: index 0 for commands, index 1 for events.
// The names are: sendTargets, members, roles, channels,
//                messages, servers, variables
//---------------------------------------------------------------------

html: function(isEvent, data) {
	return `

This action has been modified by DBM Mods.

		Source Variable:

		<select id="storage" class="round" onchange="glob.refreshVariableList(this)">
			${data.variables[1]}
		</select>

		Variable Name:

		<input id="varName" class="round" type="text" list="variableList">

		Comparison Type:

		<select id="comparison" class="round" onchange="glob.onChange1(this)">
			<option value="0" selected>Exists</option>
			<option value="1">Equals</option>
			<option value="2">Equals Exactly</option>
			<option value="3">Less Than</option>
			<option value="13">Less Than or Equal to</option>
			<option value="4">Greater Than</option>
			<option value="14">Greater Than or Equal to</option>
			<option value="5">Includes</option>
			<option value="6">Matches Regex</option>
			<option value="12">Matches Full Regex</option>
			<option value="7">Length is Bigger Than</option>
			<option value="8">Length is Smaller Than</option>
			<option value="9">Length is Equals</option>
			<option value="10">Starts With</option>
			<option value="11">Ends With</option>
		</select>

		Value to Compare to:

		<input id="value" class="round" type="text" name="is-eval" placeholder="">

	${data.conditions[0]}

`
},

//---------------------------------------------------------------------
// Action Editor Init Code
//
// When the HTML is first applied to the action editor, this code
// is also run. This helps add modifications or setup reactionary
// functions for the DOM elements.
//---------------------------------------------------------------------

init: function() {
	const {glob, document} = this;

	glob.onChange1 = function(event) {
		if(parseInt(event.value) == 0) {
			document.getElementById('directValue').style.display = 'none';
		} else {
			document.getElementById('directValue').style.display = null;
		};
		switch(parseInt(event.value)) {
			case 6:
				document.getElementById('value').placeholder = "('My'|'Regex')";
				break;
			case 12:
				document.getElementById('value').placeholder = "/('My'|'Regex')\\w+/igm";
				break;
			default:
				document.getElementById('value').placeholder = "";
		};
	};

	glob.onChange1(document.getElementById('comparison'));
	glob.refreshVariableList(document.getElementById('storage'));
	glob.onChangeTrue(document.getElementById('iftrue'));
	glob.onChangeFalse(document.getElementById('iffalse'));
},

//---------------------------------------------------------------------
// Action Bot Function
//
// This is the function for the action within the Bot's Action class.
// Keep in mind event calls won't have access to the "msg" parameter,
// so be sure to provide checks for variable existance.
//---------------------------------------------------------------------

action: function(cache) {
	const data = cache.actions[cache.index];
	const type = parseInt(data.storage);
	const varName = this.evalMessage(data.varName, cache);
	const variable = this.getVariable(type, varName, cache);
	let result = false;
	if(variable) {
		const val1 = variable;
		const compare = parseInt(data.comparison);
		let val2 = this.evalMessage(data.value, cache);
		if(compare !== 6) val2 = this.eval(val2, cache);
		if(val2 === false) val2 = this.evalMessage(data.value, cache);
		switch(compare) {
			case 0:
				result = Boolean(val1 !== undefined);
				break;
			case 1:
				result = Boolean(val1 == val2);
				break;
			case 2:
				result = Boolean(val1 === val2);
				break;
			case 3:
				result = Boolean(val1 < val2);
				break;
			case 4:
				result = Boolean(val1 > val2);
				break;
			case 5:
				if(typeof(val1.includes) === 'function') {
					result = Boolean(val1.includes(val2));
				}
				break;
			case 6:
				result = Boolean(val1.match(new RegExp('^' + val2 + '$', 'i')));
				break;
			case 7:
				result = Boolean(val1.length > val2);
				break;
			case 8:
				result = Boolean(val1.length < val2);
				break;
			case 9: //Added by Lasse
			  	result = Boolean(val1.length == val2);
			  	break;
			case 10: //Added by MrGold
			  	result = Boolean(val1.startsWith(val2));
			  	break;
			case 11: //Added by MrGold
			  	result = Boolean(val1.endsWith(val2));
			  	break;
			case 12: //Added by ZockerNico
				result = Boolean(val1.match(new RegExp(val2)));
				break;
			case 13: //Added by TheMonDon
				result = Boolean(val1 <= val2);
				break;
			case 14: //Added by TheMonDon
				result = Boolean(val1 >= val2);
				break;
		}
	}
	this.executeResults(result, data, cache);
},

//---------------------------------------------------------------------
// Action Bot Mod
//
// Upon initialization of the bot, this code is run. Using the bot's
// DBM namespace, one can add/modify existing functions if necessary.
// In order to reduce conflictions between mods, be sure to alias
// functions you wish to overwrite.
//---------------------------------------------------------------------

mod: function(DBM) {
}

}; // End of module
<|endoftext|>


<commit_before>describe("inject-pattern", function() {
    var pattern;

    requireDependencies(["patterns/inject"], function(cls) {
        pattern = cls;
    });

    // Reset the lab before each test
    beforeEach(function() {
        $("#lab *").remove();
    });

    describe("Functional tests", function() {

        describe("extract/verifyConfig", function() {
            var $a, $target;

            beforeEach(function() {
                $a = $('link');
                $target = $('
');

                $("#lab").append($a).append($target);
            });

            it("fall back to href id", function() {
                var cfgs = pattern.extractConfig($a);
                expect(pattern.verifyConfig(cfgs)).toBeTruthy();

                expect(cfgs.length).toBe(1);
                expect(cfgs[0].source).toBe('#someid');
                expect(cfgs[0].target).toBe('#someid');
            });

            it("take first positional option as source and target", function() {
                var cfgs;

                $a.attr('data-pat-inject', '#otherid');
                $target.attr('id', 'otherid');
                cfgs = pattern.extractConfig($a);
                expect(pattern.verifyConfig(cfgs)).toBeTruthy();

                expect(cfgs[0].source).toBe('#otherid');
                expect(cfgs[0].target).toBe('#otherid');
            });

            it("take two positional options as source and target resp.", function() {
                var cfgs;

                $a.attr('data-pat-inject', '#otherid; #yetanotherid');
                $target.attr('id', 'yetanotherid');
                cfgs = pattern.extractConfig($a);
                expect(pattern.verifyConfig(cfgs)).toBeTruthy();

                expect(cfgs[0].source).toBe('#otherid');
                expect(cfgs[0].target).toBe('#yetanotherid');
            });

            it("create target if it doesn't exist", function() {
                var cfgs = pattern.extractConfig($a);

                $target.remove();
                expect(pattern.verifyConfig(cfgs)).toBeTruthy();

                $target = $('#someid');
                this.after(function() { $target.remove(); });
                expect($target.length).toBeGreaterThan(0);
                expect($target.parent().prop('tagName')).toBe('BODY');
            });
        });
    });

    describe("DOM tests", function() {

        describe("inject on anchor", function() {
            var $a, $div, answer;

            answer = function(html) {
                expect($.ajax).toHaveBeenCalled();
                $.ajax.mostRecentCall.args[0]['success'](html);
            };

            beforeEach(function() {
                $a = $('link'),
                $div = $('
');
                $("#lab").append($a).append($div);

                spyOn($, "ajax");
            });

            it("fetches url on click", function() {
                pattern.init($a);

                $a.trigger("click");

                expect($.ajax).toHaveBeenCalled();
                expect($.ajax.mostRecentCall.args[0]['url']).toBe("test.html");
            });

            it("injects into existing div", function() {
                pattern.init($a);
                $a.trigger("click");
                answer('
replacement
');

                expect($div.html()).toBe("replacement");
            });

            it("injects multiple times", function() {
                pattern.init($a);
                $a.trigger("click");
                answer('
replacement
');
                expect($div.html()).toBe("replacement");

                $a.trigger("click");
                answer('
new replacement
');
                expect($div.html()).toBe("new replacement");
            });

            it("takes multiple source-target pairs", function() {
                $a.attr("data-pat-inject", "#someid1; #otherid1 && #someid2; #otherid2");
                $target1 = $('
');
                $target2 = $('
');
                $div.append($target1).append($target2);

                pattern.init($a);
                $a.trigger("click");
                answer(''
                       + '
repl1
'
                       + '
repl2
'
                       + '');

                expect($target1.html()).toBe("repl1");
                expect($target2.html()).toBe("repl2");
            });

            it("acts on other selectors as well", function() {
                $a.attr("data-pat-inject", "target: #someid > .someclass");
                $target1 = $('
');
                $target2 = $('
');
                $div.append($target1).append($target2);

                pattern.init($a);
                $a.trigger("click");
                answer(''
                       + '
repl
'
                       + '');

                expect($target1.html()).toBe("repl");
                expect($target2.html()).toBe("repl");
            });

            it("sets/unsets load class 'injecting'", function() {
                var callback = jasmine.createSpy("patterns-injected");
                $(document).on("patterns-injected", callback);

                pattern.init($a);
                $a.trigger("click");

                expect($div.hasClass("injecting")).toBeTruthy();

                answer(''
                       + '
repl
'
                       + '');
                expect($div.hasClass("injecting")).toBeFalsy();

                expect(callback).toHaveBeenCalled();
            });

            // XXX: Tests for ::element ::after, ...
        });

        describe("inject on forms", function() {
            // XXX
        });
    });
});

// jshint indent: 4, browser: true, jquery: true, quotmark: double
// vim: sw=4 expandtab
<commit_msg>tests/inject: move answer helper to be useful for form tests as well<commit_after>describe("inject-pattern", function() {
    var pattern;

    requireDependencies(["patterns/inject"], function(cls) {
        pattern = cls;
    });

    // Reset the lab before each test
    beforeEach(function() {
        $("#lab *").remove();
    });

    describe("Functional tests", function() {

        describe("extract/verifyConfig", function() {
            var $a, $target;

            beforeEach(function() {
                $a = $('link');
                $target = $('
');

                $("#lab").append($a).append($target);
            });

            it("fall back to href id", function() {
                var cfgs = pattern.extractConfig($a);
                expect(pattern.verifyConfig(cfgs)).toBeTruthy();

                expect(cfgs.length).toBe(1);
                expect(cfgs[0].source).toBe('#someid');
                expect(cfgs[0].target).toBe('#someid');
            });

            it("take first positional option as source and target", function() {
                var cfgs;

                $a.attr('data-pat-inject', '#otherid');
                $target.attr('id', 'otherid');
                cfgs = pattern.extractConfig($a);
                expect(pattern.verifyConfig(cfgs)).toBeTruthy();

                expect(cfgs[0].source).toBe('#otherid');
                expect(cfgs[0].target).toBe('#otherid');
            });

            it("take two positional options as source and target resp.", function() {
                var cfgs;

                $a.attr('data-pat-inject', '#otherid; #yetanotherid');
                $target.attr('id', 'yetanotherid');
                cfgs = pattern.extractConfig($a);
                expect(pattern.verifyConfig(cfgs)).toBeTruthy();

                expect(cfgs[0].source).toBe('#otherid');
                expect(cfgs[0].target).toBe('#yetanotherid');
            });

            it("create target if it doesn't exist", function() {
                var cfgs = pattern.extractConfig($a);

                $target.remove();
                expect(pattern.verifyConfig(cfgs)).toBeTruthy();

                $target = $('#someid');
                this.after(function() { $target.remove(); });
                expect($target.length).toBeGreaterThan(0);
                expect($target.parent().prop('tagName')).toBe('BODY');
            });
        });
    });

    describe("DOM tests", function() {
        var answer = function(html) {
            expect($.ajax).toHaveBeenCalled();
            $.ajax.mostRecentCall.args[0]['success'](html);
        };

        beforeEach(function() {
            spyOn($, "ajax");
        });

        describe("inject on anchor", function() {

            var $a, $div;

            beforeEach(function() {
                $a = $('link'),
                $div = $('
');
                $("#lab").append($a).append($div);
            });

            it("fetches url on click", function() {
                pattern.init($a);

                $a.trigger("click");

                expect($.ajax).toHaveBeenCalled();
                expect($.ajax.mostRecentCall.args[0]['url']).toBe("test.html");
            });

            it("injects into existing div", function() {
                pattern.init($a);
                $a.trigger("click");
                answer('
replacement
');

                expect($div.html()).toBe("replacement");
            });

            it("injects multiple times", function() {
                pattern.init($a);
                $a.trigger("click");
                answer('
replacement
');
                expect($div.html()).toBe("replacement");

                $a.trigger("click");
                answer('
new replacement
');
                expect($div.html()).toBe("new replacement");
            });

            it("takes multiple source-target pairs", function() {
                $a.attr("data-pat-inject", "#someid1; #otherid1 && #someid2; #otherid2");
                $target1 = $('
');
                $target2 = $('
');
                $div.append($target1).append($target2);

                pattern.init($a);
                $a.trigger("click");
                answer(''
                       + '
repl1
'
                       + '
repl2
'
                       + '');

                expect($target1.html()).toBe("repl1");
                expect($target2.html()).toBe("repl2");
            });

            it("acts on other selectors as well", function() {
                $a.attr("data-pat-inject", "target: #someid > .someclass");
                $target1 = $('
');
                $target2 = $('
');
                $div.append($target1).append($target2);

                pattern.init($a);
                $a.trigger("click");
                answer(''
                       + '
repl
'
                       + '');

                expect($target1.html()).toBe("repl");
                expect($target2.html()).toBe("repl");
            });

            it("sets/unsets load class 'injecting'", function() {
                var callback = jasmine.createSpy("patterns-injected");
                $(document).on("patterns-injected", callback);

                pattern.init($a);
                $a.trigger("click");

                expect($div.hasClass("injecting")).toBeTruthy();

                answer(''
                       + '
repl
'
                       + '');
                expect($div.hasClass("injecting")).toBeFalsy();

                expect(callback).toHaveBeenCalled();
            });

            // XXX: Tests for ::element ::after, ...
        });

        describe("inject on forms", function() {
            // XXX
        });
    });
});

// jshint indent: 4, browser: true, jquery: true, quotmark: double
// vim: sw=4 expandtab
<|endoftext|>


<commit_before>describe('Scope', function() {
	var scope;

	beforeEach(function() {
		scope = new Scope();
	});

  describe('watch()', function() {
    var listenerSpy;

    beforeEach(function() {
      listenerSpy = jasmine.createSpy();
    });

    it('should watch a property', function() {
      scope.watch('someProperty', listenerSpy);

      scope.someProperty = 'someValueHere';
      scope.deliverChangeRecords();

      expect(listenerSpy).toHaveBeenCalledWith('someValueHere', undefined);
    });

    it('should deep properties', function() {
      scope.watch('another.deep.property.here', listenerSpy);
      scope.deliverChangeRecords();

      scope.another.deep.property.here = 0;
      scope.deliverChangeRecords();

      expect(listenerSpy).toHaveBeenCalledWith(0, undefined);

      scope.another.deep.property.here += 1;
      scope.deliverChangeRecords();

      expect(listenerSpy).toHaveBeenCalledWith(1, 0);
    });
  });

  describe('clone()', function() {
    it('should create a child scope', function() {
      var child = scope.clone();

      expect(child.parentScope).toBe(scope);
    });
  });

  describe('broadcast()', function() {
    var scope,
        listenerSpy,
        deepChildScope;

    beforeEach(function() {
      scope = new Scope(),
      listenerSpy = jasmine.createSpy(),
      deepChildScope = scope;

      for(var i = 0; i < 100; i++) {
        deepChildScope = deepChildScope.clone();
      }
    });

    it('should propagate events through the parents of a scope', function() {
      scope.on('update', listenerSpy);

      for(var i = 0; i < 10; i++) {
        deepChildScope.broadcast('update', i);
        expect(listenerSpy).toHaveBeenCalledWith(i);
      }
    });
  });
});
<commit_msg>chore(test/Scope.js): `destroy()` method test case<commit_after>describe('Scope', function() {
	var scope;

	beforeEach(function() {
		scope = new Scope();
	});

  describe('watch()', function() {
    var listenerSpy;

    beforeEach(function() {
      listenerSpy = jasmine.createSpy();
    });

    it('should watch a property', function() {
      scope.watch('someProperty', listenerSpy);

      scope.someProperty = 'someValueHere';
      scope.deliverChangeRecords();

      expect(listenerSpy).toHaveBeenCalledWith('someValueHere', undefined);
    });

    it('should deep properties', function() {
      scope.watch('another.deep.property.here', listenerSpy);
      scope.deliverChangeRecords();

      scope.another.deep.property.here = 0;
      scope.deliverChangeRecords();

      expect(listenerSpy).toHaveBeenCalledWith(0, undefined);

      scope.another.deep.property.here += 1;
      scope.deliverChangeRecords();

      expect(listenerSpy).toHaveBeenCalledWith(1, 0);
    });

  describe('destroy()', function() {
    var scope,
        childScope;

    beforeEach(function() {
      scope = new Scope();
      childScope = scope.clone();
    });

    it('should destroy a scope and eliminate all the records of the scope on its parents', function() {
      for(var i = 0; i < 10; i++) {
        scope.clone();
      }

      expect(scope.childScopes.length).toBe(11);
      expect(scope.childScopes[0]).toBe(childScope);

      childScope.destroy();

      expect(scope.childScopes.length).toBe(10);

      for(var i = 0; i < scope.childScopes.length; i++) {
        expect(scope.childScopes[i]).not.toBe(childScope);
      }
    });
  });

  describe('clone()', function() {
    it('should create a child scope', function() {
      var child = scope.clone();

      expect(child.parentScope).toBe(scope);
    });
  });

  describe('broadcast()', function() {
    var scope,
        listenerSpy,
        deepChildScope;

    beforeEach(function() {
      scope = new Scope(),
      listenerSpy = jasmine.createSpy(),
      deepChildScope = scope;

      for(var i = 0; i < 100; i++) {
        deepChildScope = deepChildScope.clone();
      }
    });

    it('should propagate events through the parents of a scope', function() {
      scope.on('update', listenerSpy);

      for(var i = 0; i < 10; i++) {
        deepChildScope.broadcast('update', i);
        expect(listenerSpy).toHaveBeenCalledWith(i);
      }
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

WaveSurfer.Drawer.MultiCanvas = Object.create(WaveSurfer.Drawer);

WaveSurfer.util.extend(WaveSurfer.Drawer.MultiCanvas, {

    initDrawer: function (params) {
        this.maxCanvasWidth = params.maxCanvasWidth != null ? params.maxCanvasWidth : 4000;
        this.maxCanvasElementWidth = Math.round(this.maxCanvasWidth / this.params.pixelRatio);

        if (this.maxCanvasWidth <= 1) {
            throw 'maxCanvasWidth must be greater than 1.';
        } else if (this.maxCanvasWidth % 2 == 1) {
            throw 'maxCanvasWidth must be an even number.';
        }

        this.hasProgressCanvas = this.params.waveColor != this.params.progressColor;
        this.halfPixel = 0.5 / this.params.pixelRatio;
        this.canvases = [];
    },

    createElements: function () {
        this.progressWave = this.wrapper.appendChild(
            this.style(document.createElement('wave'), {
                position: 'absolute',
                zIndex: 2,
                left: 0,
                top: 0,
                bottom: 0,
                overflow: 'hidden',
                width: '0',
                display: 'none',
                boxSizing: 'border-box',
                borderRightStyle: 'solid',
                borderRightWidth: this.params.cursorWidth + 'px',
                borderRightColor: this.params.cursorColor,
                pointerEvents: 'none'
            })
        );

        this.addCanvas();
    },

    updateSize: function () {
        var totalWidth = Math.round(this.width / this.params.pixelRatio),
            requiredCanvases = Math.ceil(totalWidth / this.maxCanvasElementWidth);

        while (this.canvases.length < requiredCanvases) {
            this.addCanvas();
        }

        while (this.canvases.length > requiredCanvases) {
            this.removeCanvas();
        }

        this.canvases.forEach(function (entry, i) {
            // Add some overlap to prevent vertical white stripes, keep the width even for simplicity.
            var canvasWidth = this.maxCanvasWidth + 2 * Math.ceil(this.params.pixelRatio / 2);

            if (i == this.canvases.length - 1) {
                canvasWidth = this.width - (this.maxCanvasWidth * (this.canvases.length - 1));
            }

            this.updateDimensions(entry, canvasWidth, this.height);
            this.clearWaveForEntry(entry);
        }, this);
    },

    addCanvas: function () {
        var entry = {},
            leftOffset = this.maxCanvasElementWidth * this.canvases.length;

        entry.wave = this.wrapper.appendChild(
            this.style(document.createElement('canvas'), {
                position: 'absolute',
                zIndex: 2,
                left: leftOffset + 'px',
                top: 0,
                bottom: 0,
                height: '100%',
                pointerEvents: 'none'
            })
        );
        entry.waveCtx = entry.wave.getContext('2d');

        if (this.hasProgressCanvas) {
            entry.progress = this.progressWave.appendChild(
                this.style(document.createElement('canvas'), {
                    position: 'absolute',
                    left: leftOffset + 'px',
                    top: 0,
                    bottom: 0,
                    height: '100%'
                })
            );
            entry.progressCtx = entry.progress.getContext('2d');
        }

        this.canvases.push(entry);
    },

    removeCanvas: function () {
        var lastEntry = this.canvases.pop();
        lastEntry.wave.parentElement.removeChild(lastEntry.wave);
        if (this.hasProgressCanvas) {
            lastEntry.progress.parentElement.removeChild(lastEntry.progress);
        }
    },

    updateDimensions: function (entry, width, height) {
        var elementWidth = Math.round(width / this.params.pixelRatio),
            totalWidth = Math.round(this.width / this.params.pixelRatio);

        // Where the canvas starts and ends in the waveform, represented as a decimal between 0 and 1.
        entry.start = (entry.waveCtx.canvas.offsetLeft / totalWidth) || 0;
        entry.end = entry.start + elementWidth / totalWidth;

        entry.waveCtx.canvas.width = width;
        entry.waveCtx.canvas.height = height;
        this.style(entry.waveCtx.canvas, { width: elementWidth + 'px' });

        this.style(this.progressWave, { display: 'block' });

        if (this.hasProgressCanvas) {
            entry.progressCtx.canvas.width = width;
            entry.progressCtx.canvas.height = height;
            this.style(entry.progressCtx.canvas, { width: elementWidth + 'px' });
        }
    },

    clearWave: function () {
        this.canvases.forEach(function (entry) {
            this.clearWaveForEntry(entry);
        }, this);
    },

    clearWaveForEntry: function (entry) {
        entry.waveCtx.clearRect(0, 0, entry.waveCtx.canvas.width, entry.waveCtx.canvas.height);
        if (this.hasProgressCanvas) {
            entry.progressCtx.clearRect(0, 0, entry.progressCtx.canvas.width, entry.progressCtx.canvas.height);
        }
    },

    drawBars: WaveSurfer.util.frame(function (peaks, channelIndex, start, end) {
        // Split channels
        if (peaks[0] instanceof Array) {
            var channels = peaks;
            if (this.params.splitChannels) {
                this.setHeight(channels.length * this.params.height * this.params.pixelRatio);
                channels.forEach(function(channelPeaks, i) {
                    this.drawBars(channelPeaks, i, start, end);
                }, this);
                return;
            } else {
                peaks = channels[0];
            }
        }

        // Bar wave draws the bottom only as a reflection of the top,
        // so we don't need negative values
        var hasMinVals = [].some.call(peaks, function (val) {return val < 0;});
        // Skip every other value if there are negatives.
        var peakIndexScale = 1;
        if (hasMinVals) {
            peakIndexScale = 2;
        }

        // A half-pixel offset makes lines crisp
        var width = this.width;
        var height = this.params.height * this.params.pixelRatio;
        var offsetY = height * channelIndex || 0;
        var halfH = height / 2;
        var length = peaks.length / peakIndexScale;
        var bar = this.params.barWidth * this.params.pixelRatio;
        var gap = Math.max(this.params.pixelRatio, ~~(bar / 2));
        var step = bar + gap;

        var absmax = 1 / this.params.barHeight;
        if (this.params.normalize) {
            var max = WaveSurfer.util.max(peaks);
            var min = WaveSurfer.util.min(peaks);
            absmax = -min > max ? -min : max;
        }

        var scale = length / width;

        for (var i = (start / scale); i < (end / scale); i += step) {
            var peak = peaks[Math.floor(i * scale * peakIndexScale)] || 0;
            var h = Math.round(peak / absmax * halfH);
            this.fillRect(i + this.halfPixel, halfH - h + offsetY, bar + this.halfPixel, h * 2);
        }
    }),

    drawWave: WaveSurfer.util.frame(function (peaks, channelIndex, start, end) {
        // Split channels
        if (peaks[0] instanceof Array) {
            var channels = peaks;
            if (this.params.splitChannels) {
                this.setHeight(channels.length * this.params.height * this.params.pixelRatio);
                channels.forEach(function(channelPeaks, i) {
                    this.drawWave(channelPeaks, i, start, end);
                }, this);
                return;
            } else {
                peaks = channels[0];
            }
        }

        // Support arrays without negative peaks
        var hasMinValues = [].some.call(peaks, function (val) { return val < 0; });
        if (!hasMinValues) {
            var reflectedPeaks = [];
            for (var i = 0, len = peaks.length; i < len; i++) {
                reflectedPeaks[2 * i] = peaks[i];
                reflectedPeaks[2 * i + 1] = -peaks[i];
            }
            peaks = reflectedPeaks;
        }

        // A half-pixel offset makes lines crisp
        var height = this.params.height * this.params.pixelRatio;
        var offsetY = height * channelIndex || 0;
        var halfH = height / 2;

        var absmax = 1 / this.params.barHeight;
        if (this.params.normalize) {
            var max = WaveSurfer.util.max(peaks);
            var min = WaveSurfer.util.min(peaks);
            absmax = -min > max ? -min : max;
        }

        this.drawLine(peaks, absmax, halfH, offsetY, start, end);

        // Always draw a median line
        this.fillRect(0, halfH + offsetY - this.halfPixel, this.width, this.halfPixel);
    }),

    drawLine: function (peaks, absmax, halfH, offsetY, start, end) {
        this.canvases.forEach (function (entry) {
            this.setFillStyles(entry);
            this.drawLineToContext(entry, entry.waveCtx, peaks, absmax, halfH, offsetY, start, end);
            this.drawLineToContext(entry, entry.progressCtx, peaks, absmax, halfH, offsetY, start, end);
        }, this);
    },

    drawLineToContext: function (entry, ctx, peaks, absmax, halfH, offsetY, start, end) {
        if (!ctx) { return; }

        var length = peaks.length / 2;

        var scale = 1;
        if (this.params.fillParent && this.width != length) {
            scale = this.width / length;
        }

        var first = Math.round(length * entry.start),
            last = Math.round(length * entry.end);
        if (first > end || last < start) { return; }
        var canvasStart = Math.max(first, start);
        var canvasEnd = Math.min(last, end);

        ctx.beginPath();
        ctx.moveTo((canvasStart - first) * scale + this.halfPixel, halfH + offsetY);

        for (var i = canvasStart; i < canvasEnd; i++) {
            var peak = peaks[2 * i] || 0;
            var h = Math.round(peak / absmax * halfH);
            ctx.lineTo((i - first) * scale + this.halfPixel, halfH - h + offsetY);
        }

        // Draw the bottom edge going backwards, to make a single
        // closed hull to fill.
        for (var i = canvasEnd - 1; i >= canvasStart; i--) {
            var peak = peaks[2 * i + 1] || 0;
            var h = Math.round(peak / absmax * halfH);
            ctx.lineTo((i - first) * scale + this.halfPixel, halfH - h + offsetY);
        }

        ctx.closePath();
        ctx.fill();
    },

    fillRect: function (x, y, width, height) {
        var startCanvas = Math.floor(x / this.maxCanvasWidth);
        var endCanvas = Math.min(Math.ceil((x + width) / this.maxCanvasWidth) + 1,
                                this.canvases.length);
        for (var i = startCanvas; i < endCanvas; i++) {
            var entry = this.canvases[i],
                leftOffset = i * this.maxCanvasWidth;
            var intersection = {
                x1: Math.max(x, i * this.maxCanvasWidth),
                y1: y,
                x2: Math.min(x + width, i * this.maxCanvasWidth + entry.waveCtx.canvas.width),
                y2: y + height
            };

            if (intersection.x1 < intersection.x2) {
                this.setFillStyles(entry);

                this.fillRectToContext(entry.waveCtx,
                        intersection.x1 - leftOffset,
                        intersection.y1,
                        intersection.x2 - intersection.x1,
                        intersection.y2 - intersection.y1);

                this.fillRectToContext(entry.progressCtx,
                        intersection.x1 - leftOffset,
                        intersection.y1,
                        intersection.x2 - intersection.x1,
                        intersection.y2 - intersection.y1);
            }
        }
    },

    fillRectToContext: function (ctx, x, y, width, height) {
        if (!ctx) { return; }
        ctx.fillRect(x, y, width, height);
    },

    setFillStyles: function (entry) {
        entry.waveCtx.fillStyle = this.params.waveColor;
        if (this.hasProgressCanvas) {
            entry.progressCtx.fillStyle = this.params.progressColor;
        }
    },

    updateProgress: function (pos) {
        this.style(this.progressWave, { width: pos + 'px' });
    },

    /**
     * Combine all available canvases together.
     *
     * @param {String} type - an optional value of a format type. Default is image/png.
     * @param {Number} quality - an optional value between 0 and 1. Default is 0.92.
     *
     */
    getImage: function (type, quality) {
        var availableCanvas = [];
        this.canvases.forEach(function (entry) {
            availableCanvas.push(entry.wave.toDataURL(type, quality));
        });
        return availableCanvas.length > 1 ? availableCanvas : availableCanvas[0];
    }
});
<commit_msg>Add pointerEvents: none to left/right sides: undo zIndex change. (#1137)<commit_after>'use strict';

WaveSurfer.Drawer.MultiCanvas = Object.create(WaveSurfer.Drawer);

WaveSurfer.util.extend(WaveSurfer.Drawer.MultiCanvas, {

    initDrawer: function (params) {
        this.maxCanvasWidth = params.maxCanvasWidth != null ? params.maxCanvasWidth : 4000;
        this.maxCanvasElementWidth = Math.round(this.maxCanvasWidth / this.params.pixelRatio);

        if (this.maxCanvasWidth <= 1) {
            throw 'maxCanvasWidth must be greater than 1.';
        } else if (this.maxCanvasWidth % 2 == 1) {
            throw 'maxCanvasWidth must be an even number.';
        }

        this.hasProgressCanvas = this.params.waveColor != this.params.progressColor;
        this.halfPixel = 0.5 / this.params.pixelRatio;
        this.canvases = [];
    },

    createElements: function () {
        this.progressWave = this.wrapper.appendChild(
            this.style(document.createElement('wave'), {
                position: 'absolute',
                zIndex: 2,
                left: 0,
                top: 0,
                bottom: 0,
                overflow: 'hidden',
                width: '0',
                display: 'none',
                boxSizing: 'border-box',
                borderRightStyle: 'solid',
                borderRightWidth: this.params.cursorWidth + 'px',
                borderRightColor: this.params.cursorColor,
                pointerEvents: 'none'
            })
        );

        this.addCanvas();
    },

    updateSize: function () {
        var totalWidth = Math.round(this.width / this.params.pixelRatio),
            requiredCanvases = Math.ceil(totalWidth / this.maxCanvasElementWidth);

        while (this.canvases.length < requiredCanvases) {
            this.addCanvas();
        }

        while (this.canvases.length > requiredCanvases) {
            this.removeCanvas();
        }

        this.canvases.forEach(function (entry, i) {
            // Add some overlap to prevent vertical white stripes, keep the width even for simplicity.
            var canvasWidth = this.maxCanvasWidth + 2 * Math.ceil(this.params.pixelRatio / 2);

            if (i == this.canvases.length - 1) {
                canvasWidth = this.width - (this.maxCanvasWidth * (this.canvases.length - 1));
            }

            this.updateDimensions(entry, canvasWidth, this.height);
            this.clearWaveForEntry(entry);
        }, this);
    },

    addCanvas: function () {
        var entry = {},
            leftOffset = this.maxCanvasElementWidth * this.canvases.length;

        entry.wave = this.wrapper.appendChild(
            this.style(document.createElement('canvas'), {
                position: 'absolute',
                zIndex: 1,
                left: leftOffset + 'px',
                top: 0,
                bottom: 0,
                height: '100%',
                pointerEvents: 'none'
            })
        );
        entry.waveCtx = entry.wave.getContext('2d');

        if (this.hasProgressCanvas) {
            entry.progress = this.progressWave.appendChild(
                this.style(document.createElement('canvas'), {
                    position: 'absolute',
                    left: leftOffset + 'px',
                    top: 0,
                    bottom: 0,
                    height: '100%'
                })
            );
            entry.progressCtx = entry.progress.getContext('2d');
        }

        this.canvases.push(entry);
    },

    removeCanvas: function () {
        var lastEntry = this.canvases.pop();
        lastEntry.wave.parentElement.removeChild(lastEntry.wave);
        if (this.hasProgressCanvas) {
            lastEntry.progress.parentElement.removeChild(lastEntry.progress);
        }
    },

    updateDimensions: function (entry, width, height) {
        var elementWidth = Math.round(width / this.params.pixelRatio),
            totalWidth = Math.round(this.width / this.params.pixelRatio);

        // Where the canvas starts and ends in the waveform, represented as a decimal between 0 and 1.
        entry.start = (entry.waveCtx.canvas.offsetLeft / totalWidth) || 0;
        entry.end = entry.start + elementWidth / totalWidth;

        entry.waveCtx.canvas.width = width;
        entry.waveCtx.canvas.height = height;
        this.style(entry.waveCtx.canvas, { width: elementWidth + 'px' });

        this.style(this.progressWave, { display: 'block' });

        if (this.hasProgressCanvas) {
            entry.progressCtx.canvas.width = width;
            entry.progressCtx.canvas.height = height;
            this.style(entry.progressCtx.canvas, { width: elementWidth + 'px' });
        }
    },

    clearWave: function () {
        this.canvases.forEach(function (entry) {
            this.clearWaveForEntry(entry);
        }, this);
    },

    clearWaveForEntry: function (entry) {
        entry.waveCtx.clearRect(0, 0, entry.waveCtx.canvas.width, entry.waveCtx.canvas.height);
        if (this.hasProgressCanvas) {
            entry.progressCtx.clearRect(0, 0, entry.progressCtx.canvas.width, entry.progressCtx.canvas.height);
        }
    },

    drawBars: WaveSurfer.util.frame(function (peaks, channelIndex, start, end) {
        // Split channels
        if (peaks[0] instanceof Array) {
            var channels = peaks;
            if (this.params.splitChannels) {
                this.setHeight(channels.length * this.params.height * this.params.pixelRatio);
                channels.forEach(function(channelPeaks, i) {
                    this.drawBars(channelPeaks, i, start, end);
                }, this);
                return;
            } else {
                peaks = channels[0];
            }
        }

        // Bar wave draws the bottom only as a reflection of the top,
        // so we don't need negative values
        var hasMinVals = [].some.call(peaks, function (val) {return val < 0;});
        // Skip every other value if there are negatives.
        var peakIndexScale = 1;
        if (hasMinVals) {
            peakIndexScale = 2;
        }

        // A half-pixel offset makes lines crisp
        var width = this.width;
        var height = this.params.height * this.params.pixelRatio;
        var offsetY = height * channelIndex || 0;
        var halfH = height / 2;
        var length = peaks.length / peakIndexScale;
        var bar = this.params.barWidth * this.params.pixelRatio;
        var gap = Math.max(this.params.pixelRatio, ~~(bar / 2));
        var step = bar + gap;

        var absmax = 1 / this.params.barHeight;
        if (this.params.normalize) {
            var max = WaveSurfer.util.max(peaks);
            var min = WaveSurfer.util.min(peaks);
            absmax = -min > max ? -min : max;
        }

        var scale = length / width;

        for (var i = (start / scale); i < (end / scale); i += step) {
            var peak = peaks[Math.floor(i * scale * peakIndexScale)] || 0;
            var h = Math.round(peak / absmax * halfH);
            this.fillRect(i + this.halfPixel, halfH - h + offsetY, bar + this.halfPixel, h * 2);
        }
    }),

    drawWave: WaveSurfer.util.frame(function (peaks, channelIndex, start, end) {
        // Split channels
        if (peaks[0] instanceof Array) {
            var channels = peaks;
            if (this.params.splitChannels) {
                this.setHeight(channels.length * this.params.height * this.params.pixelRatio);
                channels.forEach(function(channelPeaks, i) {
                    this.drawWave(channelPeaks, i, start, end);
                }, this);
                return;
            } else {
                peaks = channels[0];
            }
        }

        // Support arrays without negative peaks
        var hasMinValues = [].some.call(peaks, function (val) { return val < 0; });
        if (!hasMinValues) {
            var reflectedPeaks = [];
            for (var i = 0, len = peaks.length; i < len; i++) {
                reflectedPeaks[2 * i] = peaks[i];
                reflectedPeaks[2 * i + 1] = -peaks[i];
            }
            peaks = reflectedPeaks;
        }

        // A half-pixel offset makes lines crisp
        var height = this.params.height * this.params.pixelRatio;
        var offsetY = height * channelIndex || 0;
        var halfH = height / 2;

        var absmax = 1 / this.params.barHeight;
        if (this.params.normalize) {
            var max = WaveSurfer.util.max(peaks);
            var min = WaveSurfer.util.min(peaks);
            absmax = -min > max ? -min : max;
        }

        this.drawLine(peaks, absmax, halfH, offsetY, start, end);

        // Always draw a median line
        this.fillRect(0, halfH + offsetY - this.halfPixel, this.width, this.halfPixel);
    }),

    drawLine: function (peaks, absmax, halfH, offsetY, start, end) {
        this.canvases.forEach (function (entry) {
            this.setFillStyles(entry);
            this.drawLineToContext(entry, entry.waveCtx, peaks, absmax, halfH, offsetY, start, end);
            this.drawLineToContext(entry, entry.progressCtx, peaks, absmax, halfH, offsetY, start, end);
        }, this);
    },

    drawLineToContext: function (entry, ctx, peaks, absmax, halfH, offsetY, start, end) {
        if (!ctx) { return; }

        var length = peaks.length / 2;

        var scale = 1;
        if (this.params.fillParent && this.width != length) {
            scale = this.width / length;
        }

        var first = Math.round(length * entry.start),
            last = Math.round(length * entry.end);
        if (first > end || last < start) { return; }
        var canvasStart = Math.max(first, start);
        var canvasEnd = Math.min(last, end);

        ctx.beginPath();
        ctx.moveTo((canvasStart - first) * scale + this.halfPixel, halfH + offsetY);

        for (var i = canvasStart; i < canvasEnd; i++) {
            var peak = peaks[2 * i] || 0;
            var h = Math.round(peak / absmax * halfH);
            ctx.lineTo((i - first) * scale + this.halfPixel, halfH - h + offsetY);
        }

        // Draw the bottom edge going backwards, to make a single
        // closed hull to fill.
        for (var i = canvasEnd - 1; i >= canvasStart; i--) {
            var peak = peaks[2 * i + 1] || 0;
            var h = Math.round(peak / absmax * halfH);
            ctx.lineTo((i - first) * scale + this.halfPixel, halfH - h + offsetY);
        }

        ctx.closePath();
        ctx.fill();
    },

    fillRect: function (x, y, width, height) {
        var startCanvas = Math.floor(x / this.maxCanvasWidth);
        var endCanvas = Math.min(Math.ceil((x + width) / this.maxCanvasWidth) + 1,
                                this.canvases.length);
        for (var i = startCanvas; i < endCanvas; i++) {
            var entry = this.canvases[i],
                leftOffset = i * this.maxCanvasWidth;
            var intersection = {
                x1: Math.max(x, i * this.maxCanvasWidth),
                y1: y,
                x2: Math.min(x + width, i * this.maxCanvasWidth + entry.waveCtx.canvas.width),
                y2: y + height
            };

            if (intersection.x1 < intersection.x2) {
                this.setFillStyles(entry);

                this.fillRectToContext(entry.waveCtx,
                        intersection.x1 - leftOffset,
                        intersection.y1,
                        intersection.x2 - intersection.x1,
                        intersection.y2 - intersection.y1);

                this.fillRectToContext(entry.progressCtx,
                        intersection.x1 - leftOffset,
                        intersection.y1,
                        intersection.x2 - intersection.x1,
                        intersection.y2 - intersection.y1);
            }
        }
    },

    fillRectToContext: function (ctx, x, y, width, height) {
        if (!ctx) { return; }
        ctx.fillRect(x, y, width, height);
    },

    setFillStyles: function (entry) {
        entry.waveCtx.fillStyle = this.params.waveColor;
        if (this.hasProgressCanvas) {
            entry.progressCtx.fillStyle = this.params.progressColor;
        }
    },

    updateProgress: function (pos) {
        this.style(this.progressWave, { width: pos + 'px' });
    },

    /**
     * Combine all available canvases together.
     *
     * @param {String} type - an optional value of a format type. Default is image/png.
     * @param {Number} quality - an optional value between 0 and 1. Default is 0.92.
     *
     */
    getImage: function (type, quality) {
        var availableCanvas = [];
        this.canvases.forEach(function (entry) {
            availableCanvas.push(entry.wave.toDataURL(type, quality));
        });
        return availableCanvas.length > 1 ? availableCanvas : availableCanvas[0];
    }
});
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * 'License'); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var App = require('app');

App.repoVersionMapper = App.QuickDataMapper.create({
  modelRepoVersions: App.RepositoryVersion,
  modelOperatingSystems: App.OS,
  modelRepositories: App.Repository,
  modelServices: App.ServiceSimple,

  modelRepoVersion: function (isCurrentStackOnly) {
    var repoVersionsKey = 'RepositoryVersions';
    return {
      id: repoVersionsKey + '.id',
      stack_version_id: repoVersionsKey + '.stackVersionId',
      display_name: repoVersionsKey + '.display_name',
      type: repoVersionsKey + '.type',
      hidden: repoVersionsKey + '.hidden',
      repository_version: repoVersionsKey + '.repository_version',
      upgrade_pack: repoVersionsKey + '.upgrade_pack',
      stack_version_type: repoVersionsKey + '.stack_name',
      stack_version_number: repoVersionsKey + '.stack_version',
      use_redhat_satellite: 'use_redhat_satellite',
      stack_services_key: 'stack_services',
      stack_services_type: 'array',
      stack_services: {
        item: 'id'
      },
      operating_systems_key: 'operating_systems',
      operating_systems_type: 'array',
      operating_systems: {
        item: 'id'
      }
    };
  },

  modelOS: {
    id: 'id',
    repository_version_id: 'repository_version_id',
    os_type: 'OperatingSystems.os_type',
    stack_name: 'OperatingSystems.stack_name',
    stack_version: 'OperatingSystems.stack_version',
    repositories_key: 'repositories',
    repositories_type: 'array',
    repositories: {
      item: 'id'
    }
  },

  modelRepository: {
    id: 'id',
    operating_system_id: 'Repositories.operating_system_id',
    base_url : 'Repositories.base_url',
    default_base_url : 'Repositories.default_base_url',
    latest_base_url : 'Repositories.latest_base_url',
    mirrors_list : 'Repositories.mirrors_list',
    os_type : 'Repositories.os_type',
    repo_id : 'Repositories.repo_id',
    repo_name : 'Repositories.repo_name',
    stack_name : 'Repositories.stack_name',
    stack_version : 'Repositories.stack_version'
  },

  map: function (json, loadAll, isCurrentStackOnly) {
    var modelRepoVersions = this.get('modelRepoVersions');
    var modelOperatingSystems = this.get('modelOperatingSystems');
    var modelRepositories = this.get('modelRepositories');
    var modelServices = this.get('modelServices');

    var resultRepoVersion = [];
    var resultOS = [];
    var resultRepo = [];
    var resultService = [];
    var repoVersionsKey = 'RepositoryVersions';

    if (!isCurrentStackOnly) {
      json = this.convertToRepoScheme(json);
    }

    if (json && json.items) {
      json.items.forEach(function (item) {
        if (loadAll || (item[repoVersionsKey] && !App.StackVersion.find().someProperty('repositoryVersion.id', item[repoVersionsKey].id))) {
          var repo = item;
          var osArray = [];
          if (item.operating_systems) {
            item.operating_systems.forEach(function (os) {
              os.id = item[repoVersionsKey].repository_version + os.OperatingSystems.os_type;
              os.repository_version_id = repo.id;
              var repoArray = [];
              if (Em.get(os, 'repositories')) {
                os.repositories.forEach(function (repo) {
                  repo.id = repo.Repositories.repo_id + os.id;
                  repo.operating_system_id = os.id;
                  repoArray.pushObject(repo);
                  resultRepo.push(this.parseIt(repo, this.get('modelRepository')));
                }, this);
              }
              os.repositories = repoArray;
              osArray.pushObject(os);
              resultOS.push(this.parseIt(os, this.get('modelOS')));
            }, this);
          }
          if (item[repoVersionsKey].stack_services) {
            item[repoVersionsKey].stack_services.forEach(function (service) {
              var serviceObj = {
                id: item[repoVersionsKey].repository_version + service.name,
                name: service.name,
                display_name: service.display_name,
                latest_version: service.versions[0] ? service.versions[0] : '',
                is_available: item[repoVersionsKey].services.someProperty('name', service.name),
                is_upgradable: json.stackServices ? json.stackServices[service.name] && json.stackServices[service.name].upgrade : true
              };
              resultService.push(serviceObj);
            }, this);
          } else if (item[repoVersionsKey].services) {
            item[repoVersionsKey].services.forEach(function (service) {
              var serviceObj = {
                id: item[repoVersionsKey].repository_version + service.name,
                name: service.name,
                display_name: service.display_name,
                latest_version: service.versions[0] ? service.versions[0].version: ''
              };
              resultService.push(serviceObj);
            }, this);
          }
          repo.use_redhat_satellite = Em.get(item, 'operating_systems.0.OperatingSystems.ambari_managed_repositories') === false;
          repo.operating_systems = osArray;
          repo.stack_services = resultService;
          resultRepoVersion.push(this.parseIt(repo, this.modelRepoVersion(isCurrentStackOnly)));
        }
      }, this);
    }
    App.store.safeLoadMany(modelRepositories, resultRepo);
    App.store.safeLoadMany(modelOperatingSystems, resultOS);
    App.store.safeLoadMany(modelServices, resultService);
    App.store.safeLoadMany(modelRepoVersions, resultRepoVersion);
  },

  /**
   *
   * @param {?object} json
   * @returns {{items: Array}}
   */
  convertToRepoScheme: function(json) {
    var extractedJson = {items: []};

    if (json && json.items) {
      json.items.forEach(function(stack) {
        stack.versions.forEach(function(version) {
          version.repository_versions.forEach(function(repoVersion) {
            extractedJson.items.push(repoVersion);
          }, this);
        }, this);
      }, this);
    }
    return extractedJson;
  }
});
<commit_msg>AMBARI-22081. UI is crashing while upgrading when services array is not present (alexantonenko)<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * 'License'); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var App = require('app');

App.repoVersionMapper = App.QuickDataMapper.create({
  modelRepoVersions: App.RepositoryVersion,
  modelOperatingSystems: App.OS,
  modelRepositories: App.Repository,
  modelServices: App.ServiceSimple,

  modelRepoVersion: function (isCurrentStackOnly) {
    var repoVersionsKey = 'RepositoryVersions';
    return {
      id: repoVersionsKey + '.id',
      stack_version_id: repoVersionsKey + '.stackVersionId',
      display_name: repoVersionsKey + '.display_name',
      type: repoVersionsKey + '.type',
      hidden: repoVersionsKey + '.hidden',
      repository_version: repoVersionsKey + '.repository_version',
      upgrade_pack: repoVersionsKey + '.upgrade_pack',
      stack_version_type: repoVersionsKey + '.stack_name',
      stack_version_number: repoVersionsKey + '.stack_version',
      use_redhat_satellite: 'use_redhat_satellite',
      stack_services_key: 'stack_services',
      stack_services_type: 'array',
      stack_services: {
        item: 'id'
      },
      operating_systems_key: 'operating_systems',
      operating_systems_type: 'array',
      operating_systems: {
        item: 'id'
      }
    };
  },

  modelOS: {
    id: 'id',
    repository_version_id: 'repository_version_id',
    os_type: 'OperatingSystems.os_type',
    stack_name: 'OperatingSystems.stack_name',
    stack_version: 'OperatingSystems.stack_version',
    repositories_key: 'repositories',
    repositories_type: 'array',
    repositories: {
      item: 'id'
    }
  },

  modelRepository: {
    id: 'id',
    operating_system_id: 'Repositories.operating_system_id',
    base_url : 'Repositories.base_url',
    default_base_url : 'Repositories.default_base_url',
    latest_base_url : 'Repositories.latest_base_url',
    mirrors_list : 'Repositories.mirrors_list',
    os_type : 'Repositories.os_type',
    repo_id : 'Repositories.repo_id',
    repo_name : 'Repositories.repo_name',
    stack_name : 'Repositories.stack_name',
    stack_version : 'Repositories.stack_version'
  },

  map: function (json, loadAll, isCurrentStackOnly) {
    var modelRepoVersions = this.get('modelRepoVersions');
    var modelOperatingSystems = this.get('modelOperatingSystems');
    var modelRepositories = this.get('modelRepositories');
    var modelServices = this.get('modelServices');

    var resultRepoVersion = [];
    var resultOS = [];
    var resultRepo = [];
    var resultService = [];
    var repoVersionsKey = 'RepositoryVersions';

    if (!isCurrentStackOnly) {
      json = this.convertToRepoScheme(json);
    }

    if (json && json.items) {
      json.items.forEach(function (item) {
        if (loadAll || (item[repoVersionsKey] && !App.StackVersion.find().someProperty('repositoryVersion.id', item[repoVersionsKey].id))) {
          var repo = item;
          var osArray = [];
          if (item.operating_systems) {
            item.operating_systems.forEach(function (os) {
              os.id = item[repoVersionsKey].repository_version + os.OperatingSystems.os_type;
              os.repository_version_id = repo.id;
              var repoArray = [];
              if (Em.get(os, 'repositories')) {
                os.repositories.forEach(function (repo) {
                  repo.id = repo.Repositories.repo_id + os.id;
                  repo.operating_system_id = os.id;
                  repoArray.pushObject(repo);
                  resultRepo.push(this.parseIt(repo, this.get('modelRepository')));
                }, this);
              }
              os.repositories = repoArray;
              osArray.pushObject(os);
              resultOS.push(this.parseIt(os, this.get('modelOS')));
            }, this);
          }
          if (item[repoVersionsKey].stack_services) {
            item[repoVersionsKey].stack_services.forEach(function (service) {
              var serviceObj = {
                id: item[repoVersionsKey].repository_version + service.name,
                name: service.name,
                display_name: service.display_name,
                latest_version: service.versions[0] ? service.versions[0] : '',
                is_available: item[repoVersionsKey].services ? item[repoVersionsKey].services.someProperty( 'name', service.name) : true,
                is_upgradable: json.stackServices ? json.stackServices[service.name] && json.stackServices[service.name].upgrade : true
              };
              resultService.push(serviceObj);
            }, this);
          } else if (item[repoVersionsKey].services) {
            item[repoVersionsKey].services.forEach(function (service) {
              var serviceObj = {
                id: item[repoVersionsKey].repository_version + service.name,
                name: service.name,
                display_name: service.display_name,
                latest_version: service.versions[0] ? service.versions[0].version: ''
              };
              resultService.push(serviceObj);
            }, this);
          }
          repo.use_redhat_satellite = Em.get(item, 'operating_systems.0.OperatingSystems.ambari_managed_repositories') === false;
          repo.operating_systems = osArray;
          repo.stack_services = resultService;
          resultRepoVersion.push(this.parseIt(repo, this.modelRepoVersion(isCurrentStackOnly)));
        }
      }, this);
    }
    App.store.safeLoadMany(modelRepositories, resultRepo);
    App.store.safeLoadMany(modelOperatingSystems, resultOS);
    App.store.safeLoadMany(modelServices, resultService);
    App.store.safeLoadMany(modelRepoVersions, resultRepoVersion);
  },

  /**
   *
   * @param {?object} json
   * @returns {{items: Array}}
   */
  convertToRepoScheme: function(json) {
    var extractedJson = {items: []};

    if (json && json.items) {
      json.items.forEach(function(stack) {
        stack.versions.forEach(function(version) {
          version.repository_versions.forEach(function(repoVersion) {
            extractedJson.items.push(repoVersion);
          }, this);
        }, this);
      }, this);
    }
    return extractedJson;
  }
});
<|endoftext|>


<commit_before>import nextNode from './next-node';

function rangeNodes(range) {
    let node = range && range.start || null,
        endNode = range && range.end || null,
        nodes = [];

    if (!node || !endNode) return nodes;
    if (node === endNode) return [node];
    if (node.nodeType === 3) {
        nodes.push(node);
    }
    while (node && node !== endNode) {
        node = nextNode(node);
        if (node.nodeType === 3) {
            nodes.push(node);
        }
    }
    return nodes;
}

export default rangeNodes;<commit_msg>add isTextNode function<commit_after>import nextNode from './next-node';

const isTextNode = node => node && node.nodeType === 3;

function rangeNodes(range) {
    let node = range && range.start,
        endNode = range && range.end,
        nodes = [];

    if (!(node && endNode)) {
        return nodes;
    }
    if (isTextNode(node)) {
        nodes.push(node);
    }
    if (node === endNode) {
        return nodes;
    }
    while (node && node !== endNode) {
        node = nextNode(node);
        if (isTextNode(node)) {
            nodes.push(node);
        }
    }
    return nodes;
}

export default rangeNodes;<|endoftext|>