<commit_before>/** @ignore */
const _ = require('lodash');
/** @ignore */
const moment = require('moment');
/** @ignore */
const Command = require('./../Command');

/**
 * Purge Command, allows users with the right permissions to
 * delete messages that was sent within the last two weeks.
 *
 * @extends {Command}
 */
class PurgeCommand extends Command {

    /**
     * Sets up the command by providing the prefix, command trigger, any
     * aliases the command might have and additional options that
     * might be usfull for the abstract command class.
     */
    constructor() {
        super('purge', ['clear'], {
            allowDM: false,
            usage: [
                '<amount> [@tagedUser]'
            ],
            middleware: [
                'throttle.channel:1,5',
                'require:text.manage_messages'
            ]
        });

        /**
         * The amount of milliseconds in 14 days.
         *
         * @type {Number}
         */
        this.fourteenDays = 1000 * 60 * 60 * 24 * 14;
    }

    /**
     * Executes the given command.
     *
     * @param  {IUser}     sender   The Discordie user object that ran the command.
     * @param  {IMessage}  message  The Discordie message object that triggered the command.
     * @param  {Array}     args     The arguments that was parsed to the command.
     * @return {mixed}
     */
    onCommand(sender, message, args) {
        if (args.length === 0) {
            return this.sendMissingArguments(message);
        }

        let amount = Math.min(Math.max(parseInt(args[0], 10), 1) + 1, 1000);

        // If no users was tagged in the command we'll just process the deleted messages
        // without any filter, this will delete all the messages that can be fetched
        // from the Discord API within the message amount limit given.
        if (message.mentions.length === 0) {
            let promise = this.deleteMessages(message.channel, amount, messages => {
                return _.filter(messages, message => {
                    return !this.is14DaysOld(message.timestamp);
                });
            });

            return this.processDeletedMessages(promise, message, 'commands.administration.purge.all-messages');
        }

        let mentions = message.mentions;
        let users = [];

        for (let i = 0; i < mentions.length; i++) {
            users.push(`${mentions[i].username}#${mentions[i].discriminator}`);
        }

        return this.processDeletedMessages(this.deleteMessages(message.channel, amount, messages => {
            return _.filter(messages, message => {
                if (this.is14DaysOld(message.timestamp)) {
                    return false;
                }

                let authorId = message.author.id;
                for (let i = 0; i < mentions.length; i++) {
                    if (mentions[i].id === authorId) {
                        return true;
                    }
                }

                return false;
            });
        }), message, 'commands.administration.purge.user-messages', {users});
    }

    /**
     * Process deleted messages, sending the "x messages has been deleted"  message
     * if successfully, if something went wrong we'll let the user know as well.
     *
     * @param  {Promise}   promise         The promise that handeled deleting the messages.
     * @param  {IMessage}  message         The Discordie message object.
     * @param  {String}    languageString  The language string that should be sent to the user if it was successfully.
     * @param  {Object}    placeholders    The placeholders for the message.
     * @return {Promise}
     */
    processDeletedMessages(promise, message, languageString, placeholders = {}) {
        return promise.then(stats => {
            placeholders.amount = stats.deletedMessages;
            placeholders.skiped = stats.skipedMessages;

            // Sends the deleted messages confirmation message from the language files
            // and starts and delayed taks to delete the message after 3½ seconds.
            return app.envoyer.sendSuccess(message, languageString, placeholders).then(message => {
                return app.scheduler.scheduleDelayedTask(() => {
                    return app.envoyer.delete(message);
                }, 3500);
            });
        }).catch(err => {
            app.logger.error(err);
            return app.envoyer.sendWarn(message, ':warning: ' + err.response.res.body.message);
        });
    }

    /**
     * Delete the list of messages given, if more than 100 messages
     * is given the method will call itself with the next set of
     * messages until it has deleted all of the messages.
     *
     * @param  {ITextChannel}  channel          The channel the messages should be deleted in.
     * @param  {Number }       left             The number of messages there are left to be deleted
     * @param  {Function}      filter           The filter that separates messages that should and shouldn't be deleted.
     * @param  {Number}        deletedMessages  Number of messages that has been deleted.
     * @return {Promise}
     */
    deleteMessages(channel, left, filter = null, stats = null) {
        if (stats === null) {
            stats = {
                deletedMessages: 0,
                skipedMessages: 0
            };
        }
        return channel.fetchMessages(Math.min(left, 100)).then(result => {
            // If the filter variable is a callback and the list of messages isn't undefined
            // we'll parse in the messages from the Discord API request to filter them
            // down so we're left with only the messages matching our filter.
            if (typeof filter === 'function' && typeof result.messages !== 'undefined') {
                let before = result.messages.length;

                result.messages = filter(result.messages);
                stats.skipedMessages += before - result.messages.length;
            }

            // If the messages length is 1 or lower we'll end the loop and send back the amount of deleted messages
            // since the only message in the fetch request would be the message that triggered the command.
            if (result.messages.length < 2) {
                return stats;
            }

            return bot.Messages.deleteMessages(result.messages).then(() => {
                stats.deletedMessages += result.messages.length;

                // Checks to see if we have more messages that needs to be deleted, and if the result from
                // the last request to the Discord API returned less then what we requested for, if
                // that is the case we can assume the channel doesn't have anymore messages.
                if (left > 100 && result.limit === 100 && result.limit === result.messages.length) {
                    return this.deleteMessages(channel, left - 100, filter, stats);
                }

                return stats;
            });
        });
    }

    /**
     * Check if the given timestamp is older than 14 days.
     *
     * @param  {String}  timestamp  The message timestamp that should be checked.
     * @return {Boolean}
     */
    is14DaysOld(timestamp) {
        return (moment(timestamp).diff(new Date) * -1) > this.fourteenDays;
    }
}

module.exports = PurgeCommand;
<commit_msg>Fix esdoc documentation for the deleteMessages method in purge command<commit_after>/** @ignore */
const _ = require('lodash');
/** @ignore */
const moment = require('moment');
/** @ignore */
const Command = require('./../Command');

/**
 * Purge Command, allows users with the right permissions to
 * delete messages that was sent within the last two weeks.
 *
 * @extends {Command}
 */
class PurgeCommand extends Command {

    /**
     * Sets up the command by providing the prefix, command trigger, any
     * aliases the command might have and additional options that
     * might be usfull for the abstract command class.
     */
    constructor() {
        super('purge', ['clear'], {
            allowDM: false,
            usage: [
                '<amount> [@tagedUser]'
            ],
            middleware: [
                'throttle.channel:1,5',
                'require:text.manage_messages'
            ]
        });

        /**
         * The amount of milliseconds in 14 days.
         *
         * @type {Number}
         */
        this.fourteenDays = 1000 * 60 * 60 * 24 * 14;
    }

    /**
     * Executes the given command.
     *
     * @param  {IUser}     sender   The Discordie user object that ran the command.
     * @param  {IMessage}  message  The Discordie message object that triggered the command.
     * @param  {Array}     args     The arguments that was parsed to the command.
     * @return {mixed}
     */
    onCommand(sender, message, args) {
        if (args.length === 0) {
            return this.sendMissingArguments(message);
        }

        let amount = Math.min(Math.max(parseInt(args[0], 10), 1) + 1, 1000);

        // If no users was tagged in the command we'll just process the deleted messages
        // without any filter, this will delete all the messages that can be fetched
        // from the Discord API within the message amount limit given.
        if (message.mentions.length === 0) {
            let promise = this.deleteMessages(message.channel, amount, messages => {
                return _.filter(messages, message => {
                    return !this.is14DaysOld(message.timestamp);
                });
            });

            return this.processDeletedMessages(promise, message, 'commands.administration.purge.all-messages');
        }

        let mentions = message.mentions;
        let users = [];

        for (let i = 0; i < mentions.length; i++) {
            users.push(`${mentions[i].username}#${mentions[i].discriminator}`);
        }

        return this.processDeletedMessages(this.deleteMessages(message.channel, amount, messages => {
            return _.filter(messages, message => {
                if (this.is14DaysOld(message.timestamp)) {
                    return false;
                }

                let authorId = message.author.id;
                for (let i = 0; i < mentions.length; i++) {
                    if (mentions[i].id === authorId) {
                        return true;
                    }
                }

                return false;
            });
        }), message, 'commands.administration.purge.user-messages', {users});
    }

    /**
     * Process deleted messages, sending the "x messages has been deleted"  message
     * if successfully, if something went wrong we'll let the user know as well.
     *
     * @param  {Promise}   promise         The promise that handeled deleting the messages.
     * @param  {IMessage}  message         The Discordie message object.
     * @param  {String}    languageString  The language string that should be sent to the user if it was successfully.
     * @param  {Object}    placeholders    The placeholders for the message.
     * @return {Promise}
     */
    processDeletedMessages(promise, message, languageString, placeholders = {}) {
        return promise.then(stats => {
            placeholders.amount = stats.deletedMessages;
            placeholders.skiped = stats.skipedMessages;

            // Sends the deleted messages confirmation message from the language files
            // and starts and delayed taks to delete the message after 3½ seconds.
            return app.envoyer.sendSuccess(message, languageString, placeholders).then(message => {
                return app.scheduler.scheduleDelayedTask(() => {
                    return app.envoyer.delete(message);
                }, 3500);
            });
        }).catch(err => {
            app.logger.error(err);
            return app.envoyer.sendWarn(message, ':warning: ' + err.response.res.body.message);
        });
    }

