<commit_before>import Declaration from "./declaration"
import * as PHPType from "../php-type"
import Method from "./method"
import Property from "./property"
import ClassConstant from "./classconstant"
import Identifier from "./identifier"
import TraitUse from "./traituse"
import {Context, ContextTypes, Doc, ParserStateOption} from "./node"
import * as PHPError from "../php-error"
export default class Class extends Declaration {
    /**
     * @type {Declaration[]}
     */
    get body() {
        return this.cacheNodeArray("body")
    }
    /**
     * @type {?Identifier}
     */
    get extends() {
        return this.cacheNode("extends")
    }
    /**
     * @type {Identifier[]}
     */
    get implements() {
        return this.cacheNode("implements")
    }
    /**
     * @type {boolean}
     */
    get isAbstract() {
        return this.node.isAbstract
    }
    /**
     * @type {boolean}
     */
    get isAnonymous() {
        return this.node.isAnonymous
    }
    /**
     * @type {boolean}
     */
    get isFinal() {
        return this.node.isFinal
    }

    /**
     * Checks that syntax seems ok
     * @param {Context} context
     * @param {Set<ParserStateOption.Base>} [parser_state]
     * @param {?Doc} [doc]
     * @returns {?ContextTypes} The set of types applicable to this value
     */
    check(context, parser_state = new Set(), doc = null) {
        super.check(context, parser_state, doc)
        if(!this.name.match(/^([0-9A-Z]+[0-9a-z]*)+$/)) {
            // This does allow names like UPSPowerState
            this.throw(new PHPError.PSR1.S3ClassCase(), context)
        }
        if(!doc) {
            this.throw(new PHPError.NoDoc(), context)
        }
        let inner_context = context.childContext()
        let extended_class
        if(this.extends) {
            let extended_class_name = context.resolveNodeName(this.extends)
            try {
                extended_class = context.findClass(extended_class_name)
            } catch(e) {
                this.handleException(e, context)
            }
        }
        inner_context.classContext = inner_context.globalContext.addClass(
            context.resolveNodeName(this),
            extended_class,
            context.fileContext
        )
        inner_context.setThis()

        /** @type {?Doc} */
        let last_doc = null
        this.body.forEach(
            b => {
                let doc = last_doc
                if(b instanceof Doc) {
                    last_doc = b
                } else if(b instanceof Method) {
                    inner_context.classContext.addTemporaryIdentifier(
                        b.name,
                        b.visibility,
                        b.isStatic,
                        () => b.check(inner_context, new Set(), doc).expressionType
                    )
                } else if(b instanceof Property) {
                    inner_context.classContext.addTemporaryIdentifier(
                        b.name,
                        b.visibility,
                        b.isStatic,
                        () => b.check(inner_context, new Set(), doc).expressionType
                    )
                } else if(b instanceof ClassConstant) {
                    inner_context.classContext.addTemporaryIdentifier(
                        b.name,
                        "public",
                        true,
                        () => b.check(inner_context, new Set(), doc).expressionType
                    )
                } else if(b instanceof TraitUse) {
                    // Do nothing - loaded shortly
                } else {
                    b.check(inner_context, new Set(), last_doc)
                    last_doc = null
                }
            }
        )

        last_doc = null
        this.body.forEach(
            b => {
                if(b instanceof Doc) {
                    last_doc = b
                } else {
                    if(b instanceof TraitUse) {
                        b.check(inner_context, new Set(), last_doc)
                    }
                    last_doc = null
                }
            }
        )
        Object.keys(inner_context.classContext.temporaryIdentifiers).forEach(name => {
            let ti = inner_context.classContext.temporaryIdentifiers[name]
            if(ti && !ti.compileStarted) {
                ti.compileStarted = true
                ti.compile()
                delete inner_context.classContext.temporaryIdentifiers[name]
            }
        })
        return ContextTypes.empty
    }
}
<commit_msg>Split class evaluation so that it can be deferred<commit_after>import Declaration from "./declaration"
import * as PHPType from "../php-type"
import Method from "./method"
import Property from "./property"
import ClassConstant from "./classconstant"
import Identifier from "./identifier"
import TraitUse from "./traituse"
import {Context, ContextTypes, Doc, ParserStateOption} from "./node"
import * as PHPError from "../php-error"
export default class Class extends Declaration {
    /**
     * @type {Declaration[]}
     */
    get body() {
        return this.cacheNodeArray("body")
    }
    /**
     * @type {?Identifier}
     */
    get extends() {
        return this.cacheNode("extends")
    }
    /**
     * @type {Identifier[]}
     */
    get implements() {
        return this.cacheNode("implements")
    }
    /**
     * @type {boolean}
     */
    get isAbstract() {
        return this.node.isAbstract
    }
    /**
     * @type {boolean}
     */
    get isAnonymous() {
        return this.node.isAnonymous
    }
    /**
     * @type {boolean}
     */
    get isFinal() {
        return this.node.isFinal
    }

    /**
     * Checks that syntax seems ok
     * @param {Context} context
     * @param {Set<ParserStateOption.Base>} [parser_state]
     * @param {?Doc} [doc]
     * @returns {?ContextTypes} The set of types applicable to this value
     */
    check(context, parser_state = new Set(), doc = null) {
        super.check(context, parser_state, doc)
        if(!this.name.match(/^([0-9A-Z]+[0-9a-z]*)+$/)) {
            // This does allow names like UPSPowerState
            this.throw(new PHPError.PSR1.S3ClassCase(), context)
        }
        if(!doc) {
            this.throw(new PHPError.NoDoc(), context)
        }
        let inner_context = context.childContext()
        let extended_class
        if(this.extends) {
            let extended_class_name = context.resolveNodeName(this.extends)
            try {
                extended_class = context.findClass(extended_class_name)
            } catch(e) {
                this.handleException(e, context)
            }
        }
        inner_context.classContext = inner_context.globalContext.addClass(
            context.resolveNodeName(this),
            extended_class,
            context.fileContext
        )
        inner_context.setThis()
        this.checkInner(inner_context, parser_state, doc)
        return ContextTypes.empty
    }

    /**
     * Checks that the syntax of the inner parts seem ok
     *
     * @param {Context} context
     * @param {Set<ParserStateOption.Base>} [parser_state]
     * @param {?Doc} [doc]
     * @returns {void}
     */
    checkInner(context, parser_state = new Set(), doc = null) {
        /** @type {?Doc} */
        let last_doc = null
        this.body.forEach(
            b => {
                let doc = last_doc
                if(b instanceof Doc) {
                    last_doc = b
                } else if(b instanceof Method) {
                    context.classContext.addTemporaryIdentifier(
                        b.name,
                        b.visibility,
                        b.isStatic,
                        () => b.check(context, new Set(), doc).expressionType
                    )
                } else if(b instanceof Property) {
                    context.classContext.addTemporaryIdentifier(
                        b.name,
                        b.visibility,
                        b.isStatic,
                        () => b.check(context, new Set(), doc).expressionType
                    )
                } else if(b instanceof ClassConstant) {
                    context.classContext.addTemporaryIdentifier(
                        b.name,
                        "public",
                        true,
                        () => b.check(context, new Set(), doc).expressionType
                    )
                } else if(b instanceof TraitUse) {
                    // Do nothing - loaded shortly
                } else {
                    b.check(context, new Set(), last_doc)
                    last_doc = null
                }
            }
        )

        last_doc = null
        this.body.forEach(
            b => {
                if(b instanceof Doc) {
                    last_doc = b
                } else {
                    if(b instanceof TraitUse) {
                        b.check(context, new Set(), last_doc)
                    }
                    last_doc = null
                }
            }
        )
        Object.keys(context.classContext.temporaryIdentifiers).forEach(name => {
            let ti = context.classContext.temporaryIdentifiers[name]
            if(ti && !ti.compileStarted) {
                ti.compileStarted = true
                ti.compile()
                delete context.classContext.temporaryIdentifiers[name]
            }
        })
    }
}
<|endoftext|>


<commit_before>const { mergeDefault } = require('../util/util');
const quotes = ['"', "'", '“”', '‘’'];

/**
 * A class to handle argument collection and parameter resolution
 */
class TextPrompt {

	/**
	 * @typedef {Object} TextPromptOptions
	 * @property {number} [promptLimit=Infinity] The number of re-prompts before this TextPrompt gives up
	 * @property {number} [promptTime=30000] The time-limit for re-prompting
	 * @property {boolean} [quotedStringSupport=false] Whether this prompt should respect quoted strings
	 * @memberof TextPrompt
	 */

	/**
	 * @since 0.5.0
	 * @param {KlasaMessage} msg The message this prompt is for
	 * @param {ParsedUsage} usage The usage for this prompt
	 * @param {TextPromptOptions} options The options of this prompt
	 */
	constructor(msg, usage, options) {
		options = mergeDefault(msg.client.options.customPromptDefaults, options);

		/**
		 * The client this TextPrompt was created with
		 * @since 0.5.0
		 * @name TextPrompt#client
		 * @type {KlasaClient}
		 * @readonly
		 */
		Object.defineProperty(this, 'client', { value: msg.client });

		/**
		 * The message this prompt is for
		 * @since 0.5.0
		 * @type {KlasaMessage}
		 */
		this.message = msg;

		/**
		 * The usage for this prompt
		 * @since 0.5.0
		 * @type {ParsedUsage|CommandUsage}
		 */
		this.usage = usage;

		/**
		 * If the command reprompted for missing args
		 * @since 0.0.1
		 * @type {boolean}
		 */
		this.reprompted = false;

		/**
		 * The flag arguments resolved by this class
		 * @since 0.5.0
		 * @type {Object}
		 */
		this.flags = {};

		/**
		 * The string arguments derived from the usageDelim of the command
		 * @since 0.0.1
		 * @type {string[]}
		 */
		this.args = [];

		/**
		 * The parameters resolved by this class
		 * @since 0.0.1
		 * @type {any[]}
		 */
		this.params = [];

		/**
		 * The time-limit for re-prompting
		 * @since 0.5.0
		 * @type {number}
		 */
		this.promptTime = options.promptTime;

		/**
		 * The number of re-prompts before this TextPrompt gives up
		 * @since 0.5.0
		 * @type {number}
		 */
		this.promptLimit = options.promptLimit;

		/**
		 * Whether this prompt should respect quoted strings
		 * @since 0.5.0
		 * @type {boolean}
		 */
		this.quotedStringSupport = options.quotedStringSupport;

		/**
		 * Whether the current usage is a repeating arg
		 * @since 0.0.1
		 * @type {boolean}
		 * @private
		 */
		this._repeat = false;

		/**
		 * Whether the current usage is required
		 * @since 0.0.1
		 * @type {number}
		 * @private
		 */
		this._required = 0;

		/**
		 * How many time this class has reprompted
		 * @since 0.0.1
		 * @type {boolean}
		 * @private
		 */
		this._prompted = 0;

		/**
		 * A cache of the current usage while validating
		 * @since 0.0.1
		 * @type {Tag}
		 * @private
		 */
		this._currentUsage = {};
	}

