<commit_before>/* vuex-i18n defines the Vuexi18nPlugin to enable localization using a vuex
** module to store the translation information. Make sure to also include the
** file vuex-i18n-store.js to include a respective vuex module.
*/

import module from './vuex-i18n-store';
import plurals from './vuex-i18n-plurals';

// initialize the plugin object
let VuexI18nPlugin = {};

// internationalization plugin for vue js using vuex
VuexI18nPlugin.install = function install(Vue, store, config) {

	// TODO: remove this block for next major update (API break)
	if (typeof arguments[2] === 'string' || typeof arguments[3] === 'string') {
		console.warn('VuexI18nPlugin: Registering the plugin with a string for `moduleName` or `identifiers` is deprecated. Use a configuration object instead.', 'https://github.com/dkfbasel/vuex-i18n#setup');
		config = {
			moduleName: arguments[2],
			identifiers: arguments[3]
		};
	}

	// merge default options with user supplied options
	let mergedConfig = Object.assign({
		moduleName: 'i18n',
		identifiers: ['{', '}'],
		onTranslationNotFound: function() {}
	}, config);

	// define module name and identifiers as constants to prevent any changes
	const moduleName = mergedConfig.moduleName;
	const identifiers = mergedConfig.identifiers;

	// initialize the onTranslationNotFound function and make sure it is actually
	// a function
	let onTranslationNotFound = mergedConfig.onTranslationNotFound;
	if (typeof onTranslationNotFound !== 'function') {
		console.error('i18n config option onTranslationNotFound must be a function');
		onTranslationNotFound = function() {};
	}

	// register the i18n module in the vuex store
	store.registerModule(moduleName, module, {preserveState: true});

	// check if the plugin was correctly initialized
	if (store.state.hasOwnProperty(moduleName) === false) {
		console.error('i18n vuex module is not correctly initialized. Please check the module name:', moduleName);

		// always return the key if module is not initialized correctly
		Vue.prototype.$i18n = function(key) {
			return key;
		};

		Vue.prototype.$getLanguage = function() {
			return null;
		};

		Vue.prototype.$setLanguage = function() {
			console.error('i18n vuex module is not correctly initialized');
		};

		return;
	};

	// initialize the replacement function
	let render = renderFn(identifiers);

	// get localized string from store. note that we pass the arguments passed
	// to the function directly to the translateInLanguage function
	let translate = function $t() {

		// get the current language from the store
		let locale = store.state[moduleName].locale;

		return translateInLanguage(locale, ...arguments);
	};

	// get localized string from store in a given language if available.
	// there are two possible signatures for the function.
	// we will check the arguments to make up the options passed.
	// 1: locale, key, options, pluralization
	// 2: locale, key, defaultValue, options, pluralization
	let translateInLanguage = function translateInLanguage(locale) {

		// read the function arguments
		let args = arguments;

		// initialize options
		let key = '';
		let defaultValue = '';
		let options = {};
		let pluralization = null;

		let count = args.length;

		// check if a default value was specified and fill options accordingly
		if (count >= 3 && typeof args[2] === 'string') {

			key = args[1];
			defaultValue = args[2];

			if (count > 3) {
				options = args[3];
			}

			if (count > 4) {
				pluralization = args[4];
			}

		} else {

			key = args[1];

			// default value was not specified and is therefore the same as the key
			defaultValue = key;

			if (count > 2) {
				options = args[2];
			}

			if (count > 3) {
				pluralization = args[3];
			}

		}

		// return the default value if the locale is not set (could happen on initialization)
		if (!locale) {
			console.warn('i18n locale is not set when trying to access translations:', key);
			return defaultValue;
		}

		// get the translations from the store
		let translations = store.state[moduleName].translations;

		// get the last resort fallback from the store
		let fallback = store.state[moduleName].fallback;

		// split locale by - to support partial fallback for regional locales
		// like de-CH, en-UK
		let localeRegional = locale.split('-');

		// flag for translation to exist or not
		let translationExists = true;

		// check if the language exists in the store. return the key if not
		if (translations.hasOwnProperty(locale) === false ) {
			translationExists = false;

		// check if the key exists in the store. return the key if not
		} else if (translations[locale].hasOwnProperty(key) === false) {
			translationExists = false;
		}

		// return the value from the store
		if (translationExists === true) {
			return render(locale, translations[locale][key], options, pluralization);
		}

		// check if a regional locale translation would be available for the key
		// i.e. de for de-CH
		if (localeRegional.length > 1 &&
			translations.hasOwnProperty(localeRegional[0]) === true &&
			translations[localeRegional[0]].hasOwnProperty(key) === true) {
			return render(localeRegional[0], translations[localeRegional[0]][key], options, pluralization);
		}

		// invoke a method if a translation is not found
		let asyncTranslation = onTranslationNotFound(locale, key);

		// resolve async translations by updating the store
		if (asyncTranslation) {
			Promise.resolve(asyncTranslation).then((value) => {
				let additionalTranslations = {};
				additionalTranslations[key] = value;
				addLocale(locale, additionalTranslations);
			});
		}

		// check if a vaild fallback exists in the store.
		// return the default value if not
		if (translations.hasOwnProperty(fallback) === false ) {
			return render(locale, defaultValue, options, pluralization);
		}

		// check if the key exists in the fallback locale in the store.
		// return the default value if not
		if (translations[fallback].hasOwnProperty(key) === false) {
			return render(fallback, defaultValue, options, pluralization);
		}

		return render(locale, translations[fallback][key], options, pluralization);

	};

	// check if the given key exists in the current locale
	let checkKeyExists = function checkKeyExists(key, scope = 'fallback') {

		// get the current language from the store
		let locale = store.state[moduleName].locale;
		let fallback = store.state[moduleName].fallback;
		let translations = store.state[moduleName].translations;

		// check the current translation
		if (translations.hasOwnProperty(locale) && translations[locale].hasOwnProperty(key)) {
			return true;
		}

		if (scope == 'strict') {
			return false;
		}

		// check any localized translations
		let localeRegional = locale.split('-');

		if (localeRegional.length > 1 &&
			translations.hasOwnProperty(localeRegional[0]) &&
			translations[localeRegional[0]].hasOwnProperty(key)) {
			return true;
		}

		if (scope == 'locale') {
			return false;
		}

		// check if a fallback locale exists
		if (translations.hasOwnProperty(fallback) && translations[fallback].hasOwnProperty(key)) {
			return true;
		}

		// key does not exist in the store
		return false;
	};

	// set fallback locale
	let setFallbackLocale = function setFallbackLocale(locale) {
		store.dispatch({
			type: `${moduleName}/setFallbackLocale`,
			locale: locale
		});
	};

	// set the current locale
	let setLocale = function setLocale(locale) {
		store.dispatch({
			type: `${moduleName}/setLocale`,
			locale: locale
		});
	};

	// get the current locale
	let getLocale = function getLocale() {
		return store.state[moduleName].locale;
	};

	// add predefined translations to the store (keeping existing information)
	let addLocale = function addLocale(locale, translations) {
		return store.dispatch({
			type: `${moduleName}/addLocale`,
			locale: locale,
			translations: translations
		});
	};

	// replace all locale information in the store
	let replaceLocale = function replaceLocale(locale, translations) {
		return store.dispatch({
			type: `${moduleName}/replaceLocale`,
			locale: locale,
			translations: translations
		});
	};

	// remove the givne locale from the store
	let removeLocale = function removeLocale(locale) {
		if (store.state[moduleName].translations.hasOwnProperty(locale)) {
			store.dispatch({
				type: `${moduleName}/removeLocale`,
				locale: locale
			});
		}
	};

	// we are phasing out the exists function
	let phaseOutExistsFn = function phaseOutExistsFn(locale) {
		console.warn('$i18n.exists is depreceated. Please use $i18n.localeExists instead. It provides exatly the same functionality.');
		return checkLocaleExists(locale);
	};

	// check if the given locale is already loaded
	let checkLocaleExists = function checkLocaleExists(locale) {
		return store.state[moduleName].translations.hasOwnProperty(locale);
	};

	// register vue prototype methods
	Vue.prototype.$i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		replace: replaceLocale,
		remove: removeLocale,
		fallback: setFallbackLocale,
		localeExists: checkLocaleExists,
		keyExists: checkKeyExists,

		exists: phaseOutExistsFn
	};

	// register global methods
	Vue.i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		replace: replaceLocale,
		remove: removeLocale,
		fallback: setFallbackLocale,
		translate: translate,
		translateIn: translateInLanguage,
		localeExists: checkLocaleExists,
		keyExists: checkKeyExists,

		exists: phaseOutExistsFn
	};

	// register the translation function on the vue instance
	Vue.prototype.$t = translate;

	// register the specific language translation function on the vue instance
	Vue.prototype.$tlang = translateInLanguage;

	// register a filter function for translations
	Vue.filter('translate', translate);

};