    /**
     * Delete the list of messages given, if more than 100 messages
     * is given the method will call itself with the next set of
     * messages until it has deleted all of the messages.
     *
     * @param  {ITextChannel}  channel  The channel the messages should be deleted in.
     * @param  {Number }       left     The number of messages there are left to be deleted
     * @param  {Function}      filter   The filter that separates messages that should and shouldn't be deleted.
     * @param  {Object}        stats    Number of messages that has been deleted.
     * @return {Promise}
     */
    deleteMessages(channel, left, filter = null, stats = null) {
        if (stats === null) {
            stats = {
                deletedMessages: 0,
                skipedMessages: 0
            };
        }
        return channel.fetchMessages(Math.min(left, 100)).then(result => {
            // If the filter variable is a callback and the list of messages isn't undefined
            // we'll parse in the messages from the Discord API request to filter them
            // down so we're left with only the messages matching our filter.
            if (typeof filter === 'function' && typeof result.messages !== 'undefined') {
                let before = result.messages.length;

                result.messages = filter(result.messages);
                stats.skipedMessages += before - result.messages.length;
            }

            // If the messages length is 1 or lower we'll end the loop and send back the amount of deleted messages
            // since the only message in the fetch request would be the message that triggered the command.
            if (result.messages.length < 2) {
                return stats;
            }

            return bot.Messages.deleteMessages(result.messages).then(() => {
                stats.deletedMessages += result.messages.length;

                // Checks to see if we have more messages that needs to be deleted, and if the result from
                // the last request to the Discord API returned less then what we requested for, if
                // that is the case we can assume the channel doesn't have anymore messages.
                if (left > 100 && result.limit === 100 && result.limit === result.messages.length) {
                    return this.deleteMessages(channel, left - 100, filter, stats);
                }

                return stats;
            });
        });
    }

    /**
     * Check if the given timestamp is older than 14 days.
     *
     * @param  {String}  timestamp  The message timestamp that should be checked.
     * @return {Boolean}
     */
    is14DaysOld(timestamp) {
        return (moment(timestamp).diff(new Date) * -1) > this.fourteenDays;
    }
}

module.exports = PurgeCommand;
<|endoftext|>


<commit_before>// Player component
var Player = {
	template: `

				<canvas id="letter"></canvas>

`,
	props: ['item'],
	data: function() {
		return {
			size: -1,
			zoom: -1,
			current: { start: -1, track: -1, tracks: [] }
		}
	},
	methods: {
		computeSize: function() {
			// Compute optimal size for letter
			var vm = this;
			var body = document.getElementById("canvas") || document.getElementById("body");
			var body_height = body.offsetHeight-50;
			var body_width = body.offsetWidth-50;
			var size = { width: body_width, height: body_height };
			vm.size = Math.min(size.width, size.height);
			var letter = document.getElementById("letter");
			letter.width = vm.size;
			letter.height = vm.size;
			letter.style.marginLeft = (size.width-vm.size)/2-50 + "px";
			vm.zoom = vm.size/document.getElementById("miniletter").naturalWidth;

			// Draw
			this.draw();
		},

		initEvent: function() {
			// Register click/touch on letter event
			var vm = this;
			var clickEvent = "click";
			var touchScreen = ("ontouchstart" in document.documentElement);
			if (touchScreen) {
				clickEvent = "touchend";
			}
			var letter = document.getElementById("letter");
			letter.addEventListener(clickEvent, function(e) {
				var x = Math.floor((e.clientX-letter.getBoundingClientRect().left)/vm.zoom);
				var y = Math.floor((e.clientY-letter.getBoundingClientRect().top)/vm.zoom);
			});
		},

		draw: function() {
			// Draw board
			var vm = this;
			var letter = document.getElementById("letter");
			var context = letter.getContext('2d');
			context.clearRect(0, 0, vm.size, vm.size);
			var imageObj = new Image();
			imageObj.onload = function() {
				// Draw letter
				context.drawImage(imageObj, 0, 0, vm.size, vm.size);
			};
			imageObj.src = vm.item.image;
		},

		onLoad: function() {
			this.computeSize();
			this.initEvent();
		},

		goBack: function() {
			app.displayTemplateView();
		}
	},

	mounted: function() {
		var vm = this;
		var timeout = 70;
		var step = function() {
			// Draw a segment of path
			var line = vm.current.tracks[vm.current.start][vm.current.track];
			var letter = document.getElementById("letter");
			if (!letter) {
				return;
			}
			var context = letter.getContext('2d');
			context.beginPath();
			context.strokeStyle = app.color.stroke;
			context.lineWidth = 10;
			context.lineCap = "round";
			context.moveTo(vm.zoom*line.x0, vm.zoom*line.y0);
			context.lineTo(vm.zoom*line.x1, vm.zoom*line.y1);
			context.stroke();
			vm.current.track++;
			if (vm.current.track >= vm.current.tracks[vm.current.start].length) {
				vm.current.start++;
				vm.current.track = 0;
				if (vm.current.start < vm.current.tracks.length) {
					setTimeout(step, timeout);
				}
			} else {
				setTimeout(step, timeout);
			}
		}
		if (vm.item.starts) {
			// Create lines set to draw letter
			vm.current.start = 0;
			vm.current.track = 0;
			vm.current.tracks = [];
			for (var i = 0 ; i < vm.item.starts.length ; i++) {
				if (!vm.item.starts[i].path) {
					continue;
				}
				var lines = [];
				var path = vm.item.starts[i].path;
				if (!vm.item.starts[i].path.length) {
					lines.push({x0: vm.item.starts[i].x, y0: vm.item.starts[i].y, x1: vm.item.starts[i].x, y1: vm.item.starts[i].y})
				} else {
					lines.push({x0: vm.item.starts[i].x, y0: vm.item.starts[i].y, x1: path[0].x, y1: path[0].y})
				}
				for (var j = 0 ; j < path.length ; j++) {
					if (j+1 < path.length) {
						lines.push({x0: path[j].x, y0: path[j].y, x1: path[j+1].x, y1: path[j+1].y});
					}
				}
				vm.current.tracks.push(lines);
			}
			setTimeout(step, timeout);
		}
	}
};
<commit_msg>Prepare input mode<commit_after>// Player component
var Player = {
	template: `

				<canvas id="letter"></canvas>

`,
	props: ['item'],
	data: function() {
		return {
			size: -1,
			zoom: -1,
			current: { start: -1, track: -1, tracks: [] },
			mode: ''
		}
	},
	methods: {
		computeSize: function() {
			// Compute optimal size for letter
			var vm = this;
			var body = document.getElementById("canvas") || document.getElementById("body");
			var body_height = body.offsetHeight-50;
			var body_width = body.offsetWidth-50;
			var size = { width: body_width, height: body_height };
			vm.size = Math.min(size.width, size.height);
			var letter = document.getElementById("letter");
			letter.width = vm.size;
			letter.height = vm.size;
			letter.style.marginLeft = (size.width-vm.size)/2-50 + "px";
			vm.zoom = vm.size/document.getElementById("miniletter").naturalWidth;

			// Draw
			this.draw();
		},

		initEvent: function() {
			// Register click/touch on letter event
			var vm = this;
			var clickEvent = "mousemove";
			var touchScreen = ("ontouchstart" in document.documentElement);
			if (touchScreen) {
				clickEvent = "touchend";
			}
			var letter = document.getElementById("letter");
			letter.addEventListener(clickEvent, function(e) {
				if (vm.mode != 'input') {
					return;
				}
				var x = Math.floor((e.clientX-letter.getBoundingClientRect().left)/vm.zoom);
				var y = Math.floor((e.clientY-letter.getBoundingClientRect().top)/vm.zoom);
				vm.current.tracks.push({x: x, y: y});
				vm.draw();
			});
		},

		draw: function() {
			// Draw board
			var vm = this;
			var letter = document.getElementById("letter");
			var context = letter.getContext('2d');
			context.clearRect(0, 0, vm.size, vm.size);
			var imageObj = new Image();
			imageObj.onload = function() {
				// Draw letter
				context.drawImage(imageObj, 0, 0, vm.size, vm.size);

				// Draw current position
				if (vm.mode == 'input' && vm.current.tracks.length) {
					context.beginPath();
					context.strokeStyle = app.color.stroke;
					context.lineWidth = 10;
					context.lineCap = "round";
					context.lineJoin = "round";
					context.moveTo(vm.zoom*vm.current.tracks[0].x, vm.zoom*vm.current.tracks[0].y);
					for (var i = 1 ; i < vm.current.tracks.length ; i++) {
						context.lineTo(vm.zoom*vm.current.tracks[i].x, vm.zoom*vm.current.tracks[i].y);
					}
					context.stroke();
				}
			};
			imageObj.src = vm.item.image;
		},

		onLoad: function() {
			this.computeSize();
			this.initEvent();
		},

		goBack: function() {
			app.displayTemplateView();
		}
	},

	mounted: function() {
		var vm = this;
		var timeout = 70;
		vm.mode = 'show';
		var step = function() {
			// Draw a segment of path
			var line = vm.current.tracks[vm.current.start][vm.current.track];
			var letter = document.getElementById("letter");
			if (!letter) {
				return;
			}
			var context = letter.getContext('2d');
			context.beginPath();
			context.strokeStyle = app.color.stroke;
			context.lineWidth = 10;
			context.lineCap = "round";
			context.lineJoin = "round";
			context.moveTo(vm.zoom*line.x0, vm.zoom*line.y0);
			context.lineTo(vm.zoom*line.x1, vm.zoom*line.y1);
			context.stroke();
			vm.current.track++;
			if (vm.current.track >= vm.current.tracks[vm.current.start].length) {
				vm.current.start++;
				vm.current.track = 0;
				if (vm.current.start < vm.current.tracks.length) {
					setTimeout(step, timeout);
				} else {
					setTimeout(function() {
						vm.mode = 'input';
						vm.current.start = 0;
						vm.current.track = 0;
						vm.current.tracks = [];
						if (vm.item.starts && vm.item.starts.length) {
							vm.current.tracks.push({x: vm.item.starts[0].x, y: vm.item.starts[0].y});
							vm.current.tracks.push({x: vm.item.starts[0].x, y: vm.item.starts[0].y});
						}
						vm.draw();
					}, 500);
				}
			} else {
				setTimeout(step, timeout);
			}
		}
		if (vm.item.starts) {
			// Create lines set to draw letter
			vm.current.start = 0;
			vm.current.track = 0;
			vm.current.tracks = [];
			for (var i = 0 ; i < vm.item.starts.length ; i++) {
				if (!vm.item.starts[i].path) {
					continue;
				}
				var lines = [];
				var path = vm.item.starts[i].path;
				if (!vm.item.starts[i].path.length) {
					lines.push({x0: vm.item.starts[i].x, y0: vm.item.starts[i].y, x1: vm.item.starts[i].x, y1: vm.item.starts[i].y})
				} else {
					lines.push({x0: vm.item.starts[i].x, y0: vm.item.starts[i].y, x1: path[0].x, y1: path[0].y})
				}
				for (var j = 0 ; j < path.length ; j++) {
					if (j+1 < path.length) {
						lines.push({x0: path[j].x, y0: path[j].y, x1: path[j+1].x, y1: path[j+1].y});
					}
				}
				vm.current.tracks.push(lines);
			}
			setTimeout(step, timeout);
		}
	}
};
<|endoftext|>