	/**
	 * Runs the custom prompt.
	 * @since 0.5.0
	 * @param {string} prompt The message to initially prompt with
	 * @returns {Promise<any[]>} The parameters resolved
	 */
	async run(prompt) {
		const message = await this.message.prompt(prompt, this.promptTime);
		this._setup(message.content);
		return this.validateArgs();
	}

	/**
	 * Collects missing required arguments.
	 * @since 0.5.0
	 * @param {string} prompt The reprompt error
	 * @returns {Promise<any[]>}
	 * @private
	 */
	async reprompt(prompt) {
		this._prompted++;
		if (this.typing) this.message.channel.stopTyping();
		const message = await this.message.prompt(
			this.message.language.get('MONITOR_COMMAND_HANDLER_REPROMPT', `<@!${this.message.author.id}>`, prompt, this.promptTime / 1000),
			this.promptTime
		);

		if (message.content.toLowerCase() === 'abort') throw this.message.language.get('MONITOR_COMMAND_HANDLER_ABORTED');

		if (this.typing) this.message.channel.startTyping();
		this.args[this.args.lastIndexOf(null)] = message.content;
		this.reprompted = true;

		if (this.usage.parsedUsage[this.params.length + 1] && this.usage.parsedUsage[this.params.length + 1].type === 'repeat') {
			return this.repeatingPrompt(prompt);
		}

		return this.validateArgs();
	}

	/**
	 * Collects repeating arguments.
	 * @since 0.5.0
	 * @returns {Promise<any[]>}
	 * @private
	 */
	async repeatingPrompt() {
		if (this.typing) this.message.channel.stopTyping();
		let message;

		try {
			message = await this.message.prompt(
				this.message.language.get('MONITOR_COMMAND_HANDLER_REPEATING_REPROMPT', `<@!${this.message.author.id}>`, this._currentUsage.possibles[0].name, this.promptTime / 1000),
				this.promptTime
			);
		} catch (err) {
			return this.validateArgs();
		}

		if (message.content.toLowerCase() === 'cancel') return this.validateArgs();

		if (this.typing) this.message.channel.startTyping();
		this.args.push(message.content);
		this.reprompted = true;

		return this.repeatingPrompt();
	}

	/**
	 * Validates and resolves args into parameters
	 * @since 0.0.1
	 * @returns {Promise<any[]>} The resolved parameters
	 * @private
	 */
	async validateArgs() {
		if (this.params.length >= this.usage.parsedUsage.length && this.params.length >= this.args.length) {
			return this.finalize();
		} else if (this.usage.parsedUsage[this.params.length]) {
			this._currentUsage = this.usage.parsedUsage[this.params.length];
			this._required = this._currentUsage.required;
		} else if (this._currentUsage.repeat) {
			this._required = 0;
			this._repeat = true;
		} else {
			return this.finalize();
		}

		if (this._currentUsage.possibles.length !== 1) return this.multiPossibles(0);

		const possible = this._currentUsage.possibles[0];
		const custom = this.usage.customResolvers[possible.type];

		if (possible.name in this.flags) this.args.splice(this.params.length, 0, this.flags[possible.name]);
		if (!this.client.argResolver[possible.type] && !custom) {
			this.client.emit('warn', `Unknown Argument Type encountered: ${possible.type}`);
			return this.pushParam(undefined);
		}

		try {
			const res = await this.client.argResolver[custom ? 'custom' : possible.type](this.args[this.params.length], possible, this.message, custom);
			return this.pushParam(res);
		} catch (err) {
			if (!this._required) {
				this.args.splice(this.params.length, 0, undefined);
				return this.pushParam(undefined);
			}
			const { response } = this._currentUsage;
			const error = typeof response === 'function' ? response(this.message, possible) : response;
			if (this._required === 1) return this.handleError(response || err);
			return this.handleError(error || (this.args[this.params.length] === undefined ?
				this.message.language.get('COMMANDMESSAGE_MISSING_REQUIRED', possible.name) :
				err)
			);
		}
	}

	/**
	 * Validates and resolves args into parameters, when multiple types of usage is defined
	 * @since 0.0.1
	 * @param {number} index The id of the possible usage currently being checked
	 * @returns {Promise<any[]>} The resolved parameters
	 * @private
	 */
	async multiPossibles(index) {
		if (index >= this._currentUsage.possibles.length) {
			if (!this._required) {
				this.args.splice(this.params.length, 0, undefined);
				return this.pushParam(undefined);
			}
			const { response } = this._currentUsage;
			if (response) {
				const error = typeof response === 'function' ? response(this.message, possible) : response;
				return this.handleError(error);
			}
			return this.handleError(this.message.language.get('COMMANDMESSAGE_NOMATCH', this._currentUsage.possibles.map(poss => poss.name).join(', ')));
		}

		const possible = this._currentUsage.possibles[index];
		const custom = this.usage.customResolvers[possible.type];

		if (possible.name in this.flags) this.args.splice(this.params.length, 0, this.flags[possible.name]);
		if (!this.client.argResolver[possible.type] && !custom) {
			this.client.emit('warn', `Unknown Argument Type encountered: ${possible.type}`);
			return this.multiPossibles(++index);
		}

		try {
			const res = await this.client.argResolver[custom ? 'custom' : possible.type](this.args[this.params.length], possible, this.message, custom);
			return this.pushParam(res);
		} catch (err) {
			return this.multiPossibles(++index);
		}
	}

	/**
	 * Pushes a parameter into this.params, and resets the re-prompt count.
	 * @since 0.5.0
	 * @param {any} param The resolved parameter
	 * @returns {any[]}
	 * @private
	 */
	pushParam(param) {
		this._prompted = 0;
		this.params.push(param);
		return this.validateArgs();
	}

	/**
	 * Decides if the prompter should reprompt or throw the error found while validating.
	 * @since 0.5.0
	 * @param {string} err The error found while validating
	 * @returns {Promise<any[]>}
	 * @private
	 */
	async handleError(err) {
		this.args.splice(this.params.length, 1, null);
		if (this.promptLimit && this._prompted < this.promptLimit) return this.reprompt(err);
		throw err;
	}

	/**
	 * Finalizes parameters and arguments for this prompt.
	 * @since 0.5.0
	 * @returns {any[]}
	 * @private
	 */
	finalize() {
		for (let i = this.params.length - 1; i >= 0 && this.params[i] === undefined; i--) this.params.pop();
		for (let i = this.args.length - 1; i >= 0 && this.args[i] === undefined; i--) this.args.pop();
		return this.params;
	}

	/**
	 * Splits the original message string into arguments.
	 * @since 0.5.0
	 * @param {string} original The original message string
	 * @returns {void}
	 * @private
	 */
	_setup(original) {
		const { content, flags } = this.constructor.getFlags(original, this.usage.usageDelim);
		this.flags = flags;
		this.args = this.quotedStringSupport ?
			this.constructor.getQuotedStringArgs(content, this.usage.usageDelim).map(arg => arg.trim()) :
			this.constructor.getArgs(content, this.usage.usageDelim).map(arg => arg.trim());
	}

	/**
	 * Parses a message into string args
	 * @since 0.5.0
	 * @param {string} content The remaining content
	 * @param {string} delim The delimiter
	 * @returns {Object}
	 * @private
	 */
	static getFlags(content, delim) {
		const flags = {};
		content = content.replace(TextPrompt.flagRegex, (match, fl, ...quote) => {
			flags[fl] = (quote.slice(0, -2).find(el => el) || fl).replace(/\\/g, '');
			return '';
		}).replace(new RegExp(`(?:(\\s)\\s+|(${delim})(?:${delim})+)`, 'g'), '$1').trim();
		return { content, flags };
	}

	/**
	 * Parses a message into string args
	 * @since 0.0.1
	 * @param {string} content The remaining content
	 * @param {string} delim The delimiter
	 * @returns {string[]}
	 * @private
	 */
	static getArgs(content, delim) {
		const args = content.replace(new RegExp(`(${delim})(?:${delim})+`, 'g'), '$1').split(delim !== '' ? delim : undefined);
		return args.length === 1 && args[0] === '' ? [] : args;
	}

	/**
	 * Parses a message into string args taking into account quoted strings
	 * @since 0.0.1
	 * @param {string} content The remaining content
	 * @param {string} delim The delimiter
	 * @returns {string[]}
	 * @private
	 */
	static getQuotedStringArgs(content, delim) {
		if (!delim || delim === '') return [content];

		const args = [];

		for (let i = 0; i < content.length; i++) {
			let current = '';
			if (content.slice(i, i + delim.length) === delim) {
				i += delim.length - 1;
				continue;
			}
			const quote = quotes.find(qt => qt.includes(content[i]));
			if (quote) {
				const qts = quote.split('');
				while (i + 1 < content.length && (content[i] === '\\' || !qts.includes(content[i + 1]))) current += content[++i] !== '\\' ? content[i] : '';
				i++;
				args.push(current);
			} else {
				current += content[i];
				while (i + 1 < content.length && content.slice(i + 1, i + delim.length + 1) !== delim) current += content[++i];
				args.push(current);
			}
		}

		return args.length === 1 && args[0] === '' ? [] : args;
	}

}

/**
 * Regular Expression to match flags with quoted string support.
 * @since 0.5.0
 * @type {RegExp}
 * @static
 */
TextPrompt.flagRegex = new RegExp(`--([\\w-]{2,})(?:=(?:${quotes.map(qu => `[${qu}]((?:[^${qu}\\\\]|\\\\.)*)[${qu}]`).join('|')}|([\\w-]+)))?`, 'g');

module.exports = TextPrompt;
<commit_msg>plugin union args into semi required<commit_after>const { mergeDefault } = require('../util/util');
const quotes = ['"', "'", '“”', '‘’'];

/**
 * A class to handle argument collection and parameter resolution
 */
class TextPrompt {

	/**
	 * @typedef {Object} TextPromptOptions
	 * @property {number} [promptLimit=Infinity] The number of re-prompts before this TextPrompt gives up
	 * @property {number} [promptTime=30000] The time-limit for re-prompting
	 * @property {boolean} [quotedStringSupport=false] Whether this prompt should respect quoted strings
	 * @memberof TextPrompt
	 */