// renderFn will initialize a function to render the variable substitutions in
// the translation string. identifiers specify the tags will be used to find
// variable substitutions, i.e. {test} or {{test}}, note that we are using a
// closure to avoid recompilation of the regular expression to match tags on
// every render cycle.
let renderFn = function(identifiers) {

	if (identifiers == null || identifiers.length != 2) {
		console.warn('You must specify the start and end character identifying variable substitutions');
	}

	// construct a regular expression ot find variable substitutions, i.e. {test}
	let matcher = new RegExp('' + identifiers[0] + '\\w+' + identifiers[1], 'g');

	// define the replacement function
	let replace = function replace(translation, replacements, warn = true) {

		// check if the object has a replace property
		if (!translation.replace) {
			return translation;
		}

		return translation.replace(matcher, function(placeholder) {

			// remove the identifiers (can be set on the module level)
			let key = placeholder.replace(identifiers[0], '').replace(identifiers[1], '');

			if (replacements[key] !== undefined) {
				return replacements[key];
			}

			// warn user that the placeholder has not been found
			if (warn === true) {
				console.group ? console.group('Not all placeholders found') : console.warn('Not all placeholders found');
				console.warn('Text:', translation);
				console.warn('Placeholder:', placeholder);
				if(console.groupEnd) {
					console.groupEnd();
				}
			}

			// return the original placeholder
			return placeholder;
		});
	};

	// the render function will replace variable substitutions and prepare the
	// translations for rendering
	let render = function render(locale, translation, replacements = {}, pluralization = null) {
		// get the type of the property
		let objType = typeof translation;
		let pluralizationType = typeof pluralization;

		let replacedText = function() {

			if (isArray(translation)) {

				// replace the placeholder elements in all sub-items
				return translation.map((item) => {
					return replace(item, replacements, false);
				});

			} else if (objType === 'string') {
				return replace(translation, replacements, true);
			}

		};

			// return translation item directly
		if (pluralization === null) {
			return replacedText();
		}

		// check if pluralization value is countable
		if (pluralizationType !== 'number') {
			console.warn('pluralization is not a number');
			return replacedText();
		}

		// check for pluralization and return the correct part of the string
		let translatedText = replacedText().split(':::');
		let index = plurals.getTranslationIndex(locale, pluralization);

		if(typeof translatedText[index] === 'undefined') {
			console.warn('no pluralized translation provided in ', translation);
			return translatedText[0].trim();
		}
		else {
			return translatedText[index].trim();
		}
	};

	// return the render function to the caller
	return render;

};

// check if the given object is an array
function isArray(obj) {
	return !!obj && Array === obj.constructor;
}

export default VuexI18nPlugin;
<commit_msg>added defaultValue into onTranslationNotFound method<commit_after>/* vuex-i18n defines the Vuexi18nPlugin to enable localization using a vuex
** module to store the translation information. Make sure to also include the
** file vuex-i18n-store.js to include a respective vuex module.
*/

import module from './vuex-i18n-store';
import plurals from './vuex-i18n-plurals';

// initialize the plugin object
let VuexI18nPlugin = {};

// internationalization plugin for vue js using vuex
VuexI18nPlugin.install = function install(Vue, store, config) {

	// TODO: remove this block for next major update (API break)
	if (typeof arguments[2] === 'string' || typeof arguments[3] === 'string') {
		console.warn('VuexI18nPlugin: Registering the plugin with a string for `moduleName` or `identifiers` is deprecated. Use a configuration object instead.', 'https://github.com/dkfbasel/vuex-i18n#setup');
		config = {
			moduleName: arguments[2],
			identifiers: arguments[3]
		};
	}

	// merge default options with user supplied options
	let mergedConfig = Object.assign({
		moduleName: 'i18n',
		identifiers: ['{', '}'],
		onTranslationNotFound: function() {}
	}, config);

	// define module name and identifiers as constants to prevent any changes
	const moduleName = mergedConfig.moduleName;
	const identifiers = mergedConfig.identifiers;

	// initialize the onTranslationNotFound function and make sure it is actually
	// a function
	let onTranslationNotFound = mergedConfig.onTranslationNotFound;
	if (typeof onTranslationNotFound !== 'function') {
		console.error('i18n config option onTranslationNotFound must be a function');
		onTranslationNotFound = function() {};
	}

	// register the i18n module in the vuex store
	store.registerModule(moduleName, module, {preserveState: true});

	// check if the plugin was correctly initialized
	if (store.state.hasOwnProperty(moduleName) === false) {
		console.error('i18n vuex module is not correctly initialized. Please check the module name:', moduleName);

		// always return the key if module is not initialized correctly
		Vue.prototype.$i18n = function(key) {
			return key;
		};

		Vue.prototype.$getLanguage = function() {
			return null;
		};

		Vue.prototype.$setLanguage = function() {
			console.error('i18n vuex module is not correctly initialized');
		};

		return;
	};

	// initialize the replacement function
	let render = renderFn(identifiers);

	// get localized string from store. note that we pass the arguments passed
	// to the function directly to the translateInLanguage function
	let translate = function $t() {

		// get the current language from the store
		let locale = store.state[moduleName].locale;

		return translateInLanguage(locale, ...arguments);
	};

	// get localized string from store in a given language if available.
	// there are two possible signatures for the function.
	// we will check the arguments to make up the options passed.
	// 1: locale, key, options, pluralization
	// 2: locale, key, defaultValue, options, pluralization
	let translateInLanguage = function translateInLanguage(locale) {

		// read the function arguments
		let args = arguments;

		// initialize options
		let key = '';
		let defaultValue = '';
		let options = {};
		let pluralization = null;

		let count = args.length;

		// check if a default value was specified and fill options accordingly
		if (count >= 3 && typeof args[2] === 'string') {

			key = args[1];
			defaultValue = args[2];

			if (count > 3) {
				options = args[3];
			}

			if (count > 4) {
				pluralization = args[4];
			}

		} else {

			key = args[1];

			// default value was not specified and is therefore the same as the key
			defaultValue = key;

			if (count > 2) {
				options = args[2];
			}

			if (count > 3) {
				pluralization = args[3];
			}

		}

		// return the default value if the locale is not set (could happen on initialization)
		if (!locale) {
			console.warn('i18n locale is not set when trying to access translations:', key);
			return defaultValue;
		}

		// get the translations from the store
		let translations = store.state[moduleName].translations;

		// get the last resort fallback from the store
		let fallback = store.state[moduleName].fallback;

		// split locale by - to support partial fallback for regional locales
		// like de-CH, en-UK
		let localeRegional = locale.split('-');

		// flag for translation to exist or not
		let translationExists = true;

		// check if the language exists in the store. return the key if not
		if (translations.hasOwnProperty(locale) === false ) {
			translationExists = false;

		// check if the key exists in the store. return the key if not
		} else if (translations[locale].hasOwnProperty(key) === false) {
			translationExists = false;
		}

		// return the value from the store
		if (translationExists === true) {
			return render(locale, translations[locale][key], options, pluralization);
		}

		// check if a regional locale translation would be available for the key
		// i.e. de for de-CH
		if (localeRegional.length > 1 &&
			translations.hasOwnProperty(localeRegional[0]) === true &&
			translations[localeRegional[0]].hasOwnProperty(key) === true) {
			return render(localeRegional[0], translations[localeRegional[0]][key], options, pluralization);
		}

		// invoke a method if a translation is not found
		let asyncTranslation = onTranslationNotFound(locale, key, defaultValue);

		// resolve async translations by updating the store
		if (asyncTranslation) {
			Promise.resolve(asyncTranslation).then((value) => {
				let additionalTranslations = {};
				additionalTranslations[key] = value;
				addLocale(locale, additionalTranslations);
			});
		}

		// check if a vaild fallback exists in the store.
		// return the default value if not
		if (translations.hasOwnProperty(fallback) === false ) {
			return render(locale, defaultValue, options, pluralization);
		}

		// check if the key exists in the fallback locale in the store.
		// return the default value if not
		if (translations[fallback].hasOwnProperty(key) === false) {
			return render(fallback, defaultValue, options, pluralization);
		}

		return render(locale, translations[fallback][key], options, pluralization);

	};

	// check if the given key exists in the current locale
	let checkKeyExists = function checkKeyExists(key, scope = 'fallback') {

		// get the current language from the store
		let locale = store.state[moduleName].locale;
		let fallback = store.state[moduleName].fallback;
		let translations = store.state[moduleName].translations;

		// check the current translation
		if (translations.hasOwnProperty(locale) && translations[locale].hasOwnProperty(key)) {
			return true;
		}

		if (scope == 'strict') {
			return false;
		}

		// check any localized translations
		let localeRegional = locale.split('-');

		if (localeRegional.length > 1 &&
			translations.hasOwnProperty(localeRegional[0]) &&
			translations[localeRegional[0]].hasOwnProperty(key)) {
			return true;
		}

		if (scope == 'locale') {
			return false;
		}

		// check if a fallback locale exists
		if (translations.hasOwnProperty(fallback) && translations[fallback].hasOwnProperty(key)) {
			return true;
		}

		// key does not exist in the store
		return false;
	};

	// set fallback locale
	let setFallbackLocale = function setFallbackLocale(locale) {
		store.dispatch({
			type: `${moduleName}/setFallbackLocale`,
			locale: locale
		});
	};

	// set the current locale
	let setLocale = function setLocale(locale) {
		store.dispatch({
			type: `${moduleName}/setLocale`,
			locale: locale
		});
	};

	// get the current locale
	let getLocale = function getLocale() {
		return store.state[moduleName].locale;
	};

	// add predefined translations to the store (keeping existing information)
	let addLocale = function addLocale(locale, translations) {
		return store.dispatch({
			type: `${moduleName}/addLocale`,
			locale: locale,
			translations: translations
		});
	};

	// replace all locale information in the store
	let replaceLocale = function replaceLocale(locale, translations) {
		return store.dispatch({
			type: `${moduleName}/replaceLocale`,
			locale: locale,
			translations: translations
		});
	};

	// remove the givne locale from the store
	let removeLocale = function removeLocale(locale) {
		if (store.state[moduleName].translations.hasOwnProperty(locale)) {
			store.dispatch({
				type: `${moduleName}/removeLocale`,
				locale: locale
			});
		}
	};

	// we are phasing out the exists function
	let phaseOutExistsFn = function phaseOutExistsFn(locale) {
		console.warn('$i18n.exists is depreceated. Please use $i18n.localeExists instead. It provides exatly the same functionality.');
		return checkLocaleExists(locale);
	};

	// check if the given locale is already loaded
	let checkLocaleExists = function checkLocaleExists(locale) {
		return store.state[moduleName].translations.hasOwnProperty(locale);
	};

	// register vue prototype methods
	Vue.prototype.$i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		replace: replaceLocale,
		remove: removeLocale,
		fallback: setFallbackLocale,
		localeExists: checkLocaleExists,
		keyExists: checkKeyExists,

		exists: phaseOutExistsFn
	};

	// register global methods
	Vue.i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		replace: replaceLocale,
		remove: removeLocale,
		fallback: setFallbackLocale,
		translate: translate,
		translateIn: translateInLanguage,
		localeExists: checkLocaleExists,
		keyExists: checkKeyExists,

		exists: phaseOutExistsFn
	};

	// register the translation function on the vue instance
	Vue.prototype.$t = translate;

	// register the specific language translation function on the vue instance
	Vue.prototype.$tlang = translateInLanguage;

	// register a filter function for translations
	Vue.filter('translate', translate);

};