<commit_before>import Ember from 'ember';
import layout from '../templates/components/ember-perfect-gallery';

export default Ember.Component.extend({
  layout,
  photos: [],
  photosChanged: Ember.observer('photos', function() {
    var scrollX = window.scrollX,
      scrollY = window.scrollY;

    Ember.run.schedule('afterRender', () => {
      var containerElement = document.getElementById('ember-perfect-gallery');
      this.perfectLayout(containerElement, this.get('photos'));

      Ember.run.schedule('afterRender', () => {
        window.scrollTo(scrollX, scrollY);
      });
    });
  }),

  // https://github.com/mike-north/ember-resize/issues/43
  resizeService: Ember.inject.service('resize'),

  didInsertElement() {
    this._super(...arguments);
    this.get('photosChanged');

    var containerElement = document.getElementById('ember-perfect-gallery');
    this.perfectLayout(containerElement, this.get('photos'));

    this.get('resizeService').on('didResize', () => {
      var numOfPhotos = this.get('photos').length;
      for (var i = 0; i < numOfPhotos; i++) {
        var photoElement = document.getElementsByClassName('ember-perfect-gallery-image')[i];
        photoElement.style.width = `0px`;
        photoElement.style.height = `0px`;
      }

      this.perfectLayout(containerElement, this.get('photos'));
    });
  },

  perfectLayout(node, photos) {
    // The '-20' magical number is a reduction in the width of gallery container DOM element.
    // It is necessary for the images to stay in one row as the window is resized.
    // It was found through experimentation to work well at this vaue.
    // perfect-layout computes the sizes well, but there's something in the DOM sizing (no extra padding/margins added)
    // itself that ends up increasing the width and wrapping elements around.
    // TODO: https://github.com/myartsev/ember-perfect-gallery/issues/2: needs investigation, I don't like magic numbers.
    var width = $(node).width() - 20;
    const perfectRows = perfectLayout(photos, width, $(window).height(), {
      margin: 2
    });

    var photoIndex = 0;
    perfectRows.forEach(function(row) {
      row.forEach(function(img) {
        var photoElement = document.getElementsByClassName('ember-perfect-gallery-image')[photoIndex++];
        photoElement.style.width = `${img.width}px`;
        photoElement.style.height = `${img.height}px`;
        photoElement.style.background = `url('${img.src}')`;
        photoElement.style['background-size'] = 'cover';
      });
    });
  },
});
<commit_msg>Fixes #6: handle the case where all images fit into a single row<commit_after>import Ember from 'ember';
import layout from '../templates/components/ember-perfect-gallery';

export default Ember.Component.extend({
  layout,
  photos: [],
  photosChanged: Ember.observer('photos', function() {
    var scrollX = window.scrollX,
      scrollY = window.scrollY;

    Ember.run.schedule('afterRender', () => {
      var containerElement = document.getElementById('ember-perfect-gallery');
      this.perfectLayout(containerElement, this.get('photos'));

      Ember.run.schedule('afterRender', () => {
        window.scrollTo(scrollX, scrollY);
      });
    });
  }),

  // https://github.com/mike-north/ember-resize/issues/43
  resizeService: Ember.inject.service('resize'),

  didInsertElement() {
    this._super(...arguments);
    this.get('photosChanged');

    var containerElement = document.getElementById('ember-perfect-gallery');
    this.perfectLayout(containerElement, this.get('photos'));

    this.get('resizeService').on('didResize', () => {
      var numOfPhotos = this.get('photos').length;
      for (var i = 0; i < numOfPhotos; i++) {
        var photoElement = document.getElementsByClassName('ember-perfect-gallery-image')[i];
        photoElement.style.width = `0px`;
        photoElement.style.height = `0px`;
      }

      this.perfectLayout(containerElement, this.get('photos'));
    });
  },

  perfectLayout(node, photos) {
    // The '-20' magical number is a reduction in the width of gallery container DOM element.
    // It is necessary for the images to stay in one row as the window is resized.
    // It was found through experimentation to work well at this vaue.
    // perfect-layout computes the sizes well, but there's something in the DOM sizing (no extra padding/margins added)
    // itself that ends up increasing the width and wrapping elements around.
    // TODO: https://github.com/myartsev/ember-perfect-gallery/issues/2: needs investigation, I don't like magic numbers.
    var width = $(node).width() - 20;
    const perfectRows = perfectLayout(photos, width, $(window).height(), {
      margin: 2
    });

    var photoIndex = 0;
    perfectRows.forEach(function(row) {
      if(!row.forEach) {
        setImageStyles(row);
      } else {
        row.forEach(function(img) {
          setImageStyles(img);
        });
      }

      function setImageStyles(img) {
        var photoElement = document.getElementsByClassName('ember-perfect-gallery-image')[photoIndex++];
        photoElement.style.width = `${img.width}px`;
        photoElement.style.height = `${img.height}px`;
        photoElement.style.background = `url('${img.src}')`;
        photoElement.style['background-size'] = 'cover';
      };
    });
  },
});
<|endoftext|>


<commit_before>import { alias } from '@ember/object/computed';
import Component from '@ember/component';
import { computed } from '@ember/object';

export default Component.extend({
  tagName: 'th',
  classNames: ['upf-datatable__column'],
  classNameBindings: [
    'column.titleIcon:text-size-6',
    'sort'
  ],
  attributeBindings: ['width'],
  width: alias('column.width'),

  sort: computed('column.{sorted,direction}', function() {
    if (!this.get('column.sorted')) {
      return 'upf-datatable__column--unsorted';
    }

    // class inversed ?
    if (this.get('column.direction') === 'asc') {
      return 'upf-datatable__column--descending';
    }

    return 'upf-datatable__column--ascending';
  }),

  didRender() {
    this._super();
    if(this.column.tooltip) {
      this.$('[data-toggle="tooltip"]').tooltip();
    }
  }
});
<commit_msg>Remove jquery tooltip issue<commit_after>import { alias } from '@ember/object/computed';
import Component from '@ember/component';
import { computed } from '@ember/object';