	/**
	 * @since 0.5.0
	 * @param {KlasaMessage} msg The message this prompt is for
	 * @param {ParsedUsage} usage The usage for this prompt
	 * @param {TextPromptOptions} options The options of this prompt
	 */
	constructor(msg, usage, options) {
		options = mergeDefault(msg.client.options.customPromptDefaults, options);

		/**
		 * The client this TextPrompt was created with
		 * @since 0.5.0
		 * @name TextPrompt#client
		 * @type {KlasaClient}
		 * @readonly
		 */
		Object.defineProperty(this, 'client', { value: msg.client });

		/**
		 * The message this prompt is for
		 * @since 0.5.0
		 * @type {KlasaMessage}
		 */
		this.message = msg;

		/**
		 * The usage for this prompt
		 * @since 0.5.0
		 * @type {ParsedUsage|CommandUsage}
		 */
		this.usage = usage;

		/**
		 * If the command reprompted for missing args
		 * @since 0.0.1
		 * @type {boolean}
		 */
		this.reprompted = false;

		/**
		 * The flag arguments resolved by this class
		 * @since 0.5.0
		 * @type {Object}
		 */
		this.flags = {};

		/**
		 * The string arguments derived from the usageDelim of the command
		 * @since 0.0.1
		 * @type {string[]}
		 */
		this.args = [];

		/**
		 * The parameters resolved by this class
		 * @since 0.0.1
		 * @type {any[]}
		 */
		this.params = [];

		/**
		 * The time-limit for re-prompting
		 * @since 0.5.0
		 * @type {number}
		 */
		this.promptTime = options.promptTime;

		/**
		 * The number of re-prompts before this TextPrompt gives up
		 * @since 0.5.0
		 * @type {number}
		 */
		this.promptLimit = options.promptLimit;

		/**
		 * Whether this prompt should respect quoted strings
		 * @since 0.5.0
		 * @type {boolean}
		 */
		this.quotedStringSupport = options.quotedStringSupport;

		/**
		 * Whether the current usage is a repeating arg
		 * @since 0.0.1
		 * @type {boolean}
		 * @private
		 */
		this._repeat = false;

		/**
		 * Whether the current usage is required
		 * @since 0.0.1
		 * @type {number}
		 * @private
		 */
		this._required = 0;

		/**
		 * How many time this class has reprompted
		 * @since 0.0.1
		 * @type {boolean}
		 * @private
		 */
		this._prompted = 0;

		/**
		 * A cache of the current usage while validating
		 * @since 0.0.1
		 * @type {Tag}
		 * @private
		 */
		this._currentUsage = {};
	}

	/**
	 * Runs the custom prompt.
	 * @since 0.5.0
	 * @param {string} prompt The message to initially prompt with
	 * @returns {Promise<any[]>} The parameters resolved
	 */
	async run(prompt) {
		const message = await this.message.prompt(prompt, this.promptTime);
		this._setup(message.content);
		return this.validateArgs();
	}

	/**
	 * Collects missing required arguments.
	 * @since 0.5.0
	 * @param {string} prompt The reprompt error
	 * @returns {Promise<any[]>}
	 * @private
	 */
	async reprompt(prompt) {
		this._prompted++;
		if (this.typing) this.message.channel.stopTyping();
		const message = await this.message.prompt(
			this.message.language.get('MONITOR_COMMAND_HANDLER_REPROMPT', `<@!${this.message.author.id}>`, prompt, this.promptTime / 1000),
			this.promptTime
		);

		if (message.content.toLowerCase() === 'abort') throw this.message.language.get('MONITOR_COMMAND_HANDLER_ABORTED');

		if (this.typing) this.message.channel.startTyping();
		this.args[this.args.lastIndexOf(null)] = message.content;
		this.reprompted = true;

		if (this.usage.parsedUsage[this.params.length + 1] && this.usage.parsedUsage[this.params.length + 1].type === 'repeat') {
			return this.repeatingPrompt(prompt);
		}

		return this.validateArgs();
	}

	/**
	 * Collects repeating arguments.
	 * @since 0.5.0
	 * @returns {Promise<any[]>}
	 * @private
	 */
	async repeatingPrompt() {
		if (this.typing) this.message.channel.stopTyping();
		let message;

		try {
			message = await this.message.prompt(
				this.message.language.get('MONITOR_COMMAND_HANDLER_REPEATING_REPROMPT', `<@!${this.message.author.id}>`, this._currentUsage.possibles[0].name, this.promptTime / 1000),
				this.promptTime
			);
		} catch (err) {
			return this.validateArgs();
		}

		if (message.content.toLowerCase() === 'cancel') return this.validateArgs();

		if (this.typing) this.message.channel.startTyping();
		this.args.push(message.content);
		this.reprompted = true;

		return this.repeatingPrompt();
	}

	/**
	 * Validates and resolves args into parameters
	 * @since 0.0.1
	 * @returns {Promise<any[]>} The resolved parameters
	 * @private
	 */
	async validateArgs() {
		if (this.params.length >= this.usage.parsedUsage.length && this.params.length >= this.args.length) {
			return this.finalize();
		} else if (this.usage.parsedUsage[this.params.length]) {
			this._currentUsage = this.usage.parsedUsage[this.params.length];
			this._required = this._currentUsage.required;
		} else if (this._currentUsage.repeat) {
			this._required = 0;
			this._repeat = true;
		} else {
			return this.finalize();
		}

		if (this._currentUsage.possibles.length !== 1) return this.multiPossibles(0);

		const possible = this._currentUsage.possibles[0];
		const custom = this.usage.customResolvers[possible.type];

		if (possible.name in this.flags) this.args.splice(this.params.length, 0, this.flags[possible.name]);
		if (!this.client.argResolver[possible.type] && !custom) {
			this.client.emit('warn', `Unknown Argument Type encountered: ${possible.type}`);
			return this.pushParam(undefined);
		}

		try {
			const res = await this.client.argResolver[custom ? 'custom' : possible.type](this.args[this.params.length], possible, this.message, custom);
			return this.pushParam(res);
		} catch (err) {
			if (!this._required) {
				this.args.splice(this.params.length, 0, undefined);
				return this.pushParam(undefined);
			}
			const { response } = this._currentUsage;
			const error = typeof response === 'function' ? response(this.message, possible) : response;
			if (this._required === 1) return this.handleError(response || err);
			return this.handleError(error || (this.args[this.params.length] === undefined ?
				this.message.language.get('COMMANDMESSAGE_MISSING_REQUIRED', possible.name) :
				err)
			);
		}
	}

	/**
	 * Validates and resolves args into parameters, when multiple types of usage is defined
	 * @since 0.0.1
	 * @param {number} index The id of the possible usage currently being checked
	 * @returns {Promise<any[]>} The resolved parameters
	 * @private
	 */
	async multiPossibles(index) {
		if (index >= this._currentUsage.possibles.length) {
			if (!this._required) {
				this.args.splice(this.params.length, 0, undefined);
				return this.pushParam(undefined);
			}
			const { response } = this._currentUsage;
			if (response) {
				const error = typeof response === 'function' ? response(this.message, possible) : response;
				return this.handleError(error);
			}
			return this.handleError(this.message.language.get('COMMANDMESSAGE_NOMATCH', this._currentUsage.possibles.map(poss => poss.name).join(', ')));
		}

		const possible = this._currentUsage.possibles[index];
		const custom = this.usage.customResolvers[possible.type];

		if (possible.name in this.flags) this.args.splice(this.params.length, 0, this.flags[possible.name]);
		if (!this.client.argResolver[possible.type] && !custom) {
			this.client.emit('warn', `Unknown Argument Type encountered: ${possible.type}`);
			return this.multiPossibles(++index);
		}

		try {
			const res = await this.client.argResolver[custom ? 'custom' : possible.type](this.args[this.params.length], possible, this.message, custom);
			return this.pushParam(res);
		} catch (err) {
			if (this._required === 1) return this.handleError(err);
			return this.multiPossibles(++index);
		}
	}

	/**
	 * Pushes a parameter into this.params, and resets the re-prompt count.
	 * @since 0.5.0
	 * @param {any} param The resolved parameter
	 * @returns {any[]}
	 * @private
	 */
	pushParam(param) {
		this._prompted = 0;
		this.params.push(param);
		return this.validateArgs();
	}

	/**
	 * Decides if the prompter should reprompt or throw the error found while validating.
	 * @since 0.5.0
	 * @param {string} err The error found while validating
	 * @returns {Promise<any[]>}
	 * @private
	 */
	async handleError(err) {
		this.args.splice(this.params.length, 1, null);
		if (this.promptLimit && this._prompted < this.promptLimit) return this.reprompt(err);
		throw err;
	}

	/**
	 * Finalizes parameters and arguments for this prompt.
	 * @since 0.5.0
	 * @returns {any[]}
	 * @private
	 */
	finalize() {
		for (let i = this.params.length - 1; i >= 0 && this.params[i] === undefined; i--) this.params.pop();
		for (let i = this.args.length - 1; i >= 0 && this.args[i] === undefined; i--) this.args.pop();
		return this.params;
	}

	/**
	 * Splits the original message string into arguments.
	 * @since 0.5.0
	 * @param {string} original The original message string
	 * @returns {void}
	 * @private
	 */
	_setup(original) {
		const { content, flags } = this.constructor.getFlags(original, this.usage.usageDelim);
		this.flags = flags;
		this.args = this.quotedStringSupport ?
			this.constructor.getQuotedStringArgs(content, this.usage.usageDelim).map(arg => arg.trim()) :
			this.constructor.getArgs(content, this.usage.usageDelim).map(arg => arg.trim());
	}

	/**
	 * Parses a message into string args
	 * @since 0.5.0
	 * @param {string} content The remaining content
	 * @param {string} delim The delimiter
	 * @returns {Object}
	 * @private
	 */
	static getFlags(content, delim) {
		const flags = {};
		content = content.replace(TextPrompt.flagRegex, (match, fl, ...quote) => {
			flags[fl] = (quote.slice(0, -2).find(el => el) || fl).replace(/\\/g, '');
			return '';
		}).replace(new RegExp(`(?:(\\s)\\s+|(${delim})(?:${delim})+)`, 'g'), '$1').trim();
		return { content, flags };
	}

	/**
	 * Parses a message into string args
	 * @since 0.0.1
	 * @param {string} content The remaining content
	 * @param {string} delim The delimiter
	 * @returns {string[]}
	 * @private
	 */
	static getArgs(content, delim) {
		const args = content.replace(new RegExp(`(${delim})(?:${delim})+`, 'g'), '$1').split(delim !== '' ? delim : undefined);
		return args.length === 1 && args[0] === '' ? [] : args;
	}

	/**
	 * Parses a message into string args taking into account quoted strings
	 * @since 0.0.1
	 * @param {string} content The remaining content
	 * @param {string} delim The delimiter
	 * @returns {string[]}
	 * @private
	 */
	static getQuotedStringArgs(content, delim) {
		if (!delim || delim === '') return [content];

		const args = [];

		for (let i = 0; i < content.length; i++) {
			let current = '';
			if (content.slice(i, i + delim.length) === delim) {
				i += delim.length - 1;
				continue;
			}
			const quote = quotes.find(qt => qt.includes(content[i]));
			if (quote) {
				const qts = quote.split('');
				while (i + 1 < content.length && (content[i] === '\\' || !qts.includes(content[i + 1]))) current += content[++i] !== '\\' ? content[i] : '';
				i++;
				args.push(current);
			} else {
				current += content[i];
				while (i + 1 < content.length && content.slice(i + 1, i + delim.length + 1) !== delim) current += content[++i];
				args.push(current);
			}
		}

		return args.length === 1 && args[0] === '' ? [] : args;
	}

}

/**
 * Regular Expression to match flags with quoted string support.
 * @since 0.5.0
 * @type {RegExp}
 * @static
 */
TextPrompt.flagRegex = new RegExp(`--([\\w-]{2,})(?:=(?:${quotes.map(qu => `[${qu}]((?:[^${qu}\\\\]|\\\\.)*)[${qu}]`).join('|')}|([\\w-]+)))?`, 'g');