// renderFn will initialize a function to render the variable substitutions in
// the translation string. identifiers specify the tags will be used to find
// variable substitutions, i.e. {test} or {{test}}, note that we are using a
// closure to avoid recompilation of the regular expression to match tags on
// every render cycle.
let renderFn = function(identifiers) {

	if (identifiers == null || identifiers.length != 2) {
		console.warn('You must specify the start and end character identifying variable substitutions');
	}

	// construct a regular expression ot find variable substitutions, i.e. {test}
	let matcher = new RegExp('' + identifiers[0] + '\\w+' + identifiers[1], 'g');

	// define the replacement function
	let replace = function replace(translation, replacements, warn = true) {

		// check if the object has a replace property
		if (!translation.replace) {
			return translation;
		}

		return translation.replace(matcher, function(placeholder) {

			// remove the identifiers (can be set on the module level)
			let key = placeholder.replace(identifiers[0], '').replace(identifiers[1], '');

			if (replacements[key] !== undefined) {
				return replacements[key];
			}

			// warn user that the placeholder has not been found
			if (warn === true) {
				console.group ? console.group('Not all placeholders found') : console.warn('Not all placeholders found');
				console.warn('Text:', translation);
				console.warn('Placeholder:', placeholder);
				if(console.groupEnd) {
					console.groupEnd();
				}
			}

			// return the original placeholder
			return placeholder;
		});
	};

	// the render function will replace variable substitutions and prepare the
	// translations for rendering
	let render = function render(locale, translation, replacements = {}, pluralization = null) {
		// get the type of the property
		let objType = typeof translation;
		let pluralizationType = typeof pluralization;

		let replacedText = function() {

			if (isArray(translation)) {

				// replace the placeholder elements in all sub-items
				return translation.map((item) => {
					return replace(item, replacements, false);
				});

			} else if (objType === 'string') {
				return replace(translation, replacements, true);
			}

		};

			// return translation item directly
		if (pluralization === null) {
			return replacedText();
		}

		// check if pluralization value is countable
		if (pluralizationType !== 'number') {
			console.warn('pluralization is not a number');
			return replacedText();
		}

		// check for pluralization and return the correct part of the string
		let translatedText = replacedText().split(':::');
		let index = plurals.getTranslationIndex(locale, pluralization);

		if(typeof translatedText[index] === 'undefined') {
			console.warn('no pluralized translation provided in ', translation);
			return translatedText[0].trim();
		}
		else {
			return translatedText[index].trim();
		}
	};

	// return the render function to the caller
	return render;

};

// check if the given object is an array
function isArray(obj) {
	return !!obj && Array === obj.constructor;
}

export default VuexI18nPlugin;
<|endoftext|>


<commit_before>(function(undefined,window,document,logFn,G){

  var helper = {

    "p3sf": function(x) {
      var s=Array('','k','M','G','T','P','E','Z','Y');
      var commas=0;
      while(x>1000){
        //console.log(x);
        commas+=1;x/=1000
      };
      //console.log("commas = "+commas);
      x=""+x;
      if(x.length>4){x=x.substring(0,4)};
      if(x[x.length-1]=="."){x=x.substring(0,x.length-1)};
      return x+s[commas];
    },

    "dur": function(sec) {
      s="";
      if(sec>=86400){var d=Math.floor(sec/86400); sec-=d*86400; s+=""+d+"day, "};
      if(sec>=3600){var h=Math.floor(sec/3600); sec-=h*3600; s+=""+h+"hr, "};
      if(sec>=60){var m=Math.floor(sec/60); sec-=m*60; s+=""+m+"min, "};
      if(sec>0 || s==""){s+=""+sec+"sec"};
      if(s[s.length-1]==" "){s=s.substring(0,s.length-2)};
      return s;
    },

    "advise": function(printer) {
      var p3sf = this.p3sf;
      var dur = this.dur;
      var log = (printer == undefined) ? logFn : printer;
      log("Current: "+p3sf(G.cookies)+" +"+p3sf(G.cookiesPs)+"/s (+"+
          p3sf(G.cookiesPs/60)+"/min, "+p3sf(G.cookiesPs/3600)+"/hr)");
      log("");
      var best=undefined;
      for(var i=0; i < G.ObjectsById.length; i++) {
        var o = G.ObjectsById[i];
        var oCps = o.cps();
        var opCps = o.price/oCps;
        log(o.name+" ("+p3sf(o.amount*oCps)+"/s total) -> +"+p3sf(oCps)+"/s => "+
            p3sf(opCps)+"/cps");
        if(o.price<G.cookies) {
          log(">     Can buy ~"+Math.floor(G.cookies/o.price)+" now for "+p3sf(o.price));
          if(best==undefined || opCps < best.pCps) {
            best = {pCps: opCps, o: o};
          }
        } else {
          log(">     Could buy one in "+
              dur(Math.ceil((o.price-G.cookies)/G.cookiesPs))+" for "+p3sf(o.price));
        }
      }
      if(best!=undefined) {
        log("");
        log("Best option now: "+best.o.name);
      }
      log("");
      for(var i=0; i < G.UpgradesInStore.length; i++) {
        var u = G.UpgradesInStore[i];
        var m = u.name+" (cost: "+p3sf(u.basePrice)+")";
        if(u.basePrice<G.cookies) {
          log(m + ", Can buy now.");
        } else {
          log(m + ", in "+ dur(Math.ceil((u.basePrice-G.cookies)/G.cookiesPs)));
        }
      }
    },

    "advicePopup": function() {
      var out = "";
      var printer = function(s) { out += "\n" + s; }
      this.advise(printer);
      alert(out);
    }
  };

  window.Helper = helper;

  var r=document.createElement("div");
  r.setAttribute('id','rHelper');
  r.innerHTML='';
  r.style.position='absolute';
  r.style.width=31;
  r.style.height=38;
  r.style.top='35px';
  r.style.right='20px';
  document.body.appendChild(r);

})(undefined,window,document,function(s){console.log(s)},Game);
<commit_msg>oops. current rate calculation was wrong for /min and /hr. added /day, too<commit_after>(function(undefined,window,document,logFn,G){

  var helper = {

    "p3sf": function(x) {
      var s=Array('','k','M','G','T','P','E','Z','Y');
      var commas=0;
      while(x>1000){
        //console.log(x);
        commas+=1;x/=1000
      };
      //console.log("commas = "+commas);
      x=""+x;
      if(x.length>4){x=x.substring(0,4)};
      if(x[x.length-1]=="."){x=x.substring(0,x.length-1)};
      return x+s[commas];
    },

    "dur": function(sec) {
      s="";
      if(sec>=86400){var d=Math.floor(sec/86400); sec-=d*86400; s+=""+d+"day, "};
      if(sec>=3600){var h=Math.floor(sec/3600); sec-=h*3600; s+=""+h+"hr, "};
      if(sec>=60){var m=Math.floor(sec/60); sec-=m*60; s+=""+m+"min, "};
      if(sec>0 || s==""){s+=""+sec+"sec"};
      if(s[s.length-1]==" "){s=s.substring(0,s.length-2)};
      return s;
    },

    "advise": function(printer) {
      var p3sf = this.p3sf;
      var dur = this.dur;
      var log = (printer == undefined) ? logFn : printer;
      log("Now: "+p3sf(G.cookies)+" +"+p3sf(G.cookiesPs)+"/s (+"+
          p3sf(G.cookiesPs*60)+"/min, "+p3sf(G.cookiesPs*3600)+"/hr, "+
          p3sf(G.cookiesPs*60)+"/day)");
      log("");
      var best=undefined;
      for(var i=0; i < G.ObjectsById.length; i++) {
        var o = G.ObjectsById[i];
        var oCps = o.cps();
        var opCps = o.price/oCps;
        log(o.name+" ("+p3sf(o.amount*oCps)+"/s total) -> +"+p3sf(oCps)+"/s => "+
            p3sf(opCps)+"/cps");
        if(o.price<G.cookies) {
          log(">     Can buy ~"+Math.floor(G.cookies/o.price)+" now for "+p3sf(o.price));
          if(best==undefined || opCps < best.pCps) {
            best = {pCps: opCps, o: o};
          }
        } else {
          log(">     Could buy one in "+
              dur(Math.ceil((o.price-G.cookies)/G.cookiesPs))+" for "+p3sf(o.price));
        }
      }
      if(best!=undefined) {
        log("");
        log("Best option now: "+best.o.name);
      }
      log("");
      for(var i=0; i < G.UpgradesInStore.length; i++) {
        var u = G.UpgradesInStore[i];
        var m = u.name+" (cost: "+p3sf(u.basePrice)+")";
        if(u.basePrice<G.cookies) {
          log(m + ", Can buy now.");
        } else {
          log(m + ", in "+ dur(Math.ceil((u.basePrice-G.cookies)/G.cookiesPs)));
        }
      }
    },

    "advicePopup": function() {
      var out = "";
      var printer = function(s) { out += "\n" + s; }
      this.advise(printer);
      alert(out);
    }
  };

  window.Helper = helper;

  var r=document.createElement("div");
  r.setAttribute('id','rHelper');
  r.innerHTML='';
  r.style.position='absolute';
  r.style.width=31;
  r.style.height=38;
  r.style.top='35px';
  r.style.right='20px';
  document.body.appendChild(r);

})(undefined,window,document,function(s){console.log(s)},Game);
<|endoftext|>


<commit_before>/* vuex-i18n defines the Vuexi18nPlugin to enable localization using a vuex
** module to store the translation information. Make sure to also include the
** file vuex-i18n-store.js to include a respective vuex module.
*/