export default Component.extend({
  tagName: 'th',
  classNames: ['upf-datatable__column'],
  classNameBindings: [
    'column.titleIcon:text-size-6',
    'sort'
  ],
  attributeBindings: ['width'],
  width: alias('column.width'),

  sort: computed('column.{sorted,direction}', function() {
    if (!this.get('column.sorted')) {
      return 'upf-datatable__column--unsorted';
    }

    // class inversed ?
    if (this.get('column.direction') === 'asc') {
      return 'upf-datatable__column--descending';
    }

    return 'upf-datatable__column--ascending';
  })
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import ajax from 'ic-ajax';

export default Ember.ObjectController.extend({

    actions: {
        signIn: function() {
            this.set('signingIn', true);
            ajax({
                type: 'PUT',
                url: '/api_v1/authentication/signin',
                contentType: 'application/json',
                data: JSON.stringify({
                    credential: this.getProperties('email', 'password', 'rememberMe')
                })
            }).then(function() {
                this.set('signingIn', false);

                if (!this.session.tryTransitionOnSignIn())
                    this.transitionToRoute('portal');
            }.bind(this), function(response) {

                this.set('signingIn', false);
                this.session.showGlobalAlert(response.jqXHR.responseJSON || response.jqXHR.responseText, 6000, 'danger');

            }.bind(this));
        }
    }

});
<commit_msg>signin error bug fix<commit_after>import Ember from 'ember';
import ajax from 'ic-ajax';

export default Ember.ObjectController.extend({

    actions: {
        signIn: function() {
            this.set('signingIn', true);
            ajax({
                type: 'PUT',
                url: '/api_v1/authentication/signin',
                contentType: 'application/json',
                data: JSON.stringify({
                    credential: this.getProperties('email', 'password', 'rememberMe')
                })
            }).then(function() {
                this.set('signingIn', false);

                if (!this.session.tryTransitionOnSignIn())
                    this.transitionToRoute('portal');
            }.bind(this), function(response) {

                this.set('signingIn', false);
                this.session.showGlobalAlert(response.jqXHR, 6000, 'danger');

            }.bind(this));
        }
    }

});
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Controller.extend({

  /**
    Value search input.

    @property searchValue
    @type String
    @default ''
  */
  searchValue: '',

  /**
    Update model for search

    @method filteredModel
  */
  filteredModel: Ember.computed('searchValue', function() {
    let searchStr = this.get('searchValue').trim().toLocaleLowerCase();
    let model = this.get('model');

    if (Ember.isBlank(searchStr)) {
      return model;
    }

    let filterFunction = function(item) {
      let name = item.get('name');
      if (!Ember.isNone(name) && name.toLocaleLowerCase().indexOf(searchStr) !== -1) {
        return item;
      }
    };

    let newClasses = model.classes.filter(function(clazz) {
      let classes = filterFunction(clazz.settings);
      if (!Ember.isNone(classes)) {
        return clazz;
      }

      let editForms = clazz.editForms.some(filterFunction);
      let listForms = clazz.listForms.some(filterFunction);
      let parents = clazz.parents.some(filterFunction);
      let bs = !Ember.isNone(clazz.bs) ? filterFunction(clazz.bs) : null;

      if (editForms || listForms || parents || !Ember.isNone(bs)) {
        return clazz;
      }
    });

    let newModel = {
      classes: Ember.A(newClasses)
    };

    for (let prop in model) {
      if (prop !== 'classes') {
        let newdata = model[prop].filter(filterFunction);
        newModel[prop] = Ember.A(newdata);
      }
    }

    return newModel;
  })
});
<commit_msg>Fix remarks 4<commit_after>import Ember from 'ember';

export default Ember.Controller.extend({

  /**
    Value search input.

    @property searchValue
    @type String
    @default ''
  */
  searchValue: '',

  /**
    Update model for search

    @method filteredModel
  */
  filteredModel: Ember.computed('searchValue', function() {
    let searchStr = this.get('searchValue');
    let model = this.get('model');

    if (Ember.isBlank(searchStr)) {
      return model;
    }

    searchStr = searchStr.trim().toLocaleLowerCase();
    let filterFunction = function(item) {
      let name = item.get('name');
      if (!Ember.isNone(name) && name.toLocaleLowerCase().indexOf(searchStr) !== -1) {
        return item;
      }
    };

    let newClasses = model.classes.filter(function(clazz) {
      let classes = filterFunction(clazz.settings);
      if (!Ember.isNone(classes)) {
        return clazz;
      }

      let editForms = clazz.editForms.some(filterFunction);
      let listForms = clazz.listForms.some(filterFunction);
      let parents = clazz.parents.some(filterFunction);
      let bs = !Ember.isNone(clazz.bs) ? filterFunction(clazz.bs) : null;

      if (editForms || listForms || parents || !Ember.isNone(bs)) {
        return clazz;
      }
    });

    let newModel = {
      classes: Ember.A(newClasses)
    };

    for (let prop in model) {
      if (prop !== 'classes') {
        let newdata = model[prop].filter(filterFunction);
        newModel[prop] = Ember.A(newdata);
      }
    }

    return newModel;
  })
});
<|endoftext|>


<commit_before>import { put, takeLatest } from 'redux-saga/effects';
import { requestStarted, requestFinished } from '../../../actions/common';
import { RESTORE_PASSWORD } from './constants';
import { restorationResent } from './actions';

function* restorePasswordSaga({ payload: { email } }) {
  yield put(requestStarted());
  console.info(email);
  yield put(requestFinished());
  yield put(restorationResent());
}

export function* forgotPasswordFlow() {
  yield takeLatest(RESTORE_PASSWORD, restorePasswordSaga);
}

export default [
  forgotPasswordFlow,
];
<commit_msg>ForgotPassword sagas.<commit_after>import { put, takeLatest, call } from 'redux-saga/effects';
import { requestStarted, requestFinished } from '../../../actions/common';
import { RESTORE_PASSWORD } from './constants';
import { restorationResent } from './actions';
import routes from '../../../utils/network/api';

function* restorePasswordSaga({ payload: { email } }) {
  yield put(requestStarted());
  const response = yield call(routes.user.forgotPassword, { email });
  yield put(requestFinished());

  if (response.err) {
    alert('error...');
  } else {
    yield put(restorationResent());
  }
}

export function* forgotPasswordFlow() {
  yield takeLatest(RESTORE_PASSWORD, restorePasswordSaga);
}

export default [
  forgotPasswordFlow,
];
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Activity = mongoose.model('Activity'),
	User = mongoose.model('User'),
	_ = require('lodash');

/**
 * Create a Activity
 */
exports.create = function(req, res) {
	var activity = new Activity(req.body);
	console.log(activity);
	if (activity.teachers.length === 0) {
		return res.status(400).send({
			message: 'Please specify at least one teacher for the activity'
		});
	}
	activity.user = req.user;
	activity.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		}
		res.jsonp(activity);
	});
};

/**
 * Show the current Activity
 */
exports.read = function(req, res) {
	res.jsonp(req.activity);
};

/**
 * Update a Activity
 */
exports.update = function(req, res) {
	var activity = req.activity;
	activity = _.extend(activity, req.body);
	activity.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		}
		res.jsonp(activity);
	});
};

/**
 * Delete an Activity
 */
exports.delete = function(req, res) {
	var activity = req.activity;
	activity.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		}
		res.jsonp(activity);
	});
};

/**
 * List of Activities
 */
exports.list = function(req, res) {
	Activity.find({}, 'ID name')
			.sort({ID: 1})
			.exec(function(err, activities) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		}
		res.jsonp(activities);
	});
};

/**
 * Activity middleware
 */
exports.activityByID = function(req, res, next, id) {
	if (req.method === 'POST') {
		return next();
	}
	Activity.findOne({ID: id}, 'ID name teachers weight')
			.populate('teachers', 'username')
			.exec(function(err, activity) {
		if (err) {
			return next(err);
		}
		if (! activity) {
			return next(new Error('Failed to load Activity ' + id));
		}
		req.activity = activity;
		next();
	});
};

/**
 * Activity authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	if (req.activity.user.id !== req.user.id) {
		return res.status(403).send('User is not authorized');
	}
	next();
};

exports.listTeachers = function(req, res) {
	User.find('teacher', 'serial displayName')
		.sort({serial: 1})
		.exec(function(err, users) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		}
		res.jsonp(users);
	});
};
<commit_msg>Remove console.log<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Activity = mongoose.model('Activity'),
	User = mongoose.model('User'),
	_ = require('lodash');

/**
 * Create a Activity
 */
exports.create = function(req, res) {
	var activity = new Activity(req.body);
	if (activity.teachers.length === 0) {
		return res.status(400).send({
			message: 'Please specify at least one teacher for the activity'
		});
	}
	activity.user = req.user;
	activity.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		}
		res.jsonp(activity);
	});
};

/**
 * Show the current Activity
 */
exports.read = function(req, res) {
	res.jsonp(req.activity);
};

/**
 * Update a Activity
 */
exports.update = function(req, res) {
	var activity = req.activity;
	activity = _.extend(activity, req.body);
	activity.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		}
		res.jsonp(activity);
	});
};

/**
 * Delete an Activity
 */
exports.delete = function(req, res) {
	var activity = req.activity;
	activity.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		}
		res.jsonp(activity);
	});
};

/**
 * List of Activities
 */
exports.list = function(req, res) {
	Activity.find({}, 'ID name')
			.sort({ID: 1})
			.exec(function(err, activities) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		}
		res.jsonp(activities);
	});
};

/**
 * Activity middleware
 */
exports.activityByID = function(req, res, next, id) {
	if (req.method === 'POST') {
		return next();
	}
	Activity.findOne({ID: id}, 'ID name teachers weight')
			.populate('teachers', 'username')
			.exec(function(err, activity) {
		if (err) {
			return next(err);
		}
		if (! activity) {
			return next(new Error('Failed to load Activity ' + id));
		}
		req.activity = activity;
		next();
	});
};