module.exports = TextPrompt;
<|endoftext|>


<commit_before>/* Gear.js
KC3改 Equipment Object
*/
(function(){
	"use strict";

	window.KC3Gear = function( data ){
		// Default object properties incuded in stringifications
		this.itemId = 0;
		this.masterId = 0;
		this.stars = 0;
		this.lock = 0;
		this.ace = -1;

		// If specified with data, fill this object
		if(typeof data != "undefined"){
			// Initialized with raw data
			if(typeof data.api_id != "undefined"){
				this.itemId = data.api_id;
				this.masterId = data.api_slotitem_id;
				this.stars = data.api_level;
				this.lock = data.api_locked;

				// Plane Ace mechanism
				if(typeof data.api_alv != "undefined"){
					this.ace = data.api_alv;
				}

			// Initialized with formatted data
			}else{
				$.extend(this, data);
			}
		}
	};

	KC3Gear.prototype.master = function(){ return KC3Master.slotitem( this.masterId ); };
	KC3Gear.prototype.name = function(){ return KC3Meta.gearName( this.master().api_name ); };

	/* FIGHTER POWER
	Get fighter power of this equipment on a slot
	--------------------------------------------------------------*/
	KC3Gear.prototype.fighterPower = function(capacity){
		// Empty item means no fighter power
		if(this.itemId===0){ return 0; }

		// Check if this object is a fighter plane
		if( KC3GearManager.antiAirFighterType2Ids.indexOf( String(this.master().api_type[2]) ) > -1){
			return Math.floor( Math.sqrt(capacity) * this.master().api_tyku );
		}

		// Equipment did not return on plane check, no fighter power
		return 0;
	};

	KC3Gear.prototype.AAStatImprovementBonous = function() {
		if (this.itemId !== 0) {
			var hasBeenImproved = typeof(this.stars) !== "undefined" && this.stars > 0;
			// reference 1:
			// http://wikiwiki.jp/kancolle/?%B2%FE%BD%A4%B9%A9%BE%B3#ic9d577c
			// for carrier-based fighters,
			// every star grants +0.2 AA stat, which is added to the AA stat bonus
			// of the gear.
			if (this.master().api_type[2] === 6 && // is carrier-based fighter
				hasBeenImproved) {
				return 0.2 * this.stars;
			}
			// reference 2:
			// http://ja.kancolle.wikia.com/wiki/%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89:951#32
			// for fighter-bombers, every star grants +0.25 AA stat.
			// there's no distinction between bomber and fighter-bomber from KCAPI,
			// so let's just say the rule applies to all bombers.
			// (regular bombers cannot be improved anyway, for now...)
			// seaplane fighters are improvable, also possible +0.25
			// ref: https://twitter.com/syoukuretin/status/842673175947177984
			// seaplane bombers are still not improvable
			if ((this.master().api_type[2] === 7 || // is bomber or
				 this.master().api_type[3] === 43) // is seaplane fighter
				&& hasBeenImproved) {
				return 0.25 * this.stars;
			}
		}
		return 0;
	};

	/* FIGHTER POWER: VETERAN
	Get fighter power of this equipment
	with added whole number proficiency bonus
	--------------------------------------------------------------*/
	KC3Gear.prototype.fighterVeteran = function(capacity){
		// Empty item means no fighter power
		if(this.itemId===0){ return 0; }

		// Check if this object is a fighter plane
		if( KC3GearManager.antiAirFighterType2Ids.indexOf( String(this.master().api_type[2]) ) > -1){
			var typInd = String( this.master().api_type[2] );
			var airAverageTable = ConfigManager.air_average[typInd];
			// if new type added to default, have to reset to default
			if(typeof airAverageTable === 'undefined') {
				var defaultTable = ConfigManager.defaults().air_average[typInd];
				if(typeof defaultTable !== 'undefined') {
					ConfigManager.resetValueOf('air_average');
					airAverageTable = defaultTable;
					console.info("Setting 'air_average' reset to default for missing type:", typInd);
				} else {
					console.warn("No 'air_average' setting found for type:", typInd);
					return 0;
				}
			}

			var veteranBonus;
			if(this.ace==-1){
				veteranBonus = airAverageTable[ 0 ];
			}else{
				veteranBonus = airAverageTable[ this.ace ];
			}
			var aaStat = this.master().api_tyku;
			aaStat += this.AAStatImprovementBonous();
			// Interceptor use evasion as interception stat against fighter
			var intStat = KC3GearManager.interceptorsType3Ids.indexOf(this.master().api_type[3]) > -1 ?
				this.master().api_houk : 0;
			aaStat += intStat * 1.5;
			return Math.floor( Math.sqrt(capacity) * aaStat + veteranBonus );
		}

		// Equipment did not return on plane check, no fighter power
		return 0;
	};

	/* FIGHTER POWER: VETERAN with BOUNDS
	Get fighter power of this equipment
	as an array with lower and upper bound bonuses
	--------------------------------------------------------------*/
	KC3Gear.prototype.fighterBounds = function(capacity){
		// Empty item means no fighter power
		if(this.itemId===0){ return [0,0]; }

		// Check if this object is a fighter plane
		if( KC3GearManager.antiAirFighterType2Ids.indexOf( String(this.master().api_type[2]) ) > -1){
			// console.log("this.ace", this.ace);

			var typInd = String( this.master().api_type[2] );
			var airBoundTable = ConfigManager.air_bounds[typInd];
			// if new type added to default, have to reset to default
			if(typeof airBoundTable === 'undefined') {
				var defaultTable = ConfigManager.defaults().air_bounds[typInd];
				if(typeof defaultTable !== 'undefined') {
					ConfigManager.resetValueOf('air_bounds');
					airBoundTable = defaultTable;
					console.info("Setting 'air_bounds' reset to default for missing type:", typInd);
				} else {
					console.warn("No 'air_bounds' setting found for type:", typInd);
					return [0,0];
				}
			}

			var veteranBounds;
			if(this.ace==-1){
				veteranBounds = airBoundTable[ 0 ];
			}else{
				veteranBounds = airBoundTable[ this.ace ];
			}
			var aaStat = this.master().api_tyku;
			aaStat += this.AAStatImprovementBonous();
			// Interceptor use evasion as interception stat against fighter
			var intStat = KC3GearManager.interceptorsType3Ids.indexOf(this.master().api_type[3]) > -1 ?
				this.master().api_houk : 0;
			aaStat += intStat * 1.5;

			return [
				Math.floor( Math.sqrt(capacity) * aaStat + veteranBounds[0] ),
				Math.floor( Math.sqrt(capacity) * aaStat + veteranBounds[1] ),
			];
		}

		// Equipment did not return on plane check, no fighter power
		return [0,0];
	};

	/* FIGHTER POWER on Air Defense with INTERCEPTOR FORMULA
	 * Normal planes get usual fighter power formula
	 * Interceptor planes get two special stats: interception, anti-bomber
	see http://wikiwiki.jp/kancolle/?%B4%F0%C3%CF%B9%D2%B6%F5%C2%E2#sccf3a4c
	see http://kancolle.wikia.com/wiki/Land-Base_Aerial_Support#Fighter_Power_Calculations
	--------------------------------------------------------------*/
	KC3Gear.prototype.interceptionPower = function(capacity){
		// Empty item means no fighter power
		if(this.itemId===0){ return 0; }
		// Check if this object is a interceptor plane or not
		if( KC3GearManager.interceptorsType3Ids.indexOf(this.master().api_type[3]) > -1) {
			// If slot has plane capacity
			if (capacity) {
				var interceptPower = (
					// Base anti-air power
					this.master().api_tyku +
					// Interception is from evasion
					this.master().api_houk +
					// Anti-Bomber is from hit accuracy
					this.master().api_houm * 2 +
					// Although no interceptor can be improved for now
					this.AAStatImprovementBonous()
				) * Math.sqrt(capacity);

				// Proficiency Bonus, no fail-over here
				if(this.ace != 1){
					var typInd = String(this.master().api_type[2]);
					var airAverageTable = ConfigManager.air_average[typInd];
					if (typeof airAverageTable != "undefined") {
						interceptPower += airAverageTable[this.ace] || 0;
					}
				}

				return Math.floor(interceptPower);
			} else {
				return 0;
			}
		} else {
			return this.fighterVeteran(capacity);
		}
	};

	KC3Gear.prototype.supportPower = function(){
		// Empty item means no fighter power
		if(this.itemId===0){ return 0; }

		// 1.5 TP + 2.0 DV
		return (1.5 * Number(this.master().api_raig) )
			+(2.0 * Number(this.master().api_baku) );
	};

	KC3Gear.prototype.bauxiteCost = function(slotCurrent, slotMaxeq){
		// Only used for the slot already equiped aircrafts, unused for now
		if(this.itemId===0){ return 0; }
		if( KC3GearManager.carrierBasedAircraftType3Ids.indexOf( this.master().api_type[3] ) > -1){
			return KC3GearManager.carrierSupplyBauxiteCostPerSlot * (slotMaxeq - slotCurrent);
		}
		return 0;
	};

	KC3Gear.prototype.aaDefense = function(forFleet) {
		if (this.masterId === 0)
			return 0;
		// permissive on "this.stars" in case the improvement level is not yet available.
		var stars = (typeof this.stars === "number") ? this.stars : 0;
		return KC3Gear.aaDefense( this.master(), stars, forFleet );
	};

	// there is no need of any Gear instance to calculate this
	// as long as we know the improvement level
	// serves as a shortcut to AntiAir module
	KC3Gear.aaDefense = function(mst, stars, forFleet) {
		return AntiAir.calcEquipmentAADefense(mst, stars, forFleet);
	};

	/*
	 * Build tooltip HTML of this Gear. Used by Panel/Strategy Room.
	 */
	KC3Gear.prototype.htmlTooltip = function() {
		return KC3Gear.buildGearTooltip(this);
	};
	/** Also export a static method */
	KC3Gear.buildGearTooltip = function(gearObj, altName) {
		var gearData = gearObj.master();
		if(gearObj.itemId === 0 || gearData === false){ return ""; }
		var title = $('

');
		$(".name", title).html(altName || gearObj.name());
		// Some stats only shown at Equipment Library, omitted here.
		var planeStats = ["or", "kk"];
		$.each([
			["hp", "taik"],
			["fp", "houg"],
			["ar", "souk"],
			["tp", "raig"],
			["dv", "baku"],
			["aa", "tyku"],
			["as", "tais"],
			["ht", "houm"],
			["ev", "houk"],
			["ls", "saku"],
			["rn", "leng"],
			["or", "distance"]
		], function(index, sdata) {
			var statBox = $('

');
			statBox.css("font-size", "11px");
			if((gearData["api_" + sdata[1]] || 0) !== 0
				&& (planeStats.indexOf(sdata[0]) < 0
				|| (planeStats.indexOf(sdata[0]) >=0
					&& KC3GearManager.landBasedAircraftType3Ids.indexOf(gearData.api_type[3])>-1)
				)
			) { // Path of image should be inputed, maybe
				$(".icon", statBox).attr("src", "../../../../assets/img/stats/" + sdata[0] + ".png");
				$(".icon", statBox).width(13).height(13).css("margin-top", "-3px");
				if(sdata[0] === "rn") {
					$(".value", statBox).text(["?","S","M","L","VL","XL"][gearData["api_" + sdata[1]]] || "?");
				} else {
					$(".value", statBox).text(gearData["api_" + sdata[1]]);
				}
				title.append(statBox.html());
			}
		});
		return title.html();
	};

	// prepare info necessary for deckbuilder
	KC3Gear.prototype.deckbuilder = function() {
		if (this.masterId <= 0)
			return false;
		var result = {id: this.masterId};
		if (typeof this.stars !== "undefined" &&
			this.stars > 0)
			result.rf = this.stars;
		if (typeof this.ace !== "undefined" &&
			this.ace > 0)
			result.mas = this.ace;
		return result;
	};
})();
<commit_msg>Update seaplane fighter improvement AA bonus<commit_after>/* Gear.js
KC3改 Equipment Object
*/
(function(){
	"use strict";

	window.KC3Gear = function( data ){
		// Default object properties incuded in stringifications
		this.itemId = 0;
		this.masterId = 0;
		this.stars = 0;
		this.lock = 0;
		this.ace = -1;

		// If specified with data, fill this object
		if(typeof data != "undefined"){
			// Initialized with raw data
			if(typeof data.api_id != "undefined"){
				this.itemId = data.api_id;
				this.masterId = data.api_slotitem_id;
				this.stars = data.api_level;
				this.lock = data.api_locked;

				// Plane Ace mechanism
				if(typeof data.api_alv != "undefined"){
					this.ace = data.api_alv;
				}

			// Initialized with formatted data
			}else{
				$.extend(this, data);
			}
		}
	};

	KC3Gear.prototype.master = function(){ return KC3Master.slotitem( this.masterId ); };
	KC3Gear.prototype.name = function(){ return KC3Meta.gearName( this.master().api_name ); };

	/* FIGHTER POWER
	Get fighter power of this equipment on a slot
	--------------------------------------------------------------*/
	KC3Gear.prototype.fighterPower = function(capacity){
		// Empty item means no fighter power
		if(this.itemId===0){ return 0; }

		// Check if this object is a fighter plane
		if( KC3GearManager.antiAirFighterType2Ids.indexOf( String(this.master().api_type[2]) ) > -1){
			return Math.floor( Math.sqrt(capacity) * this.master().api_tyku );
		}

		// Equipment did not return on plane check, no fighter power
		return 0;
	};

	KC3Gear.prototype.AAStatImprovementBonous = function() {
		if (this.itemId !== 0) {
			var hasBeenImproved = typeof(this.stars) !== "undefined" && this.stars > 0;
			// reference:
			// http://wikiwiki.jp/kancolle/?%B2%FE%BD%A4%B9%A9%BE%B3#ic9d577c
			// for carrier-based fighters,
			// every star grants +0.2 AA stat, which is added to the AA stat bonus
			// of the gear.
			if (this.master().api_type[2] === 6 && // is carrier-based fighter
				hasBeenImproved) {
				return 0.2 * this.stars;
			}
			// seaplane fighters are improvable now, 0.192 < AA bonus < 0.2014 (still verifying)
			// refs: https://twitter.com/syoukuretin/status/843271212377690112
			//       https://twitter.com/DarkQuetzal/status/842686753815191556
			// seaplane bombers are still not improvable
			if (this.master().api_type[3] === 43 // is seaplane fighter but not bomber/recon
				&& hasBeenImproved) {
				return 0.2 * this.stars;
			}
			// reference:
			// http://ja.kancolle.wikia.com/wiki/%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89:951#32
			// for fighter-bombers, every star grants +0.25 AA stat.
			// there's no distinction between bomber and fighter-bomber from KCAPI,
			// so let's just say the rule applies to all bombers.
			// (regular bombers cannot be improved anyway, for now...)
			if (this.master().api_type[2] === 7 // is bomber
				&& hasBeenImproved) {
				return 0.25 * this.stars;
			}
		}
		return 0;
	};

	/* FIGHTER POWER: VETERAN
	Get fighter power of this equipment
	with added whole number proficiency bonus
	--------------------------------------------------------------*/
	KC3Gear.prototype.fighterVeteran = function(capacity){
		// Empty item means no fighter power
		if(this.itemId===0){ return 0; }

		// Check if this object is a fighter plane
		if( KC3GearManager.antiAirFighterType2Ids.indexOf( String(this.master().api_type[2]) ) > -1){
			var typInd = String( this.master().api_type[2] );
			var airAverageTable = ConfigManager.air_average[typInd];
			// if new type added to default, have to reset to default
			if(typeof airAverageTable === 'undefined') {
				var defaultTable = ConfigManager.defaults().air_average[typInd];
				if(typeof defaultTable !== 'undefined') {
					ConfigManager.resetValueOf('air_average');
					airAverageTable = defaultTable;
					console.info("Setting 'air_average' reset to default for missing type:", typInd);
				} else {
					console.warn("No 'air_average' setting found for type:", typInd);
					return 0;
				}
			}

			var veteranBonus;
			if(this.ace==-1){
				veteranBonus = airAverageTable[ 0 ];
			}else{
				veteranBonus = airAverageTable[ this.ace ];
			}
			var aaStat = this.master().api_tyku;
			aaStat += this.AAStatImprovementBonous();
			// Interceptor use evasion as interception stat against fighter
			var intStat = KC3GearManager.interceptorsType3Ids.indexOf(this.master().api_type[3]) > -1 ?
				this.master().api_houk : 0;
			aaStat += intStat * 1.5;
			return Math.floor( Math.sqrt(capacity) * aaStat + veteranBonus );
		}

		// Equipment did not return on plane check, no fighter power
		return 0;
	};

	/* FIGHTER POWER: VETERAN with BOUNDS
	Get fighter power of this equipment
	as an array with lower and upper bound bonuses
	--------------------------------------------------------------*/
	KC3Gear.prototype.fighterBounds = function(capacity){
		// Empty item means no fighter power
		if(this.itemId===0){ return [0,0]; }

		// Check if this object is a fighter plane
		if( KC3GearManager.antiAirFighterType2Ids.indexOf( String(this.master().api_type[2]) ) > -1){
			// console.log("this.ace", this.ace);

			var typInd = String( this.master().api_type[2] );
			var airBoundTable = ConfigManager.air_bounds[typInd];
			// if new type added to default, have to reset to default
			if(typeof airBoundTable === 'undefined') {
				var defaultTable = ConfigManager.defaults().air_bounds[typInd];
				if(typeof defaultTable !== 'undefined') {
					ConfigManager.resetValueOf('air_bounds');
					airBoundTable = defaultTable;
					console.info("Setting 'air_bounds' reset to default for missing type:", typInd);
				} else {
					console.warn("No 'air_bounds' setting found for type:", typInd);
					return [0,0];
				}
			}

			var veteranBounds;
			if(this.ace==-1){
				veteranBounds = airBoundTable[ 0 ];
			}else{
				veteranBounds = airBoundTable[ this.ace ];
			}
			var aaStat = this.master().api_tyku;
			aaStat += this.AAStatImprovementBonous();
			// Interceptor use evasion as interception stat against fighter
			var intStat = KC3GearManager.interceptorsType3Ids.indexOf(this.master().api_type[3]) > -1 ?
				this.master().api_houk : 0;
			aaStat += intStat * 1.5;

			return [
				Math.floor( Math.sqrt(capacity) * aaStat + veteranBounds[0] ),
				Math.floor( Math.sqrt(capacity) * aaStat + veteranBounds[1] ),
			];
		}

		// Equipment did not return on plane check, no fighter power
		return [0,0];
	};

	/* FIGHTER POWER on Air Defense with INTERCEPTOR FORMULA
	 * Normal planes get usual fighter power formula
	 * Interceptor planes get two special stats: interception, anti-bomber
	see http://wikiwiki.jp/kancolle/?%B4%F0%C3%CF%B9%D2%B6%F5%C2%E2#sccf3a4c
	see http://kancolle.wikia.com/wiki/Land-Base_Aerial_Support#Fighter_Power_Calculations
	--------------------------------------------------------------*/
	KC3Gear.prototype.interceptionPower = function(capacity){
		// Empty item means no fighter power
		if(this.itemId===0){ return 0; }
		// Check if this object is a interceptor plane or not
		if( KC3GearManager.interceptorsType3Ids.indexOf(this.master().api_type[3]) > -1) {
			// If slot has plane capacity
			if (capacity) {
				var interceptPower = (
					// Base anti-air power
					this.master().api_tyku +
					// Interception is from evasion
					this.master().api_houk +
					// Anti-Bomber is from hit accuracy
					this.master().api_houm * 2 +
					// Although no interceptor can be improved for now
					this.AAStatImprovementBonous()
				) * Math.sqrt(capacity);

				// Proficiency Bonus, no fail-over here
				if(this.ace != 1){
					var typInd = String(this.master().api_type[2]);
					var airAverageTable = ConfigManager.air_average[typInd];
					if (typeof airAverageTable != "undefined") {
						interceptPower += airAverageTable[this.ace] || 0;
					}
				}

				return Math.floor(interceptPower);
			} else {
				return 0;
			}
		} else {
			return this.fighterVeteran(capacity);
		}
	};

	KC3Gear.prototype.supportPower = function(){
		// Empty item means no fighter power
		if(this.itemId===0){ return 0; }

		// 1.5 TP + 2.0 DV
		return (1.5 * Number(this.master().api_raig) )
			+(2.0 * Number(this.master().api_baku) );
	};

	KC3Gear.prototype.bauxiteCost = function(slotCurrent, slotMaxeq){
		// Only used for the slot already equiped aircrafts, unused for now
		if(this.itemId===0){ return 0; }
		if( KC3GearManager.carrierBasedAircraftType3Ids.indexOf( this.master().api_type[3] ) > -1){
			return KC3GearManager.carrierSupplyBauxiteCostPerSlot * (slotMaxeq - slotCurrent);
		}
		return 0;
	};

	KC3Gear.prototype.aaDefense = function(forFleet) {
		if (this.masterId === 0)
			return 0;
		// permissive on "this.stars" in case the improvement level is not yet available.
		var stars = (typeof this.stars === "number") ? this.stars : 0;
		return KC3Gear.aaDefense( this.master(), stars, forFleet );
	};

	// there is no need of any Gear instance to calculate this
	// as long as we know the improvement level
	// serves as a shortcut to AntiAir module
	KC3Gear.aaDefense = function(mst, stars, forFleet) {
		return AntiAir.calcEquipmentAADefense(mst, stars, forFleet);
	};

	/*
	 * Build tooltip HTML of this Gear. Used by Panel/Strategy Room.
	 */
	KC3Gear.prototype.htmlTooltip = function() {
		return KC3Gear.buildGearTooltip(this);
	};
	/** Also export a static method */
	KC3Gear.buildGearTooltip = function(gearObj, altName) {
		var gearData = gearObj.master();
		if(gearObj.itemId === 0 || gearData === false){ return ""; }
		var title = $('

');
		$(".name", title).html(altName || gearObj.name());
		// Some stats only shown at Equipment Library, omitted here.
		var planeStats = ["or", "kk"];
		$.each([
			["hp", "taik"],
			["fp", "houg"],
			["ar", "souk"],
			["tp", "raig"],
			["dv", "baku"],
			["aa", "tyku"],
			["as", "tais"],
			["ht", "houm"],
			["ev", "houk"],
			["ls", "saku"],
			["rn", "leng"],
			["or", "distance"]
		], function(index, sdata) {
			var statBox = $('

');
			statBox.css("font-size", "11px");
			if((gearData["api_" + sdata[1]] || 0) !== 0
				&& (planeStats.indexOf(sdata[0]) < 0
				|| (planeStats.indexOf(sdata[0]) >=0
					&& KC3GearManager.landBasedAircraftType3Ids.indexOf(gearData.api_type[3])>-1)
				)
			) { // Path of image should be inputed, maybe
				$(".icon", statBox).attr("src", "../../../../assets/img/stats/" + sdata[0] + ".png");
				$(".icon", statBox).width(13).height(13).css("margin-top", "-3px");
				if(sdata[0] === "rn") {
					$(".value", statBox).text(["?","S","M","L","VL","XL"][gearData["api_" + sdata[1]]] || "?");
				} else {
					$(".value", statBox).text(gearData["api_" + sdata[1]]);
				}
				title.append(statBox.html());
			}
		});
		return title.html();
	};

	// prepare info necessary for deckbuilder
	KC3Gear.prototype.deckbuilder = function() {
		if (this.masterId <= 0)
			return false;
		var result = {id: this.masterId};
		if (typeof this.stars !== "undefined" &&
			this.stars > 0)
			result.rf = this.stars;
		if (typeof this.ace !== "undefined" &&
			this.ace > 0)
			result.mas = this.ace;
		return result;
	};
})();
<|endoftext|>


<commit_before>/* jshint node: true */
/**
 * Wake Up Platform
 * (c) Telefonica Digital, 2014 - All rights reserved
 * License: GNU Affero V3 (see LICENSE file)
 * Fernando Rodríguez Sela <frsela at tid dot es>
 * Guillermo López Leal <gll at tid dot es>
 */

'use strict';

var config = process.configuration,
    redis = require('redis'),
    helpers = require('../shared_libs/helpers'),
    range_check = require('range_check');

module.exports = (function mobile_networks() {
  var client = redis.createClient(config.redis.port, config.redis.host,
    config.redis.options);
  init();

  function init() {
    var client_cache = redis.createClient(config.redis.port, config.redis.host,
      config.redis.options);
    client_cache.on('message', function onMessage(channel, message) {
      if (channel === 'networks_changed') {
        clearCache();
      }
    });
    client_cache.subscribe('networks_changed');

    client.on('error', function(err) {
      console.error('Mobile networks error: ' + err);
    });
    client_cache.on('error', function(err) {
      console.error('Mobile networks cache error: ' + err);
    });
  }

  function clearCache() {
    console.log('[ToDo] limpiando cache');
  }

  function getOperator(mcc, mnc, callback) {
    client.hget('operators', mcc + '-' + mnc, function(e, operatorData) {
      try {
        var data = JSON.parse(operatorData);
        callback(null, data);
      } catch (e) {
        callback(e);
      }
    });
  }

  function getNetworkByNetID(netid, callback) {
    client.hget('networks', netid, function(e, networkData) {
      try {
        var data = JSON.parse(networkData);
        callback(null, data);
      } catch (e) {
        callback(e);
      }
    });
  }

  function getNetworkByMCCMNC(mcc, mnc, callback) {
    getOperator(mcc, mnc, function(e, data) {
      if (e) {
        callback(e);
        return;
      }
      if (data.defaultNetwork) {
        getNetworkByNetID(data.defaultNetwork, callback);
      } else {
        callback('Mobile Network without wakeup host');
      }
    });
  }

  function getAllNetworks(callback) {
    client.hgetall('networks', callback);
  }

  function enableNetwork(networkName) {
    getNetworkByNetID(networkName, function(err, data) {
      if (err) return;
      data.offline = false;
      client.hset('networks', networkName, JSON.stringify(data));
    });
  }

  function disableNetwork(networkName) {
    getNetworkByNetID(networkName, function(err, data) {
      if (err) return;
      data.offline = true;
      client.hset('networks', networkName, JSON.stringify(data));
    });
  }

  return {
    getNetwork: function(netid, callback) {
      callback = helpers.checkCallback(callback);

      if (typeof(netid) === 'object') {
        if (!netid.mcc || !netid.mnc) {
          callback('No valid netid object');
          return;
        }
        getNetworkByMCCMNC(netid.mcc, netid.mnc, callback);
      } else {
        getNetworkByNetID(netid, callback);
      }
    },

    getOperator: function(operator, callback) {
      if (typeof(callback) != 'function') {
        callback = function() {};
      }

      if (typeof(operator) !== 'object' ||
          !operator.mcc || !operator.mnc) {
        callback('No valid operator object');
        return;
      }

      getOperator(operator.mcc, operator.mnc, callback);
    },

    // Checks if the deviceip is in a valid range for the specified network
    checkNetwork: function(netid, deviceip, callback) {
      if (typeof(callback) != 'function') {
        callback = function() {};
      }

      if (!range_check.valid_ip(deviceip)) {
        callback('No valid device IP');
        return;
      }

      this.getNetwork(netid, function(error, data) {
        if (error || !data) {
          callback(error || 'No network found');
          return;
        }
        // Client IP is out of the mobile network
        if (!range_check.in_range(deviceip, data.range)) {
          callback('Client IP (' + deviceip +
            ') is out of the mobile network range - ' + data.range);
          return;
        }
        callback(null, data);
      });
    },

    // Returns an array with all networks with a local node in them
    getNetworksWithLocalNode: function(callback) {
      if (typeof(callback) != 'function') {
        return;
      }
      getAllNetworks(function(err, networks) {
        if (err)
          return callback(err);

        // Convert to an array
        var nets = [];
        var k = Object.keys(networks);
        for (var i = 0; i < k.length; i++) {
          try {
            var json = JSON.parse(networks[k[i]]);
          } catch (e) {
            log.debug('mobile_networks: Error parsing network data');
          }
          nets.push({
            name: k[i],
            info: json
          });
        }
        callback(null, nets);
      });
    },

    // Enable/Disable network status
    changeNetworkStatus: function(networkName, networkStatus) {
      if (networkStatus) {
        enableNetwork(networkName);
      } else {
        disableNetwork(networkName);
      }
    }
  };
})();
<commit_msg>Update mobile_networks.js<commit_after>/* jshint node: true */
/**
 * Wake Up Platform
 * (c) Telefonica Digital, 2014 - All rights reserved
 * License: GNU Affero V3 (see LICENSE file)
 * Fernando Rodríguez Sela <frsela at tid dot es>
 * Guillermo López Leal <gll at tid dot es>
 */

'use strict';

var config = process.configuration,
    redis = require('redis'),
    helpers = require('../shared_libs/helpers'),
    range_check = require('range_check');

module.exports = (function mobile_networks() {
  var client = redis.createClient(config.redis.port, config.redis.host,
    config.redis.options);
  init();

  function init() {
    var client_cache = redis.createClient(config.redis.port, config.redis.host,
      config.redis.options);
    client_cache.on('message', function onMessage(channel, message) {
      if (channel === 'networks_changed') {
        clearCache();
      }
    });
    client_cache.subscribe('networks_changed');

    client.on('error', function(err) {
      console.error('Mobile networks error: ' + err);
    });
    client_cache.on('error', function(err) {
      console.error('Mobile networks cache error: ' + err);
    });
  }

  function clearCache() {
    console.log('[ToDo] limpiando cache');
  }

  function getOperator(mcc, mnc, callback) {
    client.hget('operators', mcc + '-' + mnc, function(e, operatorData) {
      try {
        var data = JSON.parse(operatorData);
        callback(null, data);
      } catch (e) {
        callback(e);
      }
    });
  }

  function getNetworkByNetID(netid, callback) {
    client.hget('networks', netid, function(e, networkData) {
      try {
        var data = JSON.parse(networkData);
        callback(null, data);
      } catch (e) {
        callback(e);
      }
    });
  }

  function getNetworkByMCCMNC(mcc, mnc, callback) {
    getOperator(mcc, mnc, function(e, data) {
      if (e) {
        callback(e);
        return;
      }
      if (data.defaultNetwork) {
        getNetworkByNetID(data.defaultNetwork, callback);
      } else {
        callback('Mobile Network without wakeup host');
      }
    });
  }

  function getAllNetworks(callback) {
    client.hgetall('networks', callback);
  }

  function enableNetwork(networkName) {
    getNetworkByNetID(networkName, function(err, data) {
      if (err) return;
      data.offline = false;
      client.hset('networks', networkName, JSON.stringify(data));
    });
  }

  function disableNetwork(networkName) {
    getNetworkByNetID(networkName, function(err, data) {
      if (err) return;
      data.offline = true;
      client.hset('networks', networkName, JSON.stringify(data));
    });
  }

  return {
    getNetwork: function(netid, callback) {
      callback = helpers.checkCallback(callback);

      if (typeof(netid) === 'object') {
        if (!netid.mcc || !netid.mnc) {
          callback('No valid netid object');
          return;
        }
        getNetworkByMCCMNC(netid.mcc, netid.mnc, callback);
      } else {
        getNetworkByNetID(netid, callback);
      }
    },

    getOperator: function(operator, callback) {
      if (typeof(callback) != 'function') {
        callback = function() {};
      }

      if (typeof(operator) !== 'object' ||
          !operator.mcc || !operator.mnc) {
        callback('No valid operator object');
        return;
      }

      getOperator(operator.mcc, operator.mnc, callback);
    },

    // Checks if the deviceip is in a valid range for the specified network
    checkNetwork: function(netid, deviceip, callback) {
      if (typeof(callback) != 'function') {
        callback = function() {};
      }

      if (!range_check.valid_ip(deviceip)) {
        callback('No valid device IP');
        return;
      }

      this.getNetwork(netid, function(error, data) {
        if (error || !data) {
          callback(error || 'No network found');
          return;
        }
        // Client IP is out of the mobile network
        if (!range_check.in_range(deviceip, data.range)) {
          callback('Client IP (' + deviceip +
            ') is out of the mobile network range - ' + data.range);
          return;
        }
        callback(null, data);
      });
    },

    // Returns an array with all networks with a local node in them
    getNetworksWithLocalNode: function(callback) {
      if (typeof(callback) != 'function') {
        return;
      }
      getAllNetworks(function(err, networks) {
        if (err)
          return callback(err);

        // Convert to an array
        var nets = [];
        var k = Object.keys(networks);
        for (var i = 0; i < k.length; i++) {
          try {
            var json = JSON.parse(networks[k[i]]);
          } catch (e) {
            log.debug('mobile_networks: Error parsing network data');
          }
          nets.push({
            name: k[i],
            info: json
          });
        }
        callback(null, nets);
      });
    },

    // Enable/Disable network status
    changeNetworkStatus: function(networkName, networkStatus) {
      if (networkStatus) {
        enableNetwork(networkName);
      } else {
        disableNetwork(networkName);
      }
    },

    // Returns network information which is periodically updated for
    // down wakeup nodes
    getNetworkStatuses: function() {
        // TODO
    }
  };
})();
<|endoftext|>


<commit_before>import {
  rendererDebounced as $rendererDebounced,
  rendering as $rendering,
} from '../util/symbols';
import assign from '../util/assign';
import data from '../util/data';
import empty from '../util/empty';
import dashCase from '../util/dash-case';

function getDefaultValue(elem, name, opts) {
  return typeof opts.default === 'function' ? opts.default(elem, { name }) : opts.default;
}

function getInitialValue(elem, name, opts) {
  return typeof opts.initial === 'function' ? opts.initial(elem, { name }) : opts.initial;
}

function getPropData(elem, name) {
  const namespace = `api/property/${typeof name === 'symbol' ? String(name) : name}`;
  return data(elem, namespace);
}

function createNativePropertyDefinition(name, opts) {
  const prop = {
    configurable: true,
    enumerable: true,
  };

  prop.created = function (elem) { // eslint-disable-line func-names
    const propData = getPropData(elem, name);
    const attributeName = opts.attribute === true ? dashCase(name) : opts.attribute;
    let initialValue = elem[name];
    let shouldSyncAttribute = false;

    // Store property to attribute link information.
    data(elem, 'attributeLinks')[attributeName] = name;
    data(elem, 'propertyLinks')[name] = attributeName;

    // Set up initial value if it wasn't specified.
    if (empty(initialValue)) {
      if (attributeName && elem.hasAttribute(attributeName)) {
        initialValue = opts.deserialize(elem.getAttribute(attributeName));
      } else if ('initial' in opts) {
        initialValue = getInitialValue(elem, name, opts);
        shouldSyncAttribute = true;
      } else if ('default' in opts) {
        initialValue = getDefaultValue(elem, name, opts);
      }
    }

    if (shouldSyncAttribute) {
      prop.set.call(elem, initialValue);
    } else {
      propData.internalValue = opts.coerce ? opts.coerce(initialValue) : initialValue;
    }
  };

  prop.get = function () { // eslint-disable-line func-names
    const propData = getPropData(this, name);
    const { internalValue } = propData;
    if (typeof opts.get === 'function') {
      return opts.get(this, { name, internalValue });
    }
    return internalValue;
  };

  prop.set = function (newValue) { // eslint-disable-line func-names
    const propData = getPropData(this, name);
    let { oldValue } = propData;
    let shouldRemoveAttribute = false;

    if (empty(oldValue)) {
      oldValue = null;
    }

    if (empty(newValue)) {
      newValue = getDefaultValue(this, name, opts);
      shouldRemoveAttribute = true;
    }

    if (typeof opts.coerce === 'function') {
      newValue = opts.coerce(newValue);
    }

    propData.internalValue = newValue;

    const changeData = { name, newValue, oldValue };

    if (typeof opts.set === 'function') {
      opts.set(this, changeData);
    }

    // Queue a re-render only if it's not currently rendering.
    if (!this[$rendering]) {
      this[$rendererDebounced](this);
    }

    propData.oldValue = newValue;

    // Link up the attribute.
    const attributeName = data(this, 'propertyLinks')[name];
    if (attributeName && !propData.settingAttribute) {
      const serializedValue = opts.serialize(newValue);
      const currentAttrValue = this.getAttribute(attributeName);
      const serializedIsEmpty = empty(serializedValue);
      const attributeChanged = !((serializedIsEmpty && empty(currentAttrValue)) ||
                                 serializedValue === currentAttrValue);
      propData.syncingAttribute = true;
      if (shouldRemoveAttribute || serializedIsEmpty) {
        this.removeAttribute(attributeName);
      } else {
        this.setAttribute(attributeName, serializedValue);
      }
      if (!attributeChanged && propData.syncingAttribute) {
        propData.syncingAttribute = false;
      }
    }

    // Allow the attribute to be linked again.
    propData.settingAttribute = false;
  };

  return prop;
}

export default function (opts) {
  opts = opts || {};

  if (typeof opts === 'function') {
    opts = { coerce: opts };
  }

  return name => createNativePropertyDefinition(name, assign({
    default: null,
    deserialize: value => value,
    serialize: value => value,
  }, opts));
}
<commit_msg>refactor: Updated attribute syncing algorithm.<commit_after>import {
  connected as $connected,
  rendererDebounced as $rendererDebounced,
} from '../util/symbols';
import assign from '../util/assign';
import data from '../util/data';
import empty from '../util/empty';
import dashCase from '../util/dash-case';

function getDefaultValue(elem, name, opts) {
  return typeof opts.default === 'function' ? opts.default(elem, { name }) : opts.default;
}

function getInitialValue(elem, name, opts) {
  return typeof opts.initial === 'function' ? opts.initial(elem, { name }) : opts.initial;
}

function getPropData(elem, name) {
  const namespace = `api/property/${typeof name === 'symbol' ? String(name) : name}`;
  return data(elem, namespace);
}

function createNativePropertyDefinition(name, opts) {
  const prop = {
    configurable: true,
    enumerable: true,
  };

  prop.created = function created(elem) {
    const propData = getPropData(elem, name);
    const attributeName = opts.attribute === true ? dashCase(name) : opts.attribute;
    let initialValue = elem[name];

    // Store property to attribute link information.
    data(elem, 'attributeLinks')[attributeName] = name;
    data(elem, 'propertyLinks')[name] = attributeName;

    // Set up initial value if it wasn't specified.
    if (empty(initialValue)) {
      if (attributeName && elem.hasAttribute(attributeName)) {
        initialValue = opts.deserialize(elem.getAttribute(attributeName));
      } else if ('initial' in opts) {
        initialValue = getInitialValue(elem, name, opts);
      } else if ('default' in opts) {
        initialValue = getDefaultValue(elem, name, opts);
      }
    }

    propData.internalValue = opts.coerce ? opts.coerce(initialValue) : initialValue;
  };

  prop.get = function get() {
    const propData = getPropData(this, name);
    const { internalValue } = propData;
    return typeof opts.get === 'function' ? opts.get(this, { name, internalValue }) : internalValue;
  };

  prop.set = function set(newValue) {
    const propData = getPropData(this, name);
    let { oldValue } = propData;
    let shouldRemoveAttribute = false;

    if (empty(oldValue)) {
      oldValue = null;
    }

    if (empty(newValue)) {
      newValue = getDefaultValue(this, name, opts);
      shouldRemoveAttribute = true;
    }

    if (typeof opts.coerce === 'function') {
      newValue = opts.coerce(newValue);
    }

    const changeData = { name, newValue, oldValue };

    if (typeof opts.set === 'function') {
      opts.set(this, changeData);
    }

    // Queue a re-render.
    this[$rendererDebounced](this);

    // Update prop data so we can use it next time.
    propData.internalValue = propData.oldValue = newValue;

    // Link up the attribute.
    if (this[$connected]) {
      const attributeName = data(this, 'propertyLinks')[name];

      // We only link if there's an attribute and the setting of it didn't trigger this.
      if (attributeName && !propData.settingAttribute) {
        const serializedValue = opts.serialize(newValue);
        const currentAttrValue = this.getAttribute(attributeName);
        const serializedIsEmpty = empty(serializedValue);
        const attributeChanged = !(
          (serializedIsEmpty && empty(currentAttrValue)) || serializedValue === currentAttrValue
        );

        propData.syncingAttribute = true;

        if (shouldRemoveAttribute || serializedIsEmpty) {
          this.removeAttribute(attributeName);
        } else {
          this.setAttribute(attributeName, serializedValue);
        }

        if (!attributeChanged && propData.syncingAttribute) {
          propData.syncingAttribute = false;
        }
      }

      // Allow the attribute to be linked again.
      propData.settingAttribute = false;
    }
  };

  return prop;
}

export default function (opts) {
  opts = opts || {};

  if (typeof opts === 'function') {
    opts = { coerce: opts };
  }

  return name => createNativePropertyDefinition(name, assign({
    default: null,
    deserialize: value => value,
    serialize: value => value,
  }, opts));
}
<|endoftext|>


<commit_before>;(function () {
  'use strict'
  /**
   * @ngdoc service
   * @name ehaCounter
   * @module eha.login-dialog
   */
  var ngModule = angular.module('eha.login-dialog.service', [])

  ngModule.service('ehaLoginDialogService', function (
    $q,
    $log,
    $modal,
    gettextCatalog
  ) {
    /*
     * The loginDialogService caches the modal that is currently shown
     * to be able to call multiple retriables, while not showing more
     * than one modal
     */
    var dialog
    return function loginDialog () {
      if (dialog) {
        return dialog
      }

      var buttonLabels = [
        gettextCatalog.getString('yes'),
        gettextCatalog.getString('no')
      ]

      var confirmDialog = $modal.open({
        templateUrl: 'templates/login-dialog.template.tpl.html',
        backdrop: 'static',
        keyboard: false,
        resolve: {
          modalParams: function ($q) {
            return $q.when({
              buttonLabels: {
                YES: buttonLabels[0],
                NO: buttonLabels[1]
              }
            })
          }
        },
        controller: 'EhaLoginDialogController'
      })

      dialog = confirmDialog.result
      confirmDialog.result.finally(function () {
        dialog = null
      })

      return confirmDialog.result
    }
  })

  // Check for and export to commonjs environment
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = ngModule
  }
})()
<commit_msg>fix: add dependencies<commit_after>;(function () {
  'use strict'
  /**
   * @ngdoc service
   * @name ehaCounter
   * @module eha.login-dialog
   */
  var ngModule = angular.module('eha.login-dialog.service', [
    'gettext',
    'ui.bootstrap.modal'
  ])

  ngModule.service('ehaLoginDialogService', function (
    $q,
    $log,
    $modal,
    gettextCatalog
  ) {
    /*
     * The loginDialogService caches the modal that is currently shown
     * to be able to call multiple retriables, while not showing more
     * than one modal
     */
    var dialog
    return function loginDialog () {
      if (dialog) {
        return dialog
      }

      var buttonLabels = [
        gettextCatalog.getString('yes'),
        gettextCatalog.getString('no')
      ]

      var confirmDialog = $modal.open({
        templateUrl: 'templates/login-dialog.template.tpl.html',
        backdrop: 'static',
        keyboard: false,
        resolve: {
          modalParams: function ($q) {
            return $q.when({
              buttonLabels: {
                YES: buttonLabels[0],
                NO: buttonLabels[1]
              }
            })
          }
        },
        controller: 'EhaLoginDialogController'
      })

      dialog = confirmDialog.result
      confirmDialog.result.finally(function () {
        dialog = null
      })

      return confirmDialog.result
    }
  })

  // Check for and export to commonjs environment
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = ngModule
  }
})()
<|endoftext|>


<commit_before>/*
 * Tangram
 * Copyright 2011 Baidu Inc. All rights reserved.
 */

///import magic.setup;
///import baidu.array.each;
///import baidu.dom.getAttr;
///import baidu.dom.insertHTML;
///import baidu.dom.remove;
///import magic.ComboBox;

(function() {
/**
 * @description 由原生select反向创建 组合框
 * @name magic.setup.comboBox
 * @function
 * @grammar magic.setup.comboBox(node, options)
 * @param {String|HTMLElement} node 原生select的id或者dom元素
 * @param {Object} options 选项
 * @param {Array<Object>} items ComboBox下拉菜单的数据，每项由value和content组成，如[{"value":0,"content":"女"},{"value":1,"content":"男"}]，默认[]。
 * @param {Number} viewSize 拉菜单最多显示的项目数，若选项多于此配置，则出现纵向滚动条，默认5。
 * @param {Boolean} readonly 输入框是否可以编辑输入，默认true。
 * @param {Boolean} disabled ComboBox是否处于禁用状态，默认false。
 * @param {Number} originIndex 初始化后默认选中的值的索引，不选中任何项为-1，当readonly为true时，默认0，反之默认-1。
 * @param {Number|String} width ComboBox的宽度，默认100%。
 * @return {magic.ComboBox} 组合框实例.
 * @author 夏登平(xiadengping@baidu.com)
 */
magic.setup.comboBox = function(el, options) {
    options = options || {};
    var el = baidu.dom.g(el),
        optData = parseSelectOptions(el),
        newItemOpt = null;

    baidu.object.extend(optData, {
        'width' : el.offsetWidth + 10,
        'disabled' : el.disabled
    });
    options = baidu.object.extend(optData, options);
   /**
    * @descript 组合框
    * @instace
    * @name magic.setup.comboBox!
    * @superClass magic.ComboBox
    * @return {magic.ComboBox} 组合框实例
    */
    var instance = magic.setup(el, magic.ComboBox, options);

    baidu.dom.insertHTML(el, 'beforeBegin', '');

    var host = baidu('#' + instance.guid + '-host');
    host.append(el);
    instance.select = el;
    el.style.width = (el.offsetWidth + 15) + 'px';
    el.style.visibility = 'hidden';
    instance.render(host, 'afterBegin');
    baidu(instance.getElement('container')).addClass('magic-combobox-container-setup');
    instance.on('change', function(event) {
        if (event.from == 'confirm') {
            //当用键盘选到初始值（最后一个值再往后选一个或第一个值往前选一个）
            //此时按回车，虽然出发onchange，但是原生select没有此值，无须改变。
            //by Dengping
            if (event.result.index) {
                el.options[event.result.index].selected = true;
            }
        } else if (event.from == 'blur') {
            var content = this.getElement('input').value;
            if (!newItemOpt) {
                newItemOpt = document.createElement('OPTION');
                newItemOpt.selected = true;
                el.add(newItemOpt);
            }
            newItemOpt.value = newItemOpt.text = content;
        }

    });

    instance.on('reload', function(event) {
        var data = event.data,
            length = data.length,
            i = 0;
        el.options.length = 0;
        for (; i < length; i ++) {
            var elmOption = document.createElement('OPTION');
            elmOption.value = data[i].value;
            elmOption.text = data[i].content;
            el.add(elmOption);
        }
    });

    //todo
    //当原生select有变动时，ComboBox不能同步。

    return instance;
};

/**
 * 从select中提取数据
 * @param {Node} selectNode
 * @return {Object} 数据对象
 */
function parseSelectOptions(selectNode) {
    var items = [],
        originIndex = -1,
        optionNodes = selectNode.options;
    baidu.each(optionNodes, function(index, item) {

        items.push({
            'value' : item.value,
            'content' : item.text
        });
        if (item.selected) {
            originIndex = index;
        }
    });
    return {
        'items' : items,
        'originIndex' : originIndex
    }
}

})();

<commit_msg>Merge branch 'dev' of https://github.com/BaiduFE/Magic into dev<commit_after>/*
 * Tangram
 * Copyright 2011 Baidu Inc. All rights reserved.
 */

///import magic.setup;
///import baidu.array.each;
///import baidu.dom.getAttr;
///import baidu.dom.insertHTML;
///import baidu.dom.remove;
///import magic.ComboBox;

(function() {
/**
 * @description 由原生select反向创建 组合框
 * @name magic.setup.comboBox
 * @function
 * @grammar magic.setup.comboBox(node, options)
 * @param {String|HTMLElement} node 原生select的id或者dom元素
 * @param {Object} options 选项
 * @param {Array<Object>} items ComboBox下拉菜单的数据，每项由value和content组成，如[{"value":0,"content":"女"},{"value":1,"content":"男"}]，默认[]。
 * @param {Number} viewSize 拉菜单最多显示的项目数，若选项多于此配置，则出现纵向滚动条，默认5。
 * @param {Boolean} readonly 输入框是否可以编辑输入，默认true。
 * @param {Boolean} disabled ComboBox是否处于禁用状态，默认false。
 * @param {Number} originIndex 初始化后默认选中的值的索引，不选中任何项为-1，当readonly为true时，默认0，反之默认-1。
 * @param {Number|String} width ComboBox的宽度，默认100%。
 * @return {magic.ComboBox} 组合框实例.
 * @author 夏登平(xiadengping@baidu.com)
 */
magic.setup.comboBox = function(el, options) {
    options = options || {};
    var el = baidu.dom.g(el),
        optData = parseSelectOptions(el),
        newItemOpt = null;

    baidu.object.extend(optData, {
        'width' : el.offsetWidth + 10,
        'disabled' : el.disabled
    });
    options = baidu.object.extend(optData, options);
   /**
    * @descript 组合框
    * @instace
    * @name magic.setup.comboBox!
    * @superClass magic.ComboBox
    * @return {magic.ComboBox} 组合框实例
    */
    var instance = magic.setup(el, magic.ComboBox, options);

    baidu.dom.insertHTML(el, 'beforeBegin', '');

    var host = baidu('#' + instance.guid + '-host');
    host.append(el);
    instance.select = el;
    el.style.width = (el.offsetWidth + 15) + 'px';
    el.style.visibility = 'hidden';
    instance.render(host, 'afterBegin');
    baidu(instance.getElement('container')).addClass('magic-combobox-container-setup');
    instance.on('change', function(event) {
        if (event.from == 'confirm') {
            //当用键盘选到初始值（最后一个值再往后选一个或第一个值往前选一个）
            //此时按回车，虽然出发onchange，但是原生select没有此值，无须改变。
            //by Dengping
            if (event.result.index) {
                el.options[event.result.index].selected = true;
            }
        } else if (event.from == 'blur') {
            var content = this.getElement('input').value;
            if (!newItemOpt) {
                newItemOpt = document.createElement('OPTION');
                newItemOpt.selected = true;
                el.add(newItemOpt);
            }
            newItemOpt.value = newItemOpt.text = content;
        }

    });

    instance.on('reload', function(event) {
        var data = event.data,
            length = data.length,
            i = 0;
        el.options.length = 0;
        for (; i < length; i ++) {
            var elmOption = document.createElement('OPTION');
            elmOption.value = data[i].value;
            elmOption.text = data[i].content;
            el.add(elmOption);
        }
    });

    //todo
    //当原生select有变动时，ComboBox不能同步。

    return instance;
};

/**
 * 从select中提取数据
 * @param {Node} selectNode
 * @return {Object} 数据对象
 */
function parseSelectOptions(selectNode) {
    var items = [],
        originIndex = -1,
        optionNodes = selectNode.options;
    baidu.each(optionNodes, function(index, item) {

        items.push({
            'value' : item.value,
            'content' : item.text
        });
        if (item.selected) {
            originIndex = index;
        }
    });
    return {
        'items' : items,
        'originIndex' : originIndex
    }
}

})();<|endoftext|>


<commit_before>/*
 * Copyright 2016 the original author or authors.
 *
 * Licensed 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 Vue = require('vue');

var CommentService = require('./services/comment-service.js');

var template = require('./list.html');

new Vue({
    el: '#platon-comment-thread',
    render: template.render,
    staticRenderFns: template.staticRenderFns,

    data: {
        loading: true,
        comments: []
    },

    components: {
        'platon-comment': require('./components/comment'),
        'platon-comment-form': require('./components/comment-form')
    },

    methods: {
        addComment: function(newComment) {
            this.comments.push(newComment);
        }
    },

    created: function () {
        var vm = this;
        CommentService.getComments(window.location.pathname)
            .then(function updateModel(commentsListResult) {
                vm.comments = commentsListResult.comments;
                vm.totalCommentCount = commentsListResult.totalCommentCount;
                vm.loading = false;
            })
            .catch(function displayError(reason) {
                alert(reason);
            });
    }
});
<commit_msg>Scroll to comment when URL links to it<commit_after>/*
 * Copyright 2016 the original author or authors.
 *
 * Licensed 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 Vue = require('vue');

var CommentService = require('./services/comment-service.js');

var template = require('./list.html');

new Vue({
    el: '#platon-comment-thread',
    render: template.render,
    staticRenderFns: template.staticRenderFns,

    data: {
        loading: true,
        comments: []
    },

    components: {
        'platon-comment': require('./components/comment'),
        'platon-comment-form': require('./components/comment-form')
    },

    methods: {
        addComment: function (newComment) {
            this.comments.push(newComment);
        }
    },

    created: function () {
        var vm = this;
        CommentService.getComments(window.location.pathname)
            .then(function updateModel(commentsListResult) {
                vm.comments = commentsListResult.comments;
                vm.totalCommentCount = commentsListResult.totalCommentCount;
                vm.loading = false;

                if (window.location.hash && window.location.hash.indexOf('#platon-comment-') >= 0) {
                    Vue.nextTick(function () {
                        var commentElem = document.querySelector(window.location.hash);
                        if (commentElem != null && !userHasScrolled) {
                            commentElem.scrollIntoView(true);
                        }
                    });
                }
            })
            .catch(function displayError(reason) {
                alert(reason);
            });
    }
});

var userHasScrolled = false;
window.addEventListener('scroll', scrollListener);
function scrollListener() {
    userHasScrolled = true;
    window.removeEventListener('scroll', scrollListener);
}
<|endoftext|>


<commit_before>/**
 * Fetches the login status from the server and creates a login link. If the
 * user is already logged in, the page redirects to the home page.
 */
async function getLoginUrl() {
  const response = await fetch('/login-status');
  const json = await response.json();

  // Redirect to the home page if the user is already logged in.
  if (json.loggedIn) {
    location.href = '/index.html';
    return;
  }

  const loginLink = document.getElementById('login-link');
  loginLink.href = json.loginUrl;
}
<commit_msg>Display login page body after authentication check.<commit_after>/**
 * Fetches the login status from the server and creates a login link. If the
 * user is already logged in, the page redirects to the home page.
 */
async function getLoginUrl() {
  const response = await fetch('/login-status');
  const json = await response.json();

  // Redirect to the home page if the user is already logged in.
  if (json.loggedIn) {
    location.href = '/index.html';
    return;
  }

  const loginLink = document.getElementById('login-link');
  loginLink.href = json.loginUrl;

  document.body.style.display = 'block';
}
<|endoftext|>