import module from './vuex-i18n-store';

// initialize the plugin object
let VuexI18nPlugin = {};

// internationalization plugin for vue js using vuex
VuexI18nPlugin.install = function install(Vue, store, moduleName = 'i18n') {

	store.registerModule(moduleName, module)

	// check if the plugin was correctly initialized
	if (store.state.hasOwnProperty(moduleName) === false) {
		console.error('i18n vuex module is not correctly initialized. Please check the module name:', moduleName);

		// always return the key if module is not initialized correctly
		Vue.prototype.$i18n = function(key) {
			return key;
		};

		Vue.prototype.$getLanguage = function() {
			return null;
		};

		Vue.prototype.$setLanguage = function() {
			console.error('i18n vuex module is not correctly initialized');
		};

		return;
	};

	// get localized string from store
	let translate = function $t(key, options, pluralization) {

		// get the current language from the store
		let locale = store.state[moduleName].locale;

		return translateInLanguage(locale, key, options, pluralization);
	};

	// get localized string from store in a given language if available
	let translateInLanguage = function translateInLanguage(locale, key, options, pluralization) {

		// get the current language from the store
		let fallback = store.state[moduleName].fallback;
		let translations = store.state[moduleName].translations;

		// flag for translation to exist or not
		let translationExist = true;

		// check if the language exists in the store. return the key if not
		if (translations.hasOwnProperty(locale) === false ) {
			translationExist = false;

		// check if the key exists in the store. return the key if not
		} else if (translations[locale].hasOwnProperty(key) === false) {
			translationExist = false;
		}

		// return the value from the store
		if (translationExist === true) {
			return render(translations[locale][key], options, pluralization);
		}

		// check if a vaild fallback exists in the store. return the key if not
		if (translations.hasOwnProperty(fallback) === false ) {
			return render(key, options, pluralization);
		}

		// check if the key exists in the fallback in the store. return the key if not
		if (translations[fallback].hasOwnProperty(key) === false) {
			return render(key, options, pluralization);
		}

		return render(translations[fallback][key], options, pluralization);

	};

	// set fallback locale
	let setFallbackLocale = function setFallbackLocale(locale) {
		store.dispatch({
			type: 'setFallbackLocale',
			locale: locale
		});
	};

	// set the current locale
	let setLocale = function setLocale(locale) {
		store.dispatch({
			type: 'setLocale',
			locale: locale
		});
	};

	// get the current locale
	let getLocale = function getLocale() {
		return store.state[moduleName].locale;
	};

	// add predefined translations to the store
	let addLocale = function addLocale(locale, translations) {
		return store.dispatch({
			type: 'addLocale',
			locale: locale,
			translations: translations
		});
	};

	// remove the givne locale from the store
	let removeLocale = function removeLocale(locale) {
		if (store.state[moduleName].translations.hasOwnProperty(locale)) {
			store.dispatch({
				type: 'removeLocale',
				locale: locale
			});
		}
	};

	// check if the given locale is already loaded
	let checkLocaleExists = function checkLocaleExists(locale) {
		return store.state[moduleName].translations.hasOwnProperty(locale);
	};

	// register vue prototype methods
	Vue.prototype.$i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		remove: removeLocale,
		fallback: setFallbackLocale,
		exists: checkLocaleExists
	};

	// register global methods
	Vue.i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		remove: removeLocale,
		fallback: setFallbackLocale,
		exists: checkLocaleExists,
		translate: translate,
		translateIn: translateInLanguage
	};

	// register the translation function on the vue instance
	Vue.prototype.$t = translate;

	// register the specific language translation function on the vue instance
	Vue.prototype.$tlang = translateInLanguage;

	// register a filter function for translations
	Vue.filter('translate', translate);

};

// replace will replace the given replacements in the translation string
let replace = function replace(translation, replacements, warn=true) {

	// check if the object has a replace property
	if (!translation.replace) {
		return translation;
	}

	return translation.replace(/\{\w+\}/g, function(placeholder) {

		let key = placeholder.replace('{', '').replace('}', '');

		if (replacements[key] !== undefined) {
			return replacements[key];
		}

		// warn user that the placeholder has not been found
		if (warn === true) {
			console.group('Not all placeholder founds');
			console.warn('Text:', translation);
			console.warn('Placeholder:', placeholder);
			console.groupEnd();
		}

		// return the original placeholder
		return placeholder;
	});
};

// render will return the given translation object
let render = function render(translation, replacements = {}, pluralization = null) {

	// get the type of the property
	let objType = typeof translation;
	let pluralizationType = typeof pluralization;

	let replacedText = function() {

		if (isArray(translation)) {

			// replace the placeholder elements in all sub-items
			return translation.map((item) => {
				return replace(item, replacements, false);
			});

		} else if (objType === 'string') {
			return replace(translation, replacements);
		}

	};

		// return translation item directly
	if (pluralization === null) {
		return replacedText();
	}

	// check if pluralization value is countable
	if (pluralizationType !== 'number') {
		console.warn('pluralization is not a number');
		return replacedText();
	}

	// check for pluralization and return the correct part of the string
	let translatedText = replacedText().split(':::');

	// return the left side on singular, the right side for plural
	// 0 has plural notation
	if (pluralization === 1) {
		return translatedText[0].trim();
	}

	// use singular version for -1 as well
	if (pluralization === -1) {
		return translatedText[0].trim();
	}

	if (translatedText.length > 1) {
		return translatedText[1].trim();
	}

	console.warn('no pluralized translation provided in ', translation);
	return translatedText[0].trim();

};

// check if the given object is an array
function isArray(obj) {
	return !!obj && Array === obj.constructor;
}

export default VuexI18nPlugin;
<commit_msg>add missing semicolon<commit_after>/* vuex-i18n defines the Vuexi18nPlugin to enable localization using a vuex
** module to store the translation information. Make sure to also include the
** file vuex-i18n-store.js to include a respective vuex module.
*/

import module from './vuex-i18n-store';

// initialize the plugin object
let VuexI18nPlugin = {};

// internationalization plugin for vue js using vuex
VuexI18nPlugin.install = function install(Vue, store, moduleName = 'i18n') {

	store.registerModule(moduleName, module);

	// check if the plugin was correctly initialized
	if (store.state.hasOwnProperty(moduleName) === false) {
		console.error('i18n vuex module is not correctly initialized. Please check the module name:', moduleName);

		// always return the key if module is not initialized correctly
		Vue.prototype.$i18n = function(key) {
			return key;
		};

		Vue.prototype.$getLanguage = function() {
			return null;
		};

		Vue.prototype.$setLanguage = function() {
			console.error('i18n vuex module is not correctly initialized');
		};

		return;
	};

	// get localized string from store
	let translate = function $t(key, options, pluralization) {

		// get the current language from the store
		let locale = store.state[moduleName].locale;

		return translateInLanguage(locale, key, options, pluralization);
	};

	// get localized string from store in a given language if available
	let translateInLanguage = function translateInLanguage(locale, key, options, pluralization) {

		// get the current language from the store
		let fallback = store.state[moduleName].fallback;
		let translations = store.state[moduleName].translations;

		// flag for translation to exist or not
		let translationExist = true;

		// check if the language exists in the store. return the key if not
		if (translations.hasOwnProperty(locale) === false ) {
			translationExist = false;

		// check if the key exists in the store. return the key if not
		} else if (translations[locale].hasOwnProperty(key) === false) {
			translationExist = false;
		}

		// return the value from the store
		if (translationExist === true) {
			return render(translations[locale][key], options, pluralization);
		}

		// check if a vaild fallback exists in the store. return the key if not
		if (translations.hasOwnProperty(fallback) === false ) {
			return render(key, options, pluralization);
		}

		// check if the key exists in the fallback in the store. return the key if not
		if (translations[fallback].hasOwnProperty(key) === false) {
			return render(key, options, pluralization);
		}

		return render(translations[fallback][key], options, pluralization);

	};

	// set fallback locale
	let setFallbackLocale = function setFallbackLocale(locale) {
		store.dispatch({
			type: 'setFallbackLocale',
			locale: locale
		});
	};

	// set the current locale
	let setLocale = function setLocale(locale) {
		store.dispatch({
			type: 'setLocale',
			locale: locale
		});
	};

	// get the current locale
	let getLocale = function getLocale() {
		return store.state[moduleName].locale;
	};

	// add predefined translations to the store
	let addLocale = function addLocale(locale, translations) {
		return store.dispatch({
			type: 'addLocale',
			locale: locale,
			translations: translations
		});
	};

	// remove the givne locale from the store
	let removeLocale = function removeLocale(locale) {
		if (store.state[moduleName].translations.hasOwnProperty(locale)) {
			store.dispatch({
				type: 'removeLocale',
				locale: locale
			});
		}
	};

	// check if the given locale is already loaded
	let checkLocaleExists = function checkLocaleExists(locale) {
		return store.state[moduleName].translations.hasOwnProperty(locale);
	};

	// register vue prototype methods
	Vue.prototype.$i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		remove: removeLocale,
		fallback: setFallbackLocale,
		exists: checkLocaleExists
	};

	// register global methods
	Vue.i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		remove: removeLocale,
		fallback: setFallbackLocale,
		exists: checkLocaleExists,
		translate: translate,
		translateIn: translateInLanguage
	};

	// register the translation function on the vue instance
	Vue.prototype.$t = translate;

	// register the specific language translation function on the vue instance
	Vue.prototype.$tlang = translateInLanguage;

	// register a filter function for translations
	Vue.filter('translate', translate);

};

// replace will replace the given replacements in the translation string
let replace = function replace(translation, replacements, warn=true) {

	// check if the object has a replace property
	if (!translation.replace) {
		return translation;
	}

	return translation.replace(/\{\w+\}/g, function(placeholder) {

		let key = placeholder.replace('{', '').replace('}', '');

		if (replacements[key] !== undefined) {
			return replacements[key];
		}

		// warn user that the placeholder has not been found
		if (warn === true) {
			console.group('Not all placeholder founds');
			console.warn('Text:', translation);
			console.warn('Placeholder:', placeholder);
			console.groupEnd();
		}

		// return the original placeholder
		return placeholder;
	});
};