/**
 * Activity authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	if (req.activity.user.id !== req.user.id) {
		return res.status(403).send('User is not authorized');
	}
	next();
};

exports.listTeachers = function(req, res) {
	User.find('teacher', 'serial displayName')
		.sort({serial: 1})
		.exec(function(err, users) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		}
		res.jsonp(users);
	});
};
<|endoftext|>


<commit_before>import { inject as service } from '@ember/service';
import { on } from '@ember/object/evented';
import Component from '@ember/component';
import { set, get, computed, observer } from '@ember/object';
import { alias } from '@ember/object/computed';
import layout from './template';

const PROMETHEUS = 'prometheus';
const NONE = 'none';

export default Component.extend({
  scope: service(),
  growl: service(),

  layout,

  answers:                     null,
  selected:                    NONE,
  level:                       'cluster',
  justDeployed:                false,
  enablePrometheusPersistence: false,
  enableGrafanaPersistence:    false,
  enableNodeExporter:          true,
  prometheusPersistenceSize:   '50Gi',
  grafanaPersistenceSize:      '10Gi',
  prometheusStorageClass:      null,
  grafanaStorageClass:         null,
  nodeSelectors:               null,
  retention:                   360,
  port:                        9796,

  cluster: alias('scope.currentCluster'),
  project: alias('scope.currentProject'),

  actions: {
    changeSelected(selected) {
      set(this, 'selected', selected);
    },

    save(cb) {
      if ( get(this, 'selected') === NONE ) {
        this.send('disable', cb);
      } else {
        this.send('enable', cb);
      }
    },

    enable(cb) {
      const resource = get(this, 'level') === 'cluster' ? get(this, 'cluster') : get(this, 'project');

      let answers = get(this, 'answers') || {};

      if ( get(this, 'level') === 'cluster' ) {
        answers['exporter-node.enabled'] = `${ get(this, 'enableNodeExporter') }`;
        answers['exporter-node.ports.metrics.port'] = `${ get(this, 'port') }`;
        answers['exporter-kubelets.https'] = `${ !(get(this, 'scope.currentCluster.isGKE') || get(this, 'scope.currentCluster.isAKS')) }`;
      }

      answers['prometheus.retention'] = `${ get(this, 'retention') }h`;
      answers['grafana.persistence.enabled'] = `${ get(this, 'enableGrafanaPersistence') }`;
      answers['prometheus.persistence.enabled'] = `${ get(this, 'enablePrometheusPersistence') }`;
      answers['prometheus.persistence.storageClass'] = `${ get(this, 'prometheusStorageClass') === null ? 'default' : get(this, 'prometheusStorageClass') }`;
      answers['grafana.persistence.storageClass'] = `${ get(this, 'grafanaStorageClass') === null ? 'default' : get(this, 'grafanaStorageClass') }`;
      answers['grafana.persistence.size'] = `${ get(this, 'grafanaPersistenceSize') }`;
      answers['prometheus.persistence.size'] = `${ get(this, 'prometheusPersistenceSize') }`;

      Object.keys(answers).filter((key) => key.startsWith('prometheus.nodeSelectors[') ).forEach((key) => {
        delete answers[key]
      });

      (get(this, 'nodeSelectors') || []).forEach((selector, index) => {
        let s = get(selector, 'key');

        if ( get(selector, 'value') ) {
          s += `=${ get(selector, 'value') }`;
        }
        answers[`prometheus.nodeSelectors[${ index }]`] = s;
      });

      let action = get(this, 'enabled') ?  'editMonitoring' : 'enableMonitoring';

      resource.doAction(action, { answers }).then(() => {
        set(this, 'justDeployed', true);
        cb(true);
        this.fetchSettings();
      }).catch((err) => {
        get(this, 'growl').fromError(err);
        cb(false);
      });
    },

    disable(cb) {
      const resource = get(this, 'level') === 'cluster' ? get(this, 'cluster') : get(this, 'project');

      resource.doAction('disableMonitoring').then(() => {
        cb(true);
        set(this, 'answers', null);
      }).catch((err) => {
        get(this, 'growl').fromError(err);
        cb(false);
      });
    }
  },

  enabled: computed('cluster.enableClusterMonitoring', 'project.enableProjectMonitoring', 'level', function() {
    return get(this, 'level') === 'cluster' ? get(this, 'cluster.enableClusterMonitoring') : get(this, 'project.enableProjectMonitoring');
  }),

  saveDisabled: computed('selected', 'enabled', function() {
    return (get(this, 'selected') === NONE && !get(this, 'enabled'));
  }),

  initSettings: on('init', observer('scope.currentProject.id', 'scope.currentCluster.id', function() {
    set(this, 'selected', get(this, 'enabled') ? PROMETHEUS : NONE);

    if ( get(this, 'enabled') ) {
      this.fetchSettings();
    }
  })),

  fetchSettings() {
    const resource = get(this, 'level') === 'cluster' ? get(this, 'cluster') : get(this, 'project');

    set(this, 'loading', true);
    resource.waitForAction('viewMonitoring').then(() => {
      resource.doAction('viewMonitoring').then((res) => {
        const answers = get(res, 'answers');

        set(this, 'answers', answers);
        this.updateConfig(answers);
        set(this, 'loading', false);
      }).catch((err) => {
        get(this, 'growl').fromError(err);
        set(this, 'loading', false);
      });
    })
  },

  updateConfig(answers) {
    if ( answers['prometheus.retention'] ) {
      set(this, 'retention', answers['prometheus.retention'].substr(0, answers['prometheus.retention'].length - 1));
    }
    if ( answers['grafana.persistence.enabled'] ) {
      set(this, 'enableGrafanaPersistence', answers['grafana.persistence.enabled'] === 'true');
    }
    if ( answers['prometheus.persistence.enabled'] ) {
      set(this, 'enablePrometheusPersistence', answers['prometheus.persistence.enabled'] === 'true');
    }
    if ( answers['prometheus.persistence.storageClass'] ) {
      set(this, 'prometheusStorageClass', answers['prometheus.persistence.storageClass'] === 'default' ? null : answers['prometheus.persistence.storageClass']);
    }
    if ( answers['grafana.persistence.storageClass'] ) {
      set(this, 'grafanaStorageClass', answers['grafana.persistence.storageClass'] === 'default' ? null : answers['grafana.persistence.storageClass']);
    }
    if ( answers['grafana.persistence.size'] ) {
      set(this, 'grafanaPersistenceSize', answers['grafana.persistence.size'])
    }
    if ( answers['prometheus.persistence.size'] ) {
      set(this, 'prometheusPersistenceSize', answers['prometheus.persistence.size'])
    }
    if ( get(this, 'level') === 'cluster' ) {
      if ( answers['exporter-node.enabled'] ) {
        set(this, 'enableNodeExporter', answers['exporter-node.enabled'] === 'true');
      }
      if ( answers['exporter-node.ports.metrics.port'] ) {
        set(this, 'port', answers['exporter-node.ports.metrics.port'])
      }
    }
    let nodeSelectorsStr = '';

    Object.keys(answers).filter((key) => key.startsWith('prometheus.nodeSelectors[') ).forEach((key) => {
      nodeSelectorsStr += `${ answers[key] },`;
    });

    set(this, 'nodeSelectorsStr', nodeSelectorsStr);
  },
});
<commit_msg>Set default prometheus retention period to 12 hours<commit_after>import { inject as service } from '@ember/service';
import { on } from '@ember/object/evented';
import Component from '@ember/component';
import { set, get, computed, observer } from '@ember/object';
import { alias } from '@ember/object/computed';
import layout from './template';

const PROMETHEUS = 'prometheus';
const NONE = 'none';

export default Component.extend({
  scope: service(),
  growl: service(),

  layout,

  answers:                     null,
  selected:                    NONE,
  level:                       'cluster',
  justDeployed:                false,
  enablePrometheusPersistence: false,
  enableGrafanaPersistence:    false,
  enableNodeExporter:          true,
  prometheusPersistenceSize:   '50Gi',
  grafanaPersistenceSize:      '10Gi',
  prometheusStorageClass:      null,
  grafanaStorageClass:         null,
  nodeSelectors:               null,
  retention:                   12,
  port:                        9796,

  cluster: alias('scope.currentCluster'),
  project: alias('scope.currentProject'),

  actions: {
    changeSelected(selected) {
      set(this, 'selected', selected);
    },

    save(cb) {
      if ( get(this, 'selected') === NONE ) {
        this.send('disable', cb);
      } else {
        this.send('enable', cb);
      }
    },

    enable(cb) {
      const resource = get(this, 'level') === 'cluster' ? get(this, 'cluster') : get(this, 'project');

      let answers = get(this, 'answers') || {};

      if ( get(this, 'level') === 'cluster' ) {
        answers['exporter-node.enabled'] = `${ get(this, 'enableNodeExporter') }`;
        answers['exporter-node.ports.metrics.port'] = `${ get(this, 'port') }`;
        answers['exporter-kubelets.https'] = `${ !(get(this, 'scope.currentCluster.isGKE') || get(this, 'scope.currentCluster.isAKS')) }`;
      }

      answers['prometheus.retention'] = `${ get(this, 'retention') }h`;
      answers['grafana.persistence.enabled'] = `${ get(this, 'enableGrafanaPersistence') }`;
      answers['prometheus.persistence.enabled'] = `${ get(this, 'enablePrometheusPersistence') }`;
      answers['prometheus.persistence.storageClass'] = `${ get(this, 'prometheusStorageClass') === null ? 'default' : get(this, 'prometheusStorageClass') }`;
      answers['grafana.persistence.storageClass'] = `${ get(this, 'grafanaStorageClass') === null ? 'default' : get(this, 'grafanaStorageClass') }`;
      answers['grafana.persistence.size'] = `${ get(this, 'grafanaPersistenceSize') }`;
      answers['prometheus.persistence.size'] = `${ get(this, 'prometheusPersistenceSize') }`;

      Object.keys(answers).filter((key) => key.startsWith('prometheus.nodeSelectors[') ).forEach((key) => {
        delete answers[key]
      });

      (get(this, 'nodeSelectors') || []).forEach((selector, index) => {
        let s = get(selector, 'key');

        if ( get(selector, 'value') ) {
          s += `=${ get(selector, 'value') }`;
        }
        answers[`prometheus.nodeSelectors[${ index }]`] = s;
      });

      let action = get(this, 'enabled') ?  'editMonitoring' : 'enableMonitoring';

      resource.doAction(action, { answers }).then(() => {
        set(this, 'justDeployed', true);
        cb(true);
        this.fetchSettings();
      }).catch((err) => {
        get(this, 'growl').fromError(err);
        cb(false);
      });
    },

    disable(cb) {
      const resource = get(this, 'level') === 'cluster' ? get(this, 'cluster') : get(this, 'project');

      resource.doAction('disableMonitoring').then(() => {
        cb(true);
        set(this, 'answers', null);
      }).catch((err) => {
        get(this, 'growl').fromError(err);
        cb(false);
      });
    }
  },

  enabled: computed('cluster.enableClusterMonitoring', 'project.enableProjectMonitoring', 'level', function() {
    return get(this, 'level') === 'cluster' ? get(this, 'cluster.enableClusterMonitoring') : get(this, 'project.enableProjectMonitoring');
  }),

  saveDisabled: computed('selected', 'enabled', function() {
    return (get(this, 'selected') === NONE && !get(this, 'enabled'));
  }),

  initSettings: on('init', observer('scope.currentProject.id', 'scope.currentCluster.id', function() {
    set(this, 'selected', get(this, 'enabled') ? PROMETHEUS : NONE);

    if ( get(this, 'enabled') ) {
      this.fetchSettings();
    }
  })),

  fetchSettings() {
    const resource = get(this, 'level') === 'cluster' ? get(this, 'cluster') : get(this, 'project');

    set(this, 'loading', true);
    resource.waitForAction('viewMonitoring').then(() => {
      resource.doAction('viewMonitoring').then((res) => {
        const answers = get(res, 'answers');

        set(this, 'answers', answers);
        this.updateConfig(answers);
        set(this, 'loading', false);
      }).catch((err) => {
        get(this, 'growl').fromError(err);
        set(this, 'loading', false);
      });
    })
  },

  updateConfig(answers) {
    if ( answers['prometheus.retention'] ) {
      set(this, 'retention', answers['prometheus.retention'].substr(0, answers['prometheus.retention'].length - 1));
    }
    if ( answers['grafana.persistence.enabled'] ) {
      set(this, 'enableGrafanaPersistence', answers['grafana.persistence.enabled'] === 'true');
    }
    if ( answers['prometheus.persistence.enabled'] ) {
      set(this, 'enablePrometheusPersistence', answers['prometheus.persistence.enabled'] === 'true');
    }
    if ( answers['prometheus.persistence.storageClass'] ) {
      set(this, 'prometheusStorageClass', answers['prometheus.persistence.storageClass'] === 'default' ? null : answers['prometheus.persistence.storageClass']);
    }
    if ( answers['grafana.persistence.storageClass'] ) {
      set(this, 'grafanaStorageClass', answers['grafana.persistence.storageClass'] === 'default' ? null : answers['grafana.persistence.storageClass']);
    }
    if ( answers['grafana.persistence.size'] ) {
      set(this, 'grafanaPersistenceSize', answers['grafana.persistence.size'])
    }
    if ( answers['prometheus.persistence.size'] ) {
      set(this, 'prometheusPersistenceSize', answers['prometheus.persistence.size'])
    }
    if ( get(this, 'level') === 'cluster' ) {
      if ( answers['exporter-node.enabled'] ) {
        set(this, 'enableNodeExporter', answers['exporter-node.enabled'] === 'true');
      }
      if ( answers['exporter-node.ports.metrics.port'] ) {
        set(this, 'port', answers['exporter-node.ports.metrics.port'])
      }
    }
    let nodeSelectorsStr = '';

    Object.keys(answers).filter((key) => key.startsWith('prometheus.nodeSelectors[') ).forEach((key) => {
      nodeSelectorsStr += `${ answers[key] },`;
    });

    set(this, 'nodeSelectorsStr', nodeSelectorsStr);
  },
});
<|endoftext|>


<commit_before>import Component from '@ember/component';
import { inject as service } from '@ember/service';
import layout from './template';
import { computed } from '@ember/object';
import { formatSi, parseSi, exponentNeeded } from 'shared/utils/parse-unit';

export default Component.extend({
  intl: service(),

  layout,

  nodes: null,

  cpuReservation: computed('nodes.@each.{allocatable,requested}', 'intl.locale', function() {
    return this.getGauge('cpu',
      (u, t) => formatSi(u, 1000, '', '', 0, exponentNeeded(t), 1).replace(/\s.*$/, ''),
      (t) => formatSi(t, 1000, '', '', 0, exponentNeeded(t), 1), 'reserved',
    );
  }),

  memoryReservation: computed('nodes.@each.{allocatable,requested}', 'intl.locale', function() {
    return this.getGauge('memory',
      (u, t) => formatSi(u, 1024, '', '', 0, exponentNeeded(t), 1).replace(/\s.*$/, ''),
      (t) => formatSi(t, 1024, 'iB', 'B', 0, exponentNeeded(t), 1), 'reserved',
    );
  }),

  podUsage: computed('nodes.@each.{allocatable,requested}', 'intl.locale', function() {
    return this.getGauge('pods',
      (u, t) => formatSi(u, 1000, '', '', 0, exponentNeeded(t), 1).replace(/\s.*$/, ''),
      (t) => formatSi(t, 1000, '', '', 0, exponentNeeded(t), 1), 'used',
    );
  }),

  getGauge(field, usedFormatCb, totalFormatCb, keyword) {
    const nodes = this.getNodes(field);
    const value = this.getValue(nodes)

    return {
      percent:  value.percent,
      value:    value.current,
      subtitle: this.getSubtitle(nodes, totalFormatCb, usedFormatCb, keyword),
      ticks:    this.getTicks(nodes)
    };
  },

  getTicks(nodes) {
    let filtered = [];

    if (nodes.length > 0) {
      const min = nodes[0].value;
      const max = nodes[nodes.length - 1].value;

      filtered = nodes.filter((node) => node.value === min || node.value === max);
    }
    const ticks = [];

    filtered.forEach((node) => {
      const found = ticks.find((tick) => tick.value === Math.round(node.value));

      if (found) {
        found.labels.push(node.node.nodeName);
      } else {
        ticks.push({
          value:  Math.round(node.value),
          labels: [node.node.nodeName],
        });
      }
    });

    return ticks;
  },

  getNodes(field) {
    return (this.get('nodes') || []).map((node) => {
      const tValue = node.allocatable && node.allocatable[field] ? node.allocatable[field] : '0';
      const uValue = node.requested && node.requested[field] ? node.requested[field] : '0';

      let total = parseSi(tValue);
      let used = parseSi(uValue);

      if (isNaN(total) || isNaN(used)) {
        used = 0;
        total = 0;
      }

      let value = 0;

      if (total > 0) {
        value = used * 100 / total;
      }

      return {
        node,
        used,
        total,
        value,
      };
    }).sortBy('value');
  },

  getValue(nodes) {
    let used = 0;
    let total = 0;

    nodes.forEach((node) => {
      total += node.total;
      used += node.used;
    });
    const value = Math.round(used * 100 / total);

    return {
      percent: isNaN(value) ? 0 : value,
      current:   used,
    }
  },

  getSubtitle(nodes, totalCb, usedCb, keyword) {
    let used = 0;
    let total = 0;

    nodes.forEach((node) => {
      total += node.total;
      used += node.used;
    });

    return this.get('intl').t(`clusterDashboard.subtitle.${ keyword }`, {
      used:  usedCb ? (usedCb(used, total) || '').trim() : used,
      total: totalCb ? (totalCb(total) || '').trim() : total,
    });
  },
});
<commit_msg>Fix node is not active show "undefined" in dashboard<commit_after>import Component from '@ember/component';
import { inject as service } from '@ember/service';
import layout from './template';
import { computed } from '@ember/object';
import { formatSi, parseSi, exponentNeeded } from 'shared/utils/parse-unit';

export default Component.extend({
  intl: service(),

  layout,

  nodes: null,

  cpuReservation: computed('nodes.@each.{allocatable,requested}', 'intl.locale', function() {
    return this.getGauge('cpu',
      (u, t) => formatSi(u, 1000, '', '', 0, exponentNeeded(t), 1).replace(/\s.*$/, ''),
      (t) => formatSi(t, 1000, '', '', 0, exponentNeeded(t), 1), 'reserved',
    );
  }),

  memoryReservation: computed('nodes.@each.{allocatable,requested}', 'intl.locale', function() {
    return this.getGauge('memory',
      (u, t) => formatSi(u, 1024, '', '', 0, exponentNeeded(t), 1).replace(/\s.*$/, ''),
      (t) => formatSi(t, 1024, 'iB', 'B', 0, exponentNeeded(t), 1), 'reserved',
    );
  }),

  podUsage: computed('nodes.@each.{allocatable,requested}', 'intl.locale', function() {
    return this.getGauge('pods',
      (u, t) => formatSi(u, 1000, '', '', 0, exponentNeeded(t), 1).replace(/\s.*$/, ''),
      (t) => formatSi(t, 1000, '', '', 0, exponentNeeded(t), 1), 'used',
    );
  }),

  getGauge(field, usedFormatCb, totalFormatCb, keyword) {
    const nodes = this.getNodes(field);
    const value = this.getValue(nodes)

    return {
      percent:  value.percent,
      value:    value.current,
      subtitle: this.getSubtitle(nodes, totalFormatCb, usedFormatCb, keyword),
      ticks:    this.getTicks(nodes)
    };
  },

  getTicks(nodes) {
    let filtered = [];

    if (nodes.length > 0) {
      const min = nodes[0].value;
      const max = nodes[nodes.length - 1].value;

      filtered = nodes.filter((node) => node.value === min || node.value === max);
    }
    const ticks = [];

    filtered.forEach((node) => {
      const found = ticks.find((tick) => tick.value === Math.round(node.value));

      if (found) {
        found.labels.push(node.node.nodeName || node.node.requestedHostname);
      } else {
        ticks.push({
          value:  Math.round(node.value),
          labels: [node.node.nodeName || node.node.requestedHostname],
        });
      }
    });

    return ticks;
  },

  getNodes(field) {
    return (this.get('nodes') || []).map((node) => {
      const tValue = node.allocatable && node.allocatable[field] ? node.allocatable[field] : '0';
      const uValue = node.requested && node.requested[field] ? node.requested[field] : '0';

      let total = parseSi(tValue);
      let used = parseSi(uValue);

      if (isNaN(total) || isNaN(used)) {
        used = 0;
        total = 0;
      }

      let value = 0;

      if (total > 0) {
        value = used * 100 / total;
      }

      return {
        node,
        used,
        total,
        value,
      };
    }).sortBy('value');
  },

  getValue(nodes) {
    let used = 0;
    let total = 0;

    nodes.forEach((node) => {
      total += node.total;
      used += node.used;
    });
    const value = Math.round(used * 100 / total);

    return {
      percent: isNaN(value) ? 0 : value,
      current:   used,
    }
  },

  getSubtitle(nodes, totalCb, usedCb, keyword) {
    let used = 0;
    let total = 0;

    nodes.forEach((node) => {
      total += node.total;
      used += node.used;
    });

    return this.get('intl').t(`clusterDashboard.subtitle.${ keyword }`, {
      used:  usedCb ? (usedCb(used, total) || '').trim() : used,
      total: totalCb ? (totalCb(total) || '').trim() : total,
    });
  },
});
<|endoftext|>


<commit_before>// Keep a reference to this window so it does not get collected on Android.
var messageWin;
function ApplicationTabGroup() {
	//create module instance
	var self = Ti.UI.createTabGroup(),
		BaseUIWindow = require('ui/common/BaseUIWindow'),
		ControlsWindow = require('ui/common/ControlsWindow'),
		PhoneWindow = require('ui/common/PhoneWindow'),
		PlatformWindow = require('ui/common/PlatformWindow'),
		MashupsWindow = require('ui/common/MashupsWindow');
		//MessageWindow = require('ui/common/MessageWindow');

	//create app tabs
	var baseUIWin = new BaseUIWindow(L('base_ui_title')),
		controlsWin = new ControlsWindow(L('controls_win_title')),
		phoneWin = new PhoneWindow(L('phone_win_title')),
		platformWin = new PlatformWindow(L('platform_win_title')),
		mashupsWin = new MashupsWindow(L('mashups_win_title'));
		//messageWin = new MessageWindow();

	var baseUITab = Ti.UI.createTab({
		title: L('base_ui_title'),
		icon: '/images/tabs/KS_nav_ui.png',
		window: baseUIWin
	});
	baseUIWin.containingTab = baseUITab;

	// On Tizen/Mobile Web, the tabGroup property must be initialized manually.
	// It serves to remember the tab group control that hosts the window.
	// This is needed for the tab group-related tests to be able to access the
	// main tab group control.
	if(Ti.Platform.osname == 'tizen' || Ti.Platform.osname == 'blackberry') {
	 (baseUIWin.tabGroup = self);
	}

	self.addTab(baseUITab);

	var controlsTab = Ti.UI.createTab({
		title: L('controls_win_title'),
		icon: '/images/tabs/KS_nav_views.png',
		window: controlsWin
	});
	controlsWin.containingTab = controlsTab;
	self.addTab(controlsTab);

	var phoneTab = Ti.UI.createTab({
		title:L('phone_win_title'),
		icon:'/images/tabs/KS_nav_phone.png',
		window:phoneWin
	});
	phoneWin.containingTab = phoneTab;
	self.addTab(phoneTab);

	var platformTab = Ti.UI.createTab({
		title:L('platform_win_title'),
		icon:'/images/tabs/KS_nav_platform.png',
		window:platformWin
	});
	platformWin.containingTab = platformTab;
	self.addTab(platformTab);

	var mashupsTab = Ti.UI.createTab({
		title:L('mashups_win_title'),
		icon:'/images/tabs/KS_nav_mashup.png',
		window:mashupsWin
	});
	mashupsWin.containingTab = mashupsTab;
	self.addTab(mashupsTab);

	self.setActiveTab(1);

	if(Ti.Platform.osname == 'blackberry') {
		messageWin = Ti.BlackBerry.createToast();
	} else {
		// Tabgroup events and message window
		messageWin = Titanium.UI.createWindow({
			height:30,
			width:250,
			bottom:70,
			borderRadius:10,
			touchEnabled:false,
			orientationModes : [
				Titanium.UI.PORTRAIT,
				Titanium.UI.UPSIDE_PORTRAIT,
				Titanium.UI.LANDSCAPE_LEFT,
				Titanium.UI.LANDSCAPE_RIGHT
			]
		});
		if (Ti.Platform.osname === 'iphone') {
			messageWin.orientationModes = [Ti.UI.PORTRAIT]
		}

		var messageView = Titanium.UI.createView({
			id:'messageview',
			height:30,
			width:250,
			borderRadius:10,
			backgroundColor:'#000',
			opacity:0.7,
			touchEnabled:false
		});

		var messageLabel = Titanium.UI.createLabel({
			id:'messagelabel',
			text:'',
			color:'#fff',
			width:250,
			height:'auto',
			font:{
				fontFamily:'Helvetica Neue',
				fontSize:13
			},
			textAlign:'center'
		});
		messageWin.add(messageView);
		messageWin.add(messageLabel);
	}

	self.addEventListener('close', function(e) {
		if (e.source == self){
			if (Ti.Platform.osname === 'iphone' || Ti.Platform.osname === 'ipad') {
				self.open();
			}
		}
	});

	function createAndroidToast(message) {
		var theToast = Ti.UI.createNotification({
			message:message,
    		duration: Ti.UI.NOTIFICATION_DURATION_SHORT
		});
		theToast.show();
	}

	function showMessage(message) {
		if(Ti.Platform.osname == 'backberry') {
			messageWin.cancel();
			messageWin.message = message;
			messageWin.show();
		} else if (Ti.Platform.osname == 'android') {
			createAndroidToast(message);
		} else {
			messageWin.setLabel(message);
			messageWin.open();
			setTimeout(function() {
				messageWin.close({opacity:0,duration:500});
			},1000);
		}
	}

	self.addEventListener('open',function(e) {
		if (e.source == self){
			showMessage('tab group open event');
		}
	});

	self.addEventListener('focus', function(e) {

		// On iOS, the "More..." tab is actually a tab container, not a tab. When it is clicked, e.tab is undefined.
		if (!e.tab) {
			return;
		}

		// iOS fires with source tabGroup. Android with source tab
		if ((e.source == baseUITab) || (e.source == controlsTab) || (e.source == phoneTab) || (e.source == platformTab) || (e.source == mashupsTab) || (e.source == self)) {
			if ((Ti.Platform.osname == 'blackberry')|| (Ti.Platform.osname == 'android') ) {
				var msg = 'tab changed to ' + e.index + ' old index ' + e.previousIndex;
				showMessage(msg);
			} else {
				messageLabel.text = 'tab changed to ' + e.index + ' old index ' + e.previousIndex;
				messageWin.open();

				setTimeout(function() {
					Ti.API.info('tab = ' + e.tab.title + ', prevTab = ' + (e.previousTab ? e.previousTab.title : null));
					messageLabel.text = 'active title ' + e.tab.title + ' old title ' + (e.previousTab ? e.previousTab.title : null);
				}, 1000);

				setTimeout(function() {
					messageWin.close({
						opacity : 0,
						duration : 500
					});
				}, 2000);
			}
		}

	});

	self.addEventListener('blur', function(e) {
		Titanium.API.info('tab blur - new index ' + e.index + ' old index ' + e.previousIndex);
	});
	self.model = Ti.Platform.model;

	return self;
};

module.exports = ApplicationTabGroup;
<commit_msg>[TIMOB-17023]Fixing background color on windows. <commit_after>// Keep a reference to this window so it does not get collected on Android.
var messageWin;
function ApplicationTabGroup() {
	//create module instance
	var self = Ti.UI.createTabGroup(),
		BaseUIWindow = require('ui/common/BaseUIWindow'),
		ControlsWindow = require('ui/common/ControlsWindow'),
		PhoneWindow = require('ui/common/PhoneWindow'),
		PlatformWindow = require('ui/common/PlatformWindow'),
		MashupsWindow = require('ui/common/MashupsWindow');
		//MessageWindow = require('ui/common/MessageWindow');

	//create app tabs
	var baseUIWin = new BaseUIWindow(L('base_ui_title')),
		controlsWin = new ControlsWindow(L('controls_win_title')),
		phoneWin = new PhoneWindow(L('phone_win_title')),
		platformWin = new PlatformWindow(L('platform_win_title')),
		mashupsWin = new MashupsWindow(L('mashups_win_title'));
		//messageWin = new MessageWindow();

	var baseUITab = Ti.UI.createTab({
		title: L('base_ui_title'),
		icon: '/images/tabs/KS_nav_ui.png',
		window: baseUIWin
	});
	baseUIWin.containingTab = baseUITab;

	// On Tizen/Mobile Web, the tabGroup property must be initialized manually.
	// It serves to remember the tab group control that hosts the window.
	// This is needed for the tab group-related tests to be able to access the
	// main tab group control.
	if(Ti.Platform.osname == 'tizen' || Ti.Platform.osname == 'blackberry') {
	 (baseUIWin.tabGroup = self);
	}

	self.addTab(baseUITab);

	var controlsTab = Ti.UI.createTab({
		title: L('controls_win_title'),
		icon: '/images/tabs/KS_nav_views.png',
		window: controlsWin
	});
	controlsWin.containingTab = controlsTab;
	self.addTab(controlsTab);

	var phoneTab = Ti.UI.createTab({
		title:L('phone_win_title'),
		icon:'/images/tabs/KS_nav_phone.png',
		window:phoneWin
	});
	phoneWin.containingTab = phoneTab;
	self.addTab(phoneTab);

	var platformTab = Ti.UI.createTab({
		title:L('platform_win_title'),
		icon:'/images/tabs/KS_nav_platform.png',
		window:platformWin
	});
	platformWin.containingTab = platformTab;
	self.addTab(platformTab);

	var mashupsTab = Ti.UI.createTab({
		title:L('mashups_win_title'),
		icon:'/images/tabs/KS_nav_mashup.png',
		window:mashupsWin
	});
	mashupsWin.containingTab = mashupsTab;
	self.addTab(mashupsTab);

	self.setActiveTab(1);

	if(Ti.Platform.osname == 'blackberry') {
		messageWin = Ti.BlackBerry.createToast();
	} else {
		// Tabgroup events and message window
		messageWin = Titanium.UI.createWindow({
			height:30,
			width:250,
			bottom:70,
			borderRadius:10,
			touchEnabled:false,
			orientationModes : [
				Titanium.UI.PORTRAIT,
				Titanium.UI.UPSIDE_PORTRAIT,
				Titanium.UI.LANDSCAPE_LEFT,
				Titanium.UI.LANDSCAPE_RIGHT
			]
		});
		if (Ti.Platform.osname === 'iphone') {
			messageWin.orientationModes = [Ti.UI.PORTRAIT]
		}

		var messageView = Titanium.UI.createView({
			id:'messageview',
			height:30,
			width:250,
			borderRadius:10,
			backgroundColor:'#000',
			opacity:0.7,
			touchEnabled:false
		});

		var messageLabel = Titanium.UI.createLabel({
			id:'messagelabel',
			text:'',
			color:'#fff',
			width:250,
			height:'auto',
			font:{
				fontFamily:'Helvetica Neue',
				fontSize:13
			},
			textAlign:'center'
		});
		messageWin.add(messageView);
		messageWin.add(messageLabel);
	}

	self.addEventListener('close', function(e) {
		if (e.source == self){
			if (Ti.Platform.osname === 'iphone' || Ti.Platform.osname === 'ipad') {
				self.open();
			}
		}
	});

	function createAndroidToast(message) {
		var theToast = Ti.UI.createNotification({
			message:message,
    		duration: Ti.UI.NOTIFICATION_DURATION_SHORT
		});
		theToast.show();
	}

	function showMessage(message) {
		if(Ti.Platform.osname == 'backberry') {
			messageWin.cancel();
			messageWin.message = message;
			messageWin.show();
		} else if (Ti.Platform.osname == 'android') {
			createAndroidToast(message);
		} else {
			Titanium.UI.setBackgroundColor('#fff');
			messageWin.setLabel(message);
			messageWin.open();
			setTimeout(function() {
				messageWin.close({opacity:0,duration:500});
			},1000);
		}
	}

	self.addEventListener('open',function(e) {
		if (e.source == self){
			showMessage('tab group open event');
		}
	});

	self.addEventListener('focus', function(e) {

		// On iOS, the "More..." tab is actually a tab container, not a tab. When it is clicked, e.tab is undefined.
		if (!e.tab) {
			return;
		}

		// iOS fires with source tabGroup. Android with source tab
		if ((e.source == baseUITab) || (e.source == controlsTab) || (e.source == phoneTab) || (e.source == platformTab) || (e.source == mashupsTab) || (e.source == self)) {
			if ((Ti.Platform.osname == 'blackberry')|| (Ti.Platform.osname == 'android') ) {
				var msg = 'tab changed to ' + e.index + ' old index ' + e.previousIndex;
				showMessage(msg);
			} else {
				messageLabel.text = 'tab changed to ' + e.index + ' old index ' + e.previousIndex;
				messageWin.open();

				setTimeout(function() {
					Ti.API.info('tab = ' + e.tab.title + ', prevTab = ' + (e.previousTab ? e.previousTab.title : null));
					messageLabel.text = 'active title ' + e.tab.title + ' old title ' + (e.previousTab ? e.previousTab.title : null);
				}, 1000);

				setTimeout(function() {
					messageWin.close({
						opacity : 0,
						duration : 500
					});
				}, 2000);
			}
		}

	});

	self.addEventListener('blur', function(e) {
		Titanium.API.info('tab blur - new index ' + e.index + ' old index ' + e.previousIndex);
	});
	self.model = Ti.Platform.model;

	return self;
};

module.exports = ApplicationTabGroup;
<|endoftext|>


<commit_before>var globals = require('lib/globals');

exports.speakerDetailWindow = function(opts) {
  instance = Ti.UI.createWindow({
    title: opts.title,
    backgroundColor: "#313131",
    barColor: '#3b587b'
  });
  var sdata = [];
  var headerRow = Ti.UI.createTableViewRow({
    height: 120,
    borderWidth: 0,
    layout: "vertical",
    borderColor: "#313131",
    selectionStyle: "none"
  });
  // Create the speaker image
  var fname = opts.fname.toLowerCase();
  var lname = opts.lname.replace(" ", "-");
  lname = lname.toLowerCase();
  var imagePath = (opts.img) ? opts.img.replace(/\\/gi, ""): '';
  var speakerImage = Ti.UI.createImageView({
    image: imagePath,
    top:20,
    left:20,
    width:80,
    height:90,
    borderWidth: 5,
    borderColor: "#c6c6c6"
  });
  headerRow.add(speakerImage);
  // Add the name of the speaker
  var speakerTitle = Ti.UI.createLabel({
    text: opts.title,
    top: -65,
    left: 115,
    width: "auto",
    height: "auto",
    color: "#a1b1c4",
    font: {fontWeight: "bold", fontSize: 18, fontFamily: "Georgia"}
  });

  var speakerLocation = Ti.UI.createLabel({
    text: opts.slcLocation,
    top: 0,
    left: 115,
    width: "auto",
    height: "auto",
    color: "#a1b1c4",
    font: {fontSize: 14, fontFamily: "Georgia"}
  });
  headerRow.add(speakerTitle);
  headerRow.add(speakerLocation);
  sdata.push(headerRow);

  // Add a bio is the speaker has one
  var bio = opts.bio;
  if (bio != null) {
    var bioRow = Ti.UI.createTableViewRow({
      height: "auto",
      selectionStyle: "none"
    });
    var bioLabel = Ti.UI.createLabel({
      text: bio,
      top: 0,
      left: 20,
      right: 20,
      height: "auto",
      color: "#7f7f7f"
    });
    bioRow.add(bioLabel);
    sdata.push(bioRow);
  }

  // Now add the sessions if the speaker has any
  var sessions = globals.slcdbGetSessionsSpeaker(opts.title);
  Ti.API.info("Total sessions for "+opts.title+": "+sessions.length);
  if (sessions.length > 0) {
    var sessionHeader = Ti.UI.createTableViewRow({selectionStyle: "none"});
    var sessionHeaderTitle = Ti.UI.createLabel({
      text: "Workshop Sessions",
      font: {fontSize:14, fontFamily: "Georgia", fontWeight: "bold"},
      color: "#a1b1c4",
      left: 20,
      right: 20
    });
    sessionHeader.add(sessionHeaderTitle);
    sdata.push(sessionHeader);
    for  (var i = 0, node; node = sessions[i]; i++) {
      var sessionRow = Ti.UI.createTableViewRow({
        height: "auto",
        layout: "vertical",
        selectionStyle: "none"
      });
      var sessionTitle = Ti.UI.createLabel({
        text: globals.html_decode(node.title),
        top: 0,
        left: 20,
        font: {fontSize:14},
        width: 275,
        height: "auto",
        color: "#b0b0b0"
      });
      var dayofweek = globals.DayofWeek(node.day);
      var sessionTime = globals.secondsToTime(node.datefrom);
      var sessionRoom = (node.room != null) ? " | " + node.room : '';
      var sessionExtra = Ti.UI.createLabel({
        text: dayofweek + " | " + sessionTime + sessionRoom,
        width: "auto",
        height: "auto",
        font: {fontSize: 12},
        color: "#7c7c7c",
        top: 0,
        left: 20
      });

      sessionRow.add(sessionTitle);
      sessionRow.add(sessionExtra);
      sdata.push(sessionRow);

      var paddingRow = Ti.UI.createTableViewRow({
        height: 10,
        selectionStyle: "none"
      });
      sdata.push(paddingRow);
    }
  }

  var speakerTable = Ti.UI.createTableView({
    data: sdata,
    backgroundColor: "#313131",
    separatorColor: "#313131"
  });
  instance.add(speakerTable);

  return instance;
};
<commit_msg>fix for truncated session title on speakerDetailWindow<commit_after>var globals = require('lib/globals');

exports.speakerDetailWindow = function(opts) {
  instance = Ti.UI.createWindow({
    title: opts.title,
    backgroundColor: "#313131",
    barColor: '#3b587b'
  });
  var sdata = [];
  var headerRow = Ti.UI.createTableViewRow({
    height: 120,
    borderWidth: 0,
    layout: "vertical",
    borderColor: "#313131",
    selectionStyle: "none"
  });
  // Create the speaker image
  var fname = opts.fname.toLowerCase();
  var lname = opts.lname.replace(" ", "-");
  lname = lname.toLowerCase();
  var imagePath = (opts.img) ? opts.img.replace(/\\/gi, ""): '';
  var speakerImage = Ti.UI.createImageView({
    image: imagePath,
    top:20,
    left:20,
    width:80,
    height:90,
    borderWidth: 5,
    borderColor: "#c6c6c6"
  });
  headerRow.add(speakerImage);
  // Add the name of the speaker
  var speakerTitle = Ti.UI.createLabel({
    text: opts.title,
    top: -65,
    left: 115,
    width: "auto",
    height: "auto",
    color: "#a1b1c4",
    font: {fontWeight: "bold", fontSize: 18, fontFamily: "Georgia"}
  });

  var speakerLocation = Ti.UI.createLabel({
    text: opts.slcLocation,
    top: 0,
    left: 115,
    width: "auto",
    height: "auto",
    color: "#a1b1c4",
    font: {fontSize: 14, fontFamily: "Georgia"}
  });
  headerRow.add(speakerTitle);
  headerRow.add(speakerLocation);
  sdata.push(headerRow);

  // Add a bio is the speaker has one
  var bio = opts.bio;
  if (bio != null) {
    var bioRow = Ti.UI.createTableViewRow({
      height: "auto",
      selectionStyle: "none"
    });
    var bioLabel = Ti.UI.createLabel({
      text: bio,
      top: 0,
      left: 20,
      right: 20,
      height: "auto",
      color: "#7f7f7f"
    });
    bioRow.add(bioLabel);
    sdata.push(bioRow);
  }

  // Now add the sessions if the speaker has any
  var sessions = globals.slcdbGetSessionsSpeaker(opts.title);
  Ti.API.info("Total sessions for "+opts.title+": "+sessions.length);
  if (sessions.length > 0) {
    var sessionHeader = Ti.UI.createTableViewRow({selectionStyle: "none"});
    var sessionHeaderTitle = Ti.UI.createLabel({
      text: "Workshop Sessions",
      font: {fontSize:14, fontFamily: "Georgia", fontWeight: "bold"},
      color: "#a1b1c4",
      left: 20,
      right: 20
    });
    sessionHeader.add(sessionHeaderTitle);
    sdata.push(sessionHeader);
    for  (var i = 0, node; node = sessions[i]; i++) {
      var sessionRow = Ti.UI.createTableViewRow({
        height: "auto",
        layout: "vertical",
        selectionStyle: "none"
      });
      var sessionTitle = Ti.UI.createLabel({
        text: globals.html_decode(node.title),
        top: 0,
        left: 20,
        font: {fontSize:14},
        width: "auto",
        height: "auto",
        color: "#b0b0b0"
      });
      var dayofweek = globals.DayofWeek(node.day);
      var sessionTime = globals.secondsToTime(node.datefrom);
      var sessionRoom = (node.room != null) ? " | " + node.room : '';
      var sessionExtra = Ti.UI.createLabel({
        text: dayofweek + " | " + sessionTime + sessionRoom,
        width: "auto",
        height: "auto",
        font: {fontSize: 12},
        color: "#7c7c7c",
        top: 0,
        left: 20
      });

      sessionRow.add(sessionTitle);
      sessionRow.add(sessionExtra);
      sdata.push(sessionRow);

      var paddingRow = Ti.UI.createTableViewRow({
        height: 10,
        selectionStyle: "none"
      });
      sdata.push(paddingRow);
    }
  }

  var speakerTable = Ti.UI.createTableView({
    data: sdata,
    backgroundColor: "#313131",
    separatorColor: "#313131"
  });
  instance.add(speakerTable);

  return instance;
};
<|endoftext|>