// render will return the given translation object
let render = function render(translation, replacements = {}, pluralization = null) {

	// get the type of the property
	let objType = typeof translation;
	let pluralizationType = typeof pluralization;

	let replacedText = function() {

		if (isArray(translation)) {

			// replace the placeholder elements in all sub-items
			return translation.map((item) => {
				return replace(item, replacements, false);
			});

		} else if (objType === 'string') {
			return replace(translation, replacements);
		}

	};

		// return translation item directly
	if (pluralization === null) {
		return replacedText();
	}

	// check if pluralization value is countable
	if (pluralizationType !== 'number') {
		console.warn('pluralization is not a number');
		return replacedText();
	}

	// check for pluralization and return the correct part of the string
	let translatedText = replacedText().split(':::');

	// return the left side on singular, the right side for plural
	// 0 has plural notation
	if (pluralization === 1) {
		return translatedText[0].trim();
	}

	// use singular version for -1 as well
	if (pluralization === -1) {
		return translatedText[0].trim();
	}

	if (translatedText.length > 1) {
		return translatedText[1].trim();
	}

	console.warn('no pluralized translation provided in ', translation);
	return translatedText[0].trim();

};

// check if the given object is an array
function isArray(obj) {
	return !!obj && Array === obj.constructor;
}

export default VuexI18nPlugin;
<|endoftext|>


<commit_before>// Copyright (c) 2018, Compiler Explorer Authors
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

const exec = require('../exec'),
    _ = require('underscore'),
    logger = require('../logger').logger;

class Formatter {
    constructor(ceProps) {
        this.tools = {};
        this.ceProps = ceProps;
        const formatters = _.compact(ceProps('formatters', "").split(':'));
        _.each(formatters, this.fetchToolInfo.bind(this));
    }

    fetchToolInfo(formatter) {
        const exe = this.ceProps(`formatter.${formatter}.exe`);
        if (!exe) {
            logger.warn(`Formatter ${formatter} does not have a valid exe. Skipping...`);
            return;
        }
        const versionArg = this.ceProps(`formatter.${formatter}.version`, '--version');
        const versionRe = this.ceProps(`formatter.${formatter}.versionRe`, '.*');
        this.run(exe, [versionArg], {})
            .then(result => {
                const match = result.stdout.match(versionRe);
                this.tools[formatter] = {
                    exe: exe,
                    version: match ? match[0] : result.stdout,
                    name: this.ceProps(`formatter.${formatter}.name`, exe),
                    styles: this.ceProps(`formatter.${formatter}.styles`, "").split(':')
                };
            });
    }

    supportsStyle(tool, style) {
        return tool.styles.includes(style);
    }

    validateFormatRequest(req, res) {
        let requestedTool = this.tools[req.params.tool];
        if (!requestedTool) {
            res.status(422); // Unprocessable Entity
            res.end(JSON.stringify({
                exit: 2,
                answer: "Tool not supported"
            }));
            return false;
        }
        // Only clang supported for now
        if (!req.body || !req.body.source) {
            res.end(JSON.stringify({
                exit: 0,
                answer: ""
            }));
            return false;
        }
        // Hardcoded supported clang-format base styles.
        // Will need a bit of work if we want to support other tools!
        if (!this.supportsStyle(requestedTool, req.body.base)) {
            res.status(422); // Unprocessable Entity
            res.end(JSON.stringify({
                exit: 3,
                answer: "Base style not supported"
            }));
            return false;
        }
        return true;
    }

    formatHandler(req, res) {
        res.set('Content-Type', 'application/json');
        if (!this.validateFormatRequest(req, res)) return;
        this.formatCode(this.tools[req.params.tool], [`-style=${req.body.base}`], {input: req.body.source})
            .then(result => {
                res.end(JSON.stringify({
                    exit: result.code,
                    answer: result.stdout || "",
                }));
            })
            .catch(ex => {
                // Unexpected problem when running the formatter
                res.status(500);
                res.end(JSON.stringify({
                    exit: 1,
                    thrown: true,
                    answer: ex.message || "Internal server error"
                }));

            });
    }

    run(exe, args, options) {
        return exec.execute(exe, args, options);
    }

    formatCode(tool, args, options) {
        return this.run(tool.exe, args, options);
    }

    listHandler(req, res) {
        res.setHeader('Content-Type', 'application/json');
        const response = [];
        _.each(this.tools, tool => {
            response.push({name: tool.name, version: tool.version});
        });
        return res.end(JSON.stringify(response));
    }
}

module.exports = Formatter;
<commit_msg>Handle uncaught promise on formatter version error<commit_after>// Copyright (c) 2018, Compiler Explorer Authors
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

const exec = require('../exec'),
    _ = require('underscore'),
    logger = require('../logger').logger;

class Formatter {
    constructor(ceProps) {
        this.tools = {};
        this.ceProps = ceProps;
        const formatters = _.compact(ceProps('formatters', "").split(':'));
        _.each(formatters, this.fetchToolInfo.bind(this));
    }

    fetchToolInfo(formatter) {
        const exe = this.ceProps(`formatter.${formatter}.exe`);
        if (!exe) {
            logger.warn(`Formatter ${formatter} does not have a valid executable. Skipping...`);
            return;
        }
        const versionArg = this.ceProps(`formatter.${formatter}.version`, '--version');
        const versionRe = this.ceProps(`formatter.${formatter}.versionRe`, '.*');
        this.run(exe, [versionArg], {})
            .then(result => {
                const match = result.stdout.match(versionRe);
                this.tools[formatter] = {
                    exe: exe,
                    version: match ? match[0] : result.stdout,
                    name: this.ceProps(`formatter.${formatter}.name`, exe),
                    styles: this.ceProps(`formatter.${formatter}.styles`, "").split(':')
                };
            })
            .catch(logger.warn); // Log things like ENOENT errors as warnings
    }

    supportsStyle(tool, style) {
        return tool.styles.includes(style);
    }

    validateFormatRequest(req, res) {
        let requestedTool = this.tools[req.params.tool];
        if (!requestedTool) {
            res.status(422); // Unprocessable Entity
            res.end(JSON.stringify({
                exit: 2,
                answer: "Tool not supported"
            }));
            return false;
        }
        // Only clang supported for now
        if (!req.body || !req.body.source) {
            res.end(JSON.stringify({
                exit: 0,
                answer: ""
            }));
            return false;
        }
        // Hardcoded supported clang-format base styles.
        // Will need a bit of work if we want to support other tools!
        if (!this.supportsStyle(requestedTool, req.body.base)) {
            res.status(422); // Unprocessable Entity
            res.end(JSON.stringify({
                exit: 3,
                answer: "Base style not supported"
            }));
            return false;
        }
        return true;
    }

    formatHandler(req, res) {
        res.set('Content-Type', 'application/json');
        if (!this.validateFormatRequest(req, res)) return;
        this.formatCode(this.tools[req.params.tool], [`-style=${req.body.base}`], {input: req.body.source})
            .then(result => {
                res.end(JSON.stringify({
                    exit: result.code,
                    answer: result.stdout || "",
                }));
            })
            .catch(ex => {
                // Unexpected problem when running the formatter
                res.status(500);
                res.end(JSON.stringify({
                    exit: 1,
                    thrown: true,
                    answer: ex.message || "Internal server error"
                }));

            });
    }

    run(exe, args, options) {
        return exec.execute(exe, args, options);
    }

    formatCode(tool, args, options) {
        return this.run(tool.exe, args, options);
    }

    listHandler(req, res) {
        res.setHeader('Content-Type', 'application/json');
        const response = [];
        _.each(this.tools, tool => {
            response.push({name: tool.name, version: tool.version});
        });
        return res.end(JSON.stringify(response));
    }
}

module.exports = Formatter;
<|endoftext|>


<commit_before>import HTMLOperation from "./HTMLOperation.js";
import Sortable from "sortablejs";

/**
 * Waiter to handle events related to the recipe.
 *
 * @author n1474335 [n1474335@gmail.com]
 * @copyright Crown Copyright 2016
 * @license Apache-2.0
 *
 * @constructor
 * @param {App} app - The main view object for CyberChef.
 * @param {Manager} manager - The CyberChef event manager.
 */
var RecipeWaiter = function(app, manager) {
    this.app = app;
    this.manager = manager;
    this.removeIntent = false;
};

/**
 * Sets up the drag and drop capability for operations in the operations and recipe areas.
 */
RecipeWaiter.prototype.initialiseOperationDragNDrop = function() {
    var recList = document.getElementById("rec-list");

    // Recipe list
    Sortable.create(recList, {
        group: "recipe",
        sort: true,
        animation: 0,
        delay: 0,
        filter: ".arg-input,.arg",
        preventOnFilter: false,
        setData: function(dataTransfer, dragEl) {
            dataTransfer.setData("Text", dragEl.querySelector(".arg-title").textContent);
        },
        onEnd: function(evt) {
            if (this.removeIntent) {
                evt.item.remove();
                evt.target.dispatchEvent(this.manager.operationremove);
            }
        }.bind(this)
    });

    Sortable.utils.on(recList, "dragover", function() {
        this.removeIntent = false;
    }.bind(this));

    Sortable.utils.on(recList, "dragleave", function() {
        this.removeIntent = true;
        this.app.progress = 0;
    }.bind(this));

    Sortable.utils.on(recList, "touchend", function(e) {
        var loc = e.changedTouches[0],
            target = document.elementFromPoint(loc.clientX, loc.clientY);

        this.removeIntent = !recList.contains(target);
    }.bind(this));

    // Favourites category
    document.querySelector("#categories a").addEventListener("dragover", this.favDragover.bind(this));
    document.querySelector("#categories a").addEventListener("dragleave", this.favDragleave.bind(this));
    document.querySelector("#categories a").addEventListener("drop", this.favDrop.bind(this));
};

/**
 * Creates a drag-n-droppable seed list of operations.
 *
 * @param {element} listEl - The list the initialise
 */
RecipeWaiter.prototype.createSortableSeedList = function(listEl) {
    Sortable.create(listEl, {
        group: {
            name: "recipe",
            pull: "clone",
            put: false
        },
        sort: false,
        setData: function(dataTransfer, dragEl) {
            dataTransfer.setData("Text", dragEl.textContent);
        },
        onStart: function(evt) {
            $(evt.item).popover("destroy");
            evt.item.setAttribute("data-toggle", "popover-disabled");
        },
        onEnd: this.opSortEnd.bind(this)
    });
};

/**
 * Handler for operation sort end events.
 * Removes the operation from the list if it has been dropped outside. If not, adds it to the list
 * at the appropriate place and initialises it.
 *
 * @fires Manager#operationadd
 * @param {event} evt
 */
RecipeWaiter.prototype.opSortEnd = function(evt) {
    if (this.removeIntent) {
        if (evt.item.parentNode.id === "rec-list") {
            evt.item.remove();
        }
        return;
    }

    // Reinitialise the popover on the original element in the ops list because for some reason it
    // gets destroyed and recreated.
    $(evt.clone).popover();
    $(evt.clone).children("[data-toggle=popover]").popover();

    if (evt.item.parentNode.id !== "rec-list") {
        return;
    }

    this.buildRecipeOperation(evt.item);
    evt.item.dispatchEvent(this.manager.operationadd);
};

/**
 * Handler for favourite dragover events.
 * If the element being dragged is an operation, displays a visual cue so that the user knows it can
 * be dropped here.
 *
 * @param {event} e
 */
RecipeWaiter.prototype.favDragover = function(e) {
    if (e.dataTransfer.effectAllowed !== "move")
        return false;

    e.stopPropagation();
    e.preventDefault();
    if (e.target.className && e.target.className.indexOf("category-title") > -1) {
        // Hovering over the a
        e.target.classList.add("favourites-hover");
    } else if (e.target.parentNode.className && e.target.parentNode.className.indexOf("category-title") > -1) {
        // Hovering over the Edit button
        e.target.parentNode.classList.add("favourites-hover");
    } else if (e.target.parentNode.parentNode.className && e.target.parentNode.parentNode.className.indexOf("category-title") > -1) {
        // Hovering over the image on the Edit button
        e.target.parentNode.parentNode.classList.add("favourites-hover");
    }
};

/**
 * Handler for favourite dragleave events.
 * Removes the visual cue.
 *
 * @param {event} e
 */
RecipeWaiter.prototype.favDragleave = function(e) {
    e.stopPropagation();
    e.preventDefault();
    document.querySelector("#categories a").classList.remove("favourites-hover");
};

/**
 * Handler for favourite drop events.
 * Adds the dragged operation to the favourites list.
 *
 * @param {event} e
 */
RecipeWaiter.prototype.favDrop = function(e) {
    e.stopPropagation();
    e.preventDefault();
    e.target.classList.remove("favourites-hover");

    var opName = e.dataTransfer.getData("Text");
    this.app.addFavourite(opName);
};

/**
 * Handler for ingredient change events.
 *
 * @fires Manager#statechange
 */
RecipeWaiter.prototype.ingChange = function() {
    window.dispatchEvent(this.manager.statechange);
};

/**
 * Handler for disable click events.
 * Updates the icon status.
 *
 * @fires Manager#statechange
 * @param {event} e
 */
RecipeWaiter.prototype.disableClick = function(e) {
    var icon = e.target;

    if (icon.getAttribute("disabled") === "false") {
        icon.setAttribute("disabled", "true");
        icon.classList.add("disable-icon-selected");
        icon.parentNode.parentNode.classList.add("disabled");
    } else {
        icon.setAttribute("disabled", "false");
        icon.classList.remove("disable-icon-selected");
        icon.parentNode.parentNode.classList.remove("disabled");
    }

    this.app.progress = 0;
    window.dispatchEvent(this.manager.statechange);
};

/**
 * Handler for breakpoint click events.
 * Updates the icon status.
 *
 * @fires Manager#statechange
 * @param {event} e
 */
RecipeWaiter.prototype.breakpointClick = function(e) {
    var bp = e.target;

    if (bp.getAttribute("break") === "false") {
        bp.setAttribute("break", "true");
        bp.classList.add("breakpoint-selected");
    } else {
        bp.setAttribute("break", "false");
        bp.classList.remove("breakpoint-selected");
    }

    window.dispatchEvent(this.manager.statechange);
};

/**
 * Handler for operation doubleclick events.
 * Removes the operation from the recipe and auto bakes.
 *
 * @fires Manager#statechange
 * @param {event} e
 */
RecipeWaiter.prototype.operationDblclick = function(e) {
    e.target.remove();
    window.dispatchEvent(this.manager.statechange);
};

/**
 * Handler for operation child doubleclick events.
 * Removes the operation from the recipe.
 *
 * @fires Manager#statechange
 * @param {event} e
 */
RecipeWaiter.prototype.operationChildDblclick = function(e) {
    e.target.parentNode.remove();
    window.dispatchEvent(this.manager.statechange);
};

/**
 * Generates a configuration object to represent the current recipe.
 *
 * @returns {recipeConfig}
 */
RecipeWaiter.prototype.getConfig = function() {
    var config = [], ingredients, ingList, disabled, bp, item,
        operations = document.querySelectorAll("#rec-list li.operation");

    for (var i = 0; i < operations.length; i++) {
        ingredients = [];
        disabled = operations[i].querySelector(".disable-icon");
        bp = operations[i].querySelector(".breakpoint");
        ingList = operations[i].querySelectorAll(".arg");

        for (var j = 0; j < ingList.length; j++) {
            if (ingList[j].getAttribute("type") === "checkbox") {
                // checkbox
                ingredients[j] = ingList[j].checked;
            } else if (ingList[j].classList.contains("toggle-string")) {
                // toggleString
                ingredients[j] = {
                    option: ingList[j].previousSibling.children[0].textContent.slice(0, -1),
                    string: ingList[j].value
                };
            } else {
                // all others
                ingredients[j] = ingList[j].value;
            }
        }

        item = {
            op: operations[i].querySelector(".arg-title").textContent,
            args: ingredients
        };

        if (disabled && disabled.getAttribute("disabled") === "true") {
            item.disabled = true;
        }

        if (bp && bp.getAttribute("break") === "true") {
            item.breakpoint = true;
        }

        config.push(item);
    }

    return config;
};

/**
 * Moves or removes the breakpoint indicator in the recipe based on the position.
 *
 * @param {number} position
 */
RecipeWaiter.prototype.updateBreakpointIndicator = function(position) {
    var operations = document.querySelectorAll("#rec-list li.operation");
    for (var i = 0; i < operations.length; i++) {
        if (i === position) {
            operations[i].classList.add("break");
        } else {
            operations[i].classList.remove("break");
        }
    }
};

/**
 * Given an operation stub element, this function converts it into a full recipe element with
 * arguments.
 *
 * @param {element} el - The operation stub element from the operations pane
 */
RecipeWaiter.prototype.buildRecipeOperation = function(el) {
    var opName = el.textContent;
    var op = new HTMLOperation(opName, this.app.operations[opName], this.app, this.manager);
    el.innerHTML = op.toFullHtml();

    if (this.app.operations[opName].flowControl) {
        el.classList.add("flow-control-op");
    }

    // Disable auto-bake if this is a manual op - this should be moved to the 'operationadd'
    // handler after event restructuring
    if (op.manualBake && this.app.autoBake_) {
        this.manager.controls.setAutoBake(false);
        this.app.alert("Auto-Bake is disabled by default when using this operation.", "info", 5000);
    }
};

/**
 * Adds the specified operation to the recipe.
 *
 * @fires Manager#operationadd
 * @param {string} name - The name of the operation to add
 * @returns {element}
 */
RecipeWaiter.prototype.addOperation = function(name) {
    var item = document.createElement("li");

    item.classList.add("operation");
    item.innerHTML = name;
    this.buildRecipeOperation(item);
    document.getElementById("rec-list").appendChild(item);

    item.dispatchEvent(this.manager.operationadd);
    return item;
};

/**
 * Removes all operations from the recipe.
 *
 * @fires Manager#operationremove
 */
RecipeWaiter.prototype.clearRecipe = function() {
    var recList = document.getElementById("rec-list");
    while (recList.firstChild) {
        recList.removeChild(recList.firstChild);
    }
    recList.dispatchEvent(this.manager.operationremove);
};

/**
 * Handler for operation dropdown events from toggleString arguments.
 * Sets the selected option as the name of the button.
 *
 * @param {event} e
 */
RecipeWaiter.prototype.dropdownToggleClick = function(e) {
    var el = e.target,
        button = el.parentNode.parentNode.previousSibling;

    button.innerHTML = el.textContent + " ";
    this.ingChange();
};

/**
 * Handler for operationadd events.
 *
 * @listens Manager#operationadd
 * @fires Manager#statechange
 * @param {event} e
 */
RecipeWaiter.prototype.opAdd = function(e) {
    window.dispatchEvent(this.manager.statechange);
};

/**
 * Handler for operationremove events.
 *
 * @listens Manager#operationremove
 * @fires Manager#statechange
 * @param {event} e
 */
RecipeWaiter.prototype.opRemove = function(e) {
    window.dispatchEvent(this.manager.statechange);
};

export default RecipeWaiter;
<commit_msg>BUGFIX #98: Auto-Bake now triggers when operations are re-ordered.<commit_after>import HTMLOperation from "./HTMLOperation.js";
import Sortable from "sortablejs";

/**
 * Waiter to handle events related to the recipe.
 *
 * @author n1474335 [n1474335@gmail.com]
 * @copyright Crown Copyright 2016
 * @license Apache-2.0
 *
 * @constructor
 * @param {App} app - The main view object for CyberChef.
 * @param {Manager} manager - The CyberChef event manager.
 */
var RecipeWaiter = function(app, manager) {
    this.app = app;
    this.manager = manager;
    this.removeIntent = false;
};

/**
 * Sets up the drag and drop capability for operations in the operations and recipe areas.
 */
RecipeWaiter.prototype.initialiseOperationDragNDrop = function() {
    var recList = document.getElementById("rec-list");

    // Recipe list
    Sortable.create(recList, {
        group: "recipe",
        sort: true,
        animation: 0,
        delay: 0,
        filter: ".arg-input,.arg",
        preventOnFilter: false,
        setData: function(dataTransfer, dragEl) {
            dataTransfer.setData("Text", dragEl.querySelector(".arg-title").textContent);
        },
        onEnd: function(evt) {
            if (this.removeIntent) {
                evt.item.remove();
                evt.target.dispatchEvent(this.manager.operationremove);
            }
        }.bind(this),
        onSort: function(evt) {
            document.dispatchEvent(this.manager.statechange);
        }.bind(this)
    });

    Sortable.utils.on(recList, "dragover", function() {
        this.removeIntent = false;
    }.bind(this));

    Sortable.utils.on(recList, "dragleave", function() {
        this.removeIntent = true;
        this.app.progress = 0;
    }.bind(this));

    Sortable.utils.on(recList, "touchend", function(e) {
        var loc = e.changedTouches[0],
            target = document.elementFromPoint(loc.clientX, loc.clientY);

        this.removeIntent = !recList.contains(target);
    }.bind(this));

    // Favourites category
    document.querySelector("#categories a").addEventListener("dragover", this.favDragover.bind(this));
    document.querySelector("#categories a").addEventListener("dragleave", this.favDragleave.bind(this));
    document.querySelector("#categories a").addEventListener("drop", this.favDrop.bind(this));
};

/**
 * Creates a drag-n-droppable seed list of operations.
 *
 * @param {element} listEl - The list the initialise
 */
RecipeWaiter.prototype.createSortableSeedList = function(listEl) {
    Sortable.create(listEl, {
        group: {
            name: "recipe",
            pull: "clone",
            put: false
        },
        sort: false,
        setData: function(dataTransfer, dragEl) {
            dataTransfer.setData("Text", dragEl.textContent);
        },
        onStart: function(evt) {
            $(evt.item).popover("destroy");
            evt.item.setAttribute("data-toggle", "popover-disabled");
        },
        onEnd: this.opSortEnd.bind(this)
    });
};

/**
 * Handler for operation sort end events.
 * Removes the operation from the list if it has been dropped outside. If not, adds it to the list
 * at the appropriate place and initialises it.
 *
 * @fires Manager#operationadd
 * @param {event} evt
 */
RecipeWaiter.prototype.opSortEnd = function(evt) {
    if (this.removeIntent) {
        if (evt.item.parentNode.id === "rec-list") {
            evt.item.remove();
        }
        return;
    }

    // Reinitialise the popover on the original element in the ops list because for some reason it
    // gets destroyed and recreated.
    $(evt.clone).popover();
    $(evt.clone).children("[data-toggle=popover]").popover();

    if (evt.item.parentNode.id !== "rec-list") {
        return;
    }

    this.buildRecipeOperation(evt.item);
    evt.item.dispatchEvent(this.manager.operationadd);
};

/**
 * Handler for favourite dragover events.
 * If the element being dragged is an operation, displays a visual cue so that the user knows it can
 * be dropped here.
 *
 * @param {event} e
 */
RecipeWaiter.prototype.favDragover = function(e) {
    if (e.dataTransfer.effectAllowed !== "move")
        return false;

    e.stopPropagation();
    e.preventDefault();
    if (e.target.className && e.target.className.indexOf("category-title") > -1) {
        // Hovering over the a
        e.target.classList.add("favourites-hover");
    } else if (e.target.parentNode.className && e.target.parentNode.className.indexOf("category-title") > -1) {
        // Hovering over the Edit button
        e.target.parentNode.classList.add("favourites-hover");
    } else if (e.target.parentNode.parentNode.className && e.target.parentNode.parentNode.className.indexOf("category-title") > -1) {
        // Hovering over the image on the Edit button
        e.target.parentNode.parentNode.classList.add("favourites-hover");
    }
};

/**
 * Handler for favourite dragleave events.
 * Removes the visual cue.
 *
 * @param {event} e
 */
RecipeWaiter.prototype.favDragleave = function(e) {
    e.stopPropagation();
    e.preventDefault();
    document.querySelector("#categories a").classList.remove("favourites-hover");
};

/**
 * Handler for favourite drop events.
 * Adds the dragged operation to the favourites list.
 *
 * @param {event} e
 */
RecipeWaiter.prototype.favDrop = function(e) {
    e.stopPropagation();
    e.preventDefault();
    e.target.classList.remove("favourites-hover");

    var opName = e.dataTransfer.getData("Text");
    this.app.addFavourite(opName);
};

/**
 * Handler for ingredient change events.
 *
 * @fires Manager#statechange
 */
RecipeWaiter.prototype.ingChange = function() {
    window.dispatchEvent(this.manager.statechange);
};

/**
 * Handler for disable click events.
 * Updates the icon status.
 *
 * @fires Manager#statechange
 * @param {event} e
 */
RecipeWaiter.prototype.disableClick = function(e) {
    var icon = e.target;

    if (icon.getAttribute("disabled") === "false") {
        icon.setAttribute("disabled", "true");
        icon.classList.add("disable-icon-selected");
        icon.parentNode.parentNode.classList.add("disabled");
    } else {
        icon.setAttribute("disabled", "false");
        icon.classList.remove("disable-icon-selected");
        icon.parentNode.parentNode.classList.remove("disabled");
    }

    this.app.progress = 0;
    window.dispatchEvent(this.manager.statechange);
};

/**
 * Handler for breakpoint click events.
 * Updates the icon status.
 *
 * @fires Manager#statechange
 * @param {event} e
 */
RecipeWaiter.prototype.breakpointClick = function(e) {
    var bp = e.target;

    if (bp.getAttribute("break") === "false") {
        bp.setAttribute("break", "true");
        bp.classList.add("breakpoint-selected");
    } else {
        bp.setAttribute("break", "false");
        bp.classList.remove("breakpoint-selected");
    }

    window.dispatchEvent(this.manager.statechange);
};

/**
 * Handler for operation doubleclick events.
 * Removes the operation from the recipe and auto bakes.
 *
 * @fires Manager#statechange
 * @param {event} e
 */
RecipeWaiter.prototype.operationDblclick = function(e) {
    e.target.remove();
    window.dispatchEvent(this.manager.statechange);
};

/**
 * Handler for operation child doubleclick events.
 * Removes the operation from the recipe.
 *
 * @fires Manager#statechange
 * @param {event} e
 */
RecipeWaiter.prototype.operationChildDblclick = function(e) {
    e.target.parentNode.remove();
    window.dispatchEvent(this.manager.statechange);
};

/**
 * Generates a configuration object to represent the current recipe.
 *
 * @returns {recipeConfig}
 */
RecipeWaiter.prototype.getConfig = function() {
    var config = [], ingredients, ingList, disabled, bp, item,
        operations = document.querySelectorAll("#rec-list li.operation");

    for (var i = 0; i < operations.length; i++) {
        ingredients = [];
        disabled = operations[i].querySelector(".disable-icon");
        bp = operations[i].querySelector(".breakpoint");
        ingList = operations[i].querySelectorAll(".arg");

        for (var j = 0; j < ingList.length; j++) {
            if (ingList[j].getAttribute("type") === "checkbox") {
                // checkbox
                ingredients[j] = ingList[j].checked;
            } else if (ingList[j].classList.contains("toggle-string")) {
                // toggleString
                ingredients[j] = {
                    option: ingList[j].previousSibling.children[0].textContent.slice(0, -1),
                    string: ingList[j].value
                };
            } else {
                // all others
                ingredients[j] = ingList[j].value;
            }
        }

        item = {
            op: operations[i].querySelector(".arg-title").textContent,
            args: ingredients
        };

        if (disabled && disabled.getAttribute("disabled") === "true") {
            item.disabled = true;
        }

        if (bp && bp.getAttribute("break") === "true") {
            item.breakpoint = true;
        }

        config.push(item);
    }

    return config;
};

/**
 * Moves or removes the breakpoint indicator in the recipe based on the position.
 *
 * @param {number} position
 */
RecipeWaiter.prototype.updateBreakpointIndicator = function(position) {
    var operations = document.querySelectorAll("#rec-list li.operation");
    for (var i = 0; i < operations.length; i++) {
        if (i === position) {
            operations[i].classList.add("break");
        } else {
            operations[i].classList.remove("break");
        }
    }
};

/**
 * Given an operation stub element, this function converts it into a full recipe element with
 * arguments.
 *
 * @param {element} el - The operation stub element from the operations pane
 */
RecipeWaiter.prototype.buildRecipeOperation = function(el) {
    var opName = el.textContent;
    var op = new HTMLOperation(opName, this.app.operations[opName], this.app, this.manager);
    el.innerHTML = op.toFullHtml();

    if (this.app.operations[opName].flowControl) {
        el.classList.add("flow-control-op");
    }

    // Disable auto-bake if this is a manual op - this should be moved to the 'operationadd'
    // handler after event restructuring
    if (op.manualBake && this.app.autoBake_) {
        this.manager.controls.setAutoBake(false);
        this.app.alert("Auto-Bake is disabled by default when using this operation.", "info", 5000);
    }
};

/**
 * Adds the specified operation to the recipe.
 *
 * @fires Manager#operationadd
 * @param {string} name - The name of the operation to add
 * @returns {element}
 */
RecipeWaiter.prototype.addOperation = function(name) {
    var item = document.createElement("li");

    item.classList.add("operation");
    item.innerHTML = name;
    this.buildRecipeOperation(item);
    document.getElementById("rec-list").appendChild(item);

    item.dispatchEvent(this.manager.operationadd);
    return item;
};

/**
 * Removes all operations from the recipe.
 *
 * @fires Manager#operationremove
 */
RecipeWaiter.prototype.clearRecipe = function() {
    var recList = document.getElementById("rec-list");
    while (recList.firstChild) {
        recList.removeChild(recList.firstChild);
    }
    recList.dispatchEvent(this.manager.operationremove);
};

/**
 * Handler for operation dropdown events from toggleString arguments.
 * Sets the selected option as the name of the button.
 *
 * @param {event} e
 */
RecipeWaiter.prototype.dropdownToggleClick = function(e) {
    var el = e.target,
        button = el.parentNode.parentNode.previousSibling;

    button.innerHTML = el.textContent + " ";
    this.ingChange();
};

/**
 * Handler for operationadd events.
 *
 * @listens Manager#operationadd
 * @fires Manager#statechange
 * @param {event} e
 */
RecipeWaiter.prototype.opAdd = function(e) {
    window.dispatchEvent(this.manager.statechange);
};

/**
 * Handler for operationremove events.
 *
 * @listens Manager#operationremove
 * @fires Manager#statechange
 * @param {event} e
 */
RecipeWaiter.prototype.opRemove = function(e) {
    window.dispatchEvent(this.manager.statechange);
};

export default RecipeWaiter;
<|endoftext|>


<commit_before>/*
 * This file is part of Cockpit.
 *
 * Copyright (C) 2013 Red Hat, Inc.
 *
 * Cockpit is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Cockpit is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
 */

/* MAIN

   - $cockpit.connection_config

   An object with information about the current connection to the
   interface server when we are logged in.  It has 'user' and 'name'
   fields describing the user account of the logged in user.

   - $cockpit.language_code
   - $cockpit.language_po

   Information about the selected display language.  'language_code'
   contains the symbol identifying the language, such as "de" or "fi".
   'language_po' is a dictionary with the actual translations.

   - client = $cockpit.dbus(address, [options], [auto_reconnect])
   - client.release()

   Manage the active D-Bus clients.  The 'dbus' function returns a
   client for the given 'address' with the given 'options'.  Use
   'client.release' to release it.  Typically, clients are gotten in
   the 'enter' method of a page and released again in 'leave'.

   A client returned by 'dbus' can be in any state; it isn't
   necessarily "ready". However, if it is closed and auto_reconnect is
   not false, a reconnection attempt will be started.  (The
   auto_reconnect parameter defaults to 'true'.)

   Clients stay around a while after they have been released by its
   last user.
*/

var $cockpit = $cockpit || { };

(function($, $cockpit) {

$cockpit.dbus = dbus;

$cockpit._logged_out = _logged_out;

$(init);

function init() {
    $cockpit.language_code = "";
    $cockpit.language_po = null;
    cockpit_visited_pages = {};

    var lang_code = null;
    var language, language_normalized, code, code_normalized;

    // First load translations, if any... first, check browser storage
    lang_code = cockpit_settings_get("lang-code");

    // If that didn't work, try inferring from whatever language the
    // browser is using... this is a language code from RFC4646, see
    // http://tools.ietf.org/html/rfc4646
    if (!lang_code) {
        language = window.navigator.userLanguage || window.navigator.language;
        language_normalized = language.toLowerCase().replace("_", "-");
        for (code in cockpitdyn_supported_languages) {
            if (code.length > 0) {
                code_normalized = code.toLowerCase().replace("_", "-");
                if (language_normalized.indexOf(code_normalized) === 0) {
                    lang_code = code;
                }
            }
        }
    }

    if (lang_code) {
        init_load_lang(lang_code);
    } else {
        init_done();
    }
}

function init_load_lang(lang_code) {
    //cockpit_debug("Loading language `" + lang_code + "'");
    var jqxhr = $.getJSON("lang/" + lang_code + ".json");
    jqxhr.error(function() {
        cockpit_debug("Error loading language \"" + lang_code + "\"");
        init_done();
    });
    jqxhr.success(function(data) {
        $cockpit.language_code = lang_code;
        $cockpit.language_po = data[lang_code];
        init_done();
    });
}

function init_done() {
    cockpit_login_init ();
    cockpit_content_init ();
    cockpit_localize_pages();

    cockpit_login_try ();
}

var dbus_clients = { };

function make_dict_key(dict) {
    function stringify_elt(k) { return JSON.stringify(k) + ':' + JSON.stringify(dict[k]); }
    return Object.keys(dict).sort().map(stringify_elt).join(";");
}

function dbus(address, options, auto_reconnect) {
    var key, client;

    options = $.extend({host: address}, options);
    key = make_dict_key(options);
    client = dbus_clients[key];

    if (!client) {
        dbus_debug("Creating dbus client for %s", key);
        client = new DBusClient(options.host, options);
        client._get_dbus_client_leases = 0;
        client._get_dbus_client_key = key;
        dbus_clients[key] = client;
    } else {
        dbus_debug("Getting dbus client for %s", key);
    }

    client._get_dbus_client_leases += 1;
    if (client.state == "closed" && auto_reconnect !== false)
        client.connect();

    function release() {
        dbus_debug("Releasing %s", key);
        // Only really release it after a delay
        setTimeout(function () {
            if (!client._get_dbus_client_leases) {
                console.warn("Releasing unleased client");
            } else {
                client._get_dbus_client_leases -= 1;
                if (client._get_dbus_client_leases === 0) {
                    delete dbus_clients[key];
                    dbus_debug("Closing %s", key);
                    client.close("unused");
                }
            }
        }, 10000);
    }

    client.release = release;
    return client;
}

function _logged_out() {
    dbus_clients = { };
    if (Channel.transport)
        Channel.transport.close('disconnecting');
}

})(jQuery, $cockpit);
<commit_msg>web: Improve $cockpit.dbus()<commit_after>/*
 * This file is part of Cockpit.
 *
 * Copyright (C) 2013 Red Hat, Inc.
 *
 * Cockpit is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Cockpit is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
 */

/* MAIN

   - $cockpit.connection_config

   An object with information about the current connection to the
   interface server when we are logged in.  It has 'user' and 'name'
   fields describing the user account of the logged in user.

   - $cockpit.language_code
   - $cockpit.language_po

   Information about the selected display language.  'language_code'
   contains the symbol identifying the language, such as "de" or "fi".
   'language_po' is a dictionary with the actual translations.

   - client = $cockpit.dbus(address, [options], [auto_reconnect])
   - client.release()

   Manage the active D-Bus clients.  The 'dbus' function returns a
   client for the given 'address' with the given 'options'.  Use
   'client.release' to release it.  Typically, clients are gotten in
   the 'enter' method of a page and released again in 'leave'.

   A client returned by 'dbus' can be in any state; it isn't
   necessarily "ready". However, if it is closed and auto_reconnect is
   not false, a reconnection attempt will be started.  (The
   auto_reconnect parameter defaults to 'true'.)

   Clients stay around a while after they have been released by its
   last user.
*/

var $cockpit = $cockpit || { };

(function($, $cockpit) {

$cockpit.dbus = dbus;

$cockpit._logged_out = _logged_out;

$(init);

function init() {
    $cockpit.language_code = "";
    $cockpit.language_po = null;
    cockpit_visited_pages = {};

    var lang_code = null;
    var language, language_normalized, code, code_normalized;

    // First load translations, if any... first, check browser storage
    lang_code = cockpit_settings_get("lang-code");

    // If that didn't work, try inferring from whatever language the
    // browser is using... this is a language code from RFC4646, see
    // http://tools.ietf.org/html/rfc4646
    if (!lang_code) {
        language = window.navigator.userLanguage || window.navigator.language;
        language_normalized = language.toLowerCase().replace("_", "-");
        for (code in cockpitdyn_supported_languages) {
            if (code.length > 0) {
                code_normalized = code.toLowerCase().replace("_", "-");
                if (language_normalized.indexOf(code_normalized) === 0) {
                    lang_code = code;
                }
            }
        }
    }

    if (lang_code) {
        init_load_lang(lang_code);
    } else {
        init_done();
    }
}

function init_load_lang(lang_code) {
    //cockpit_debug("Loading language `" + lang_code + "'");
    var jqxhr = $.getJSON("lang/" + lang_code + ".json");
    jqxhr.error(function() {
        cockpit_debug("Error loading language \"" + lang_code + "\"");
        init_done();
    });
    jqxhr.success(function(data) {
        $cockpit.language_code = lang_code;
        $cockpit.language_po = data[lang_code];
        init_done();
    });
}

function init_done() {
    cockpit_login_init ();
    cockpit_content_init ();
    cockpit_localize_pages();

    cockpit_login_try ();
}

var dbus_clients = { };

function make_dict_key(dict) {
    function stringify_elt(k) { return JSON.stringify(k) + ':' + JSON.stringify(dict[k]); }
    return Object.keys(dict).sort().map(stringify_elt).join(";");
}

function dbus(address, options, auto_reconnect) {
    var key, handle;

    options = $.extend({host: address}, options);
    key = make_dict_key(options);
    handle = dbus_clients[key];

    if (!handle) {
        dbus_debug("Creating dbus client for %s", key);
        handle = { refcount: 1, client: new DBusClient(options.host, options) };
        dbus_clients[key] = handle;

        handle.client.release = function() {
            dbus_debug("Releasing %s", key);
            // Only really release it after a delay
            setTimeout(function () {
                if (!handle.refcount) {
                    console.warn("Releasing unreffed client");
                } else {
                    handle.refcount -= 1;
                    if (handle.refcount === 0) {
                        delete dbus_clients[key];
                        dbus_debug("Closing %s", key);
                        handle.client.close("unused");
                    }
                }
            }, 10000);
        };
    } else {
        dbus_debug("Getting dbus client for %s", key);
        handle.refcount += 1;
    }

    if (handle.client.state == "closed" && auto_reconnect !== false)
        handle.client.connect();

    return handle.client;
}

function _logged_out() {
    dbus_clients = { };
    if (Channel.transport)
        Channel.transport.close('disconnecting');
}

})(jQuery, $cockpit);
<|endoftext|>