(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else {
		var a = factory();
		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
	}
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 827);
/******/ })
/************************************************************************/
/******/ ({

/***/ 0:
/***/ (function(module, exports) {

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
// css base code, injected by the css-loader
module.exports = function(useSourceMap) {
	var list = [];

	// return the list of modules as css string
	list.toString = function toString() {
		return this.map(function (item) {
			var content = cssWithMappingToString(item, useSourceMap);
			if(item[2]) {
				return "@media " + item[2] + "{" + content + "}";
			} else {
				return content;
			}
		}).join("");
	};

	// import a list of modules into the list
	list.i = function(modules, mediaQuery) {
		if(typeof modules === "string")
			modules = [[null, modules, ""]];
		var alreadyImportedModules = {};
		for(var i = 0; i < this.length; i++) {
			var id = this[i][0];
			if(typeof id === "number")
				alreadyImportedModules[id] = true;
		}
		for(i = 0; i < modules.length; i++) {
			var item = modules[i];
			// skip already imported module
			// this implementation is not 100% perfect for weird media query combinations
			//  when a module is imported multiple times with different media queries.
			//  I hope this will never occur (Hey this way we have smaller bundles)
			if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) {
				if(mediaQuery && !item[2]) {
					item[2] = mediaQuery;
				} else if(mediaQuery) {
					item[2] = "(" + item[2] + ") and (" + mediaQuery + ")";
				}
				list.push(item);
			}
		}
	};
	return list;
};

function cssWithMappingToString(item, useSourceMap) {
	var content = item[1] || '';
	var cssMapping = item[3];
	if (!cssMapping) {
		return content;
	}

	if (useSourceMap && typeof btoa === 'function') {
		var sourceMapping = toComment(cssMapping);
		var sourceURLs = cssMapping.sources.map(function (source) {
			return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */'
		});

		return [content].concat(sourceURLs).concat([sourceMapping]).join('\n');
	}

	return [content].join('\n');
}

// Adapted from convert-source-map (MIT)
function toComment(sourceMap) {
	// eslint-disable-next-line no-undef
	var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));
	var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;

	return '/*# ' + data + ' */';
}


/***/ }),

/***/ 1:
/***/ (function(module, exports, __webpack_require__) {

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

var stylesInDom = {};

var	memoize = function (fn) {
	var memo;

	return function () {
		if (typeof memo === "undefined") memo = fn.apply(this, arguments);
		return memo;
	};
};

var isOldIE = memoize(function () {
	// Test for IE <= 9 as proposed by Browserhacks
	// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
	// Tests for existence of standard globals is to allow style-loader
	// to operate correctly into non-standard environments
	// @see https://github.com/webpack-contrib/style-loader/issues/177
	return window && document && document.all && !window.atob;
});

var getElement = (function (fn) {
	var memo = {};

	return function(selector) {
		if (typeof memo[selector] === "undefined") {
			var styleTarget = fn.call(this, selector);
			// Special case to return head of iframe instead of iframe itself
			if (styleTarget instanceof window.HTMLIFrameElement) {
				try {
					// This will throw an exception if access to iframe is blocked
					// due to cross-origin restrictions
					styleTarget = styleTarget.contentDocument.head;
				} catch(e) {
					styleTarget = null;
				}
			}
			memo[selector] = styleTarget;
		}
		return memo[selector]
	};
})(function (target) {
	return document.querySelector(target)
});

var singleton = null;
var	singletonCounter = 0;
var	stylesInsertedAtTop = [];

var	fixUrls = __webpack_require__(2);

module.exports = function(list, options) {
	if (typeof DEBUG !== "undefined" && DEBUG) {
		if (typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
	}

	options = options || {};

	options.attrs = typeof options.attrs === "object" ? options.attrs : {};

	// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
	// tags it will allow on a page
	if (!options.singleton) options.singleton = isOldIE();

	// By default, add <style> tags to the <head> element
	if (!options.insertInto) options.insertInto = "head";

	// By default, add <style> tags to the bottom of the target
	if (!options.insertAt) options.insertAt = "bottom";

	var styles = listToStyles(list, options);

	addStylesToDom(styles, options);

	return function update (newList) {
		var mayRemove = [];

		for (var i = 0; i < styles.length; i++) {
			var item = styles[i];
			var domStyle = stylesInDom[item.id];

			domStyle.refs--;
			mayRemove.push(domStyle);
		}

		if(newList) {
			var newStyles = listToStyles(newList, options);
			addStylesToDom(newStyles, options);
		}

		for (var i = 0; i < mayRemove.length; i++) {
			var domStyle = mayRemove[i];

			if(domStyle.refs === 0) {
				for (var j = 0; j < domStyle.parts.length; j++) domStyle.parts[j]();

				delete stylesInDom[domStyle.id];
			}
		}
	};
};

function addStylesToDom (styles, options) {
	for (var i = 0; i < styles.length; i++) {
		var item = styles[i];
		var domStyle = stylesInDom[item.id];

		if(domStyle) {
			domStyle.refs++;

			for(var j = 0; j < domStyle.parts.length; j++) {
				domStyle.parts[j](item.parts[j]);
			}

			for(; j < item.parts.length; j++) {
				domStyle.parts.push(addStyle(item.parts[j], options));
			}
		} else {
			var parts = [];

			for(var j = 0; j < item.parts.length; j++) {
				parts.push(addStyle(item.parts[j], options));
			}

			stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
		}
	}
}

function listToStyles (list, options) {
	var styles = [];
	var newStyles = {};

	for (var i = 0; i < list.length; i++) {
		var item = list[i];
		var id = options.base ? item[0] + options.base : item[0];
		var css = item[1];
		var media = item[2];
		var sourceMap = item[3];
		var part = {css: css, media: media, sourceMap: sourceMap};

		if(!newStyles[id]) styles.push(newStyles[id] = {id: id, parts: [part]});
		else newStyles[id].parts.push(part);
	}

	return styles;
}

function insertStyleElement (options, style) {
	var target = getElement(options.insertInto)

	if (!target) {
		throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
	}

	var lastStyleElementInsertedAtTop = stylesInsertedAtTop[stylesInsertedAtTop.length - 1];

	if (options.insertAt === "top") {
		if (!lastStyleElementInsertedAtTop) {
			target.insertBefore(style, target.firstChild);
		} else if (lastStyleElementInsertedAtTop.nextSibling) {
			target.insertBefore(style, lastStyleElementInsertedAtTop.nextSibling);
		} else {
			target.appendChild(style);
		}
		stylesInsertedAtTop.push(style);
	} else if (options.insertAt === "bottom") {
		target.appendChild(style);
	} else if (typeof options.insertAt === "object" && options.insertAt.before) {
		var nextSibling = getElement(options.insertInto + " " + options.insertAt.before);
		target.insertBefore(style, nextSibling);
	} else {
		throw new Error("[Style Loader]\n\n Invalid value for parameter 'insertAt' ('options.insertAt') found.\n Must be 'top', 'bottom', or Object.\n (https://github.com/webpack-contrib/style-loader#insertat)\n");
	}
}

function removeStyleElement (style) {
	if (style.parentNode === null) return false;
	style.parentNode.removeChild(style);

	var idx = stylesInsertedAtTop.indexOf(style);
	if(idx >= 0) {
		stylesInsertedAtTop.splice(idx, 1);
	}
}

function createStyleElement (options) {
	var style = document.createElement("style");

	options.attrs.type = "text/css";

	addAttrs(style, options.attrs);
	insertStyleElement(options, style);

	return style;
}

function createLinkElement (options) {
	var link = document.createElement("link");

	options.attrs.type = "text/css";
	options.attrs.rel = "stylesheet";

	addAttrs(link, options.attrs);
	insertStyleElement(options, link);

	return link;
}

function addAttrs (el, attrs) {
	Object.keys(attrs).forEach(function (key) {
		el.setAttribute(key, attrs[key]);
	});
}

function addStyle (obj, options) {
	var style, update, remove, result;

	// If a transform function was defined, run it on the css
	if (options.transform && obj.css) {
	    result = options.transform(obj.css);

	    if (result) {
	    	// If transform returns a value, use that instead of the original css.
	    	// This allows running runtime transformations on the css.
	    	obj.css = result;
	    } else {
	    	// If the transform function returns a falsy value, don't add this css.
	    	// This allows conditional loading of css
	    	return function() {
	    		// noop
	    	};
	    }
	}

	if (options.singleton) {
		var styleIndex = singletonCounter++;

		style = singleton || (singleton = createStyleElement(options));

		update = applyToSingletonTag.bind(null, style, styleIndex, false);
		remove = applyToSingletonTag.bind(null, style, styleIndex, true);

	} else if (
		obj.sourceMap &&
		typeof URL === "function" &&
		typeof URL.createObjectURL === "function" &&
		typeof URL.revokeObjectURL === "function" &&
		typeof Blob === "function" &&
		typeof btoa === "function"
	) {
		style = createLinkElement(options);
		update = updateLink.bind(null, style, options);
		remove = function () {
			removeStyleElement(style);

			if(style.href) URL.revokeObjectURL(style.href);
		};
	} else {
		style = createStyleElement(options);
		update = applyToTag.bind(null, style);
		remove = function () {
			removeStyleElement(style);
		};
	}

	update(obj);

	return function updateStyle (newObj) {
		if (newObj) {
			if (
				newObj.css === obj.css &&
				newObj.media === obj.media &&
				newObj.sourceMap === obj.sourceMap
			) {
				return;
			}

			update(obj = newObj);
		} else {
			remove();
		}
	};
}

var replaceText = (function () {
	var textStore = [];

	return function (index, replacement) {
		textStore[index] = replacement;

		return textStore.filter(Boolean).join('\n');
	};
})();

function applyToSingletonTag (style, index, remove, obj) {
	var css = remove ? "" : obj.css;

	if (style.styleSheet) {
		style.styleSheet.cssText = replaceText(index, css);
	} else {
		var cssNode = document.createTextNode(css);
		var childNodes = style.childNodes;

		if (childNodes[index]) style.removeChild(childNodes[index]);

		if (childNodes.length) {
			style.insertBefore(cssNode, childNodes[index]);
		} else {
			style.appendChild(cssNode);
		}
	}
}

function applyToTag (style, obj) {
	var css = obj.css;
	var media = obj.media;

	if(media) {
		style.setAttribute("media", media)
	}

	if(style.styleSheet) {
		style.styleSheet.cssText = css;
	} else {
		while(style.firstChild) {
			style.removeChild(style.firstChild);
		}

		style.appendChild(document.createTextNode(css));
	}
}

function updateLink (link, options, obj) {
	var css = obj.css;
	var sourceMap = obj.sourceMap;

	/*
		If convertToAbsoluteUrls isn't defined, but sourcemaps are enabled
		and there is no publicPath defined then lets turn convertToAbsoluteUrls
		on by default.  Otherwise default to the convertToAbsoluteUrls option
		directly
	*/
	var autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;

	if (options.convertToAbsoluteUrls || autoFixUrls) {
		css = fixUrls(css);
	}

	if (sourceMap) {
		// http://stackoverflow.com/a/26603875
		css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
	}

	var blob = new Blob([css], { type: "text/css" });

	var oldSrc = link.href;

	link.href = URL.createObjectURL(blob);

	if(oldSrc) URL.revokeObjectURL(oldSrc);
}


/***/ }),

/***/ 109:
/***/ (function(module, exports) {

module.exports = "\"data:image/svg+xml,%3C?xml version='1.0' encoding='utf-8'?%3E %3C!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E %3Csvg version='1.1' id='Ebene_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 42.5 42.5' style='enable-background:new 0 0 42.5 42.5;' xml:space='preserve' width='42.5' height='42.5'%3E %3Cpath d='M34.8,28.5c0.6-0.3,1.1-0.7,1.6-1.2v3.5c0,1.2-0.4,2.3-1.3,3.2c-0.9,0.9-1.9,1.3-3.2,1.3H7.5c-1.2,0-2.3-0.4-3.2-1.3 C3.4,33.1,3,32,3,30.8V11.5c0-1.2,0.4-2.3,1.3-3.2C5.2,7.4,6.3,7,7.5,7h6.4c1.2,0,2.3,0.4,3.2,1.3s1.3,1.9,1.3,3.2v0.6h5.5 c-0.9,0.7-1.7,1.6-2.3,2.6h-3.8c-0.5,0-1-0.2-1.4-0.6c-0.4-0.4-0.6-0.8-0.6-1.4v-1.3c0-0.5-0.2-1-0.6-1.4c-0.4-0.4-0.8-0.6-1.4-0.6 H7.5c-0.5,0-1,0.2-1.4,0.6c-0.4,0.4-0.6,0.8-0.6,1.4v19.3c0,0.5,0.2,1,0.6,1.4c0.4,0.4,0.8,0.6,1.4,0.6h24.4c0.5,0,1-0.2,1.4-0.6 c0.4-0.4,0.6-0.8,0.6-1.4V29C34.1,28.8,34.5,28.6,34.8,28.5z M38.4,15c-0.9-1.5-2.1-2.7-3.6-3.6c-1.5-0.9-3.1-1.3-4.9-1.3 s-3.4,0.4-4.9,1.3c-0.4,0.2-0.7,0.5-1,0.7h5.9c0,0,0,0,0.1,0s0,0,0.1,0h2c1.2,0,2.3,0.4,3.2,1.3c0.5,0.5,0.9,1.2,1.1,1.9 c0.2,0.2,0.3,0.5,0.5,0.7c0.7,1.2,1,2.5,1,3.9c0,1.4-0.3,2.7-1,3.9c-0.1,0.1-0.2,0.3-0.3,0.4v3c0.8-0.7,1.5-1.5,2-2.4 c0.9-1.5,1.3-3.1,1.3-4.9C39.7,18.2,39.3,16.5,38.4,15z M29.9,27.8c-1.4,0-2.7-0.3-3.9-1c-1.2-0.7-2.1-1.6-2.8-2.8 c-0.7-1.2-1-2.5-1-3.9c0-1.4,0.3-2.7,1-3.9c0.3-0.5,0.6-0.9,1-1.3h-2.5c-0.1,0.1-0.1,0.2-0.2,0.3c-0.9,1.5-1.3,3.1-1.3,4.9 c0,1.8,0.4,3.4,1.3,4.9c0.9,1.5,2.1,2.7,3.6,3.6s3.1,1.3,4.9,1.3c1.4,0,2.7-0.3,3.9-0.8v-2.3c0,0,0,0,0,0 C32.6,27.4,31.3,27.8,29.9,27.8z M36.2,15.3c-0.2-0.7-0.6-1.3-1.1-1.9c-0.9-0.9-1.9-1.3-3.2-1.3h-2c1.4,0,2.7,0.4,3.9,1 C34.7,13.7,35.5,14.5,36.2,15.3z M33.8,26.7V29c0.3-0.1,0.7-0.3,1-0.5c0.6-0.3,1.1-0.7,1.6-1.2v-3C35.7,25.3,34.9,26.1,33.8,26.7z M21.6,14.7h2.5c0.5-0.6,1.2-1.1,1.9-1.5c1.2-0.7,2.5-1,3.9-1h-5.9C23,12.8,22.2,13.7,21.6,14.7z M31.8,15h-1.1 c-0.2,0-0.3,0.1-0.4,0.2c-0.1,0.1-0.2,0.2-0.2,0.4v6.2h-3.9c-0.2,0-0.3,0.1-0.4,0.2c-0.1,0.1-0.2,0.2-0.2,0.4v1.1 c0,0.2,0.1,0.3,0.2,0.4C25.9,24,26,24,26.2,24h5.6c0.2,0,0.3-0.1,0.4-0.2c0.1-0.1,0.2-0.2,0.2-0.4v-7.9c0-0.2-0.1-0.3-0.2-0.4 C32.1,15.1,32,15,31.8,15z'/%3E %3C/svg%3E\""

/***/ }),

/***/ 2:
/***/ (function(module, exports) {


/**
 * When source maps are enabled, `style-loader` uses a link element with a data-uri to
 * embed the css on the page. This breaks all relative urls because now they are relative to a
 * bundle instead of the current page.
 *
 * One solution is to only use full urls, but that may be impossible.
 *
 * Instead, this function "fixes" the relative urls to be absolute according to the current page location.
 *
 * A rudimentary test suite is located at `test/fixUrls.js` and can be run via the `npm test` command.
 *
 */

module.exports = function (css) {
  // get current location
  var location = typeof window !== "undefined" && window.location;

  if (!location) {
    throw new Error("fixUrls requires window.location");
  }

	// blank or null?
	if (!css || typeof css !== "string") {
	  return css;
  }

  var baseUrl = location.protocol + "//" + location.host;
  var currentDir = baseUrl + location.pathname.replace(/\/[^\/]*$/, "/");

	// convert each url(...)
	/*
	This regular expression is just a way to recursively match brackets within
	a string.

	 /url\s*\(  = Match on the word "url" with any whitespace after it and then a parens
	   (  = Start a capturing group
	     (?:  = Start a non-capturing group
	         [^)(]  = Match anything that isn't a parentheses
	         |  = OR
	         \(  = Match a start parentheses
	             (?:  = Start another non-capturing groups
	                 [^)(]+  = Match anything that isn't a parentheses
	                 |  = OR
	                 \(  = Match a start parentheses
	                     [^)(]*  = Match anything that isn't a parentheses
	                 \)  = Match a end parentheses
	             )  = End Group
              *\) = Match anything and then a close parens
          )  = Close non-capturing group
          *  = Match anything
       )  = Close capturing group
	 \)  = Match a close parens

	 /gi  = Get all matches, not the first.  Be case insensitive.
	 */
	var fixedCss = css.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi, function(fullMatch, origUrl) {
		// strip quotes (if they exist)
		var unquotedOrigUrl = origUrl
			.trim()
			.replace(/^"(.*)"$/, function(o, $1){ return $1; })
			.replace(/^'(.*)'$/, function(o, $1){ return $1; });

		// already a full url? no change
		if (/^(#|data:|http:\/\/|https:\/\/|file:\/\/\/)/i.test(unquotedOrigUrl)) {
		  return fullMatch;
		}

		// convert the url to a full url
		var newUrl;

		if (unquotedOrigUrl.indexOf("//") === 0) {
		  	//TODO: should we add protocol?
			newUrl = unquotedOrigUrl;
		} else if (unquotedOrigUrl.indexOf("/") === 0) {
			// path should be relative to the base url
			newUrl = baseUrl + unquotedOrigUrl; // already starts with '/'
		} else {
			// path should be relative to current directory
			newUrl = currentDir + unquotedOrigUrl.replace(/^\.\//, ""); // Strip leading './'
		}

		// send back the fixed url(...)
		return "url(" + JSON.stringify(newUrl) + ")";
	});

	// send back the fixed css
	return fixedCss;
};


/***/ }),

/***/ 827:
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__(828);
__webpack_require__(829);
__webpack_require__(830);
__webpack_require__(831);
__webpack_require__(832);
__webpack_require__(833);
__webpack_require__(834);
__webpack_require__(835);
__webpack_require__(836);
__webpack_require__(837);
__webpack_require__(838);
__webpack_require__(839);
__webpack_require__(840);
__webpack_require__(841);
__webpack_require__(842);
__webpack_require__(843);
__webpack_require__(844);


/***/ }),

/***/ 828:
/***/ (function(module, exports) {

/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2015 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.ns('Tine.Timetracker');

/**
 * handles minutes to time conversions
 * @class Tine.Timetracker.DurationSpinner
 * @extends Ext.ux.form.Spinner
 */
Tine.Timetracker.DurationSpinner = Ext.extend(Ext.ux.form.Spinner,  {
    
    /**
     * Set to empty value if value equals 0
     * @cfg emptyOnZero
     */
    emptyOnZero: null,

    /**
     * $cfg {String} baseUnit minutes|seconds
     */
    baseUnit: 'minutes',
    
    initComponent: function() {
        this.preventMark = false;
        this.strategy = new Ext.ux.form.Spinner.TimeStrategy({
            incrementValue : 15
        });
        this.format = this.strategy.format;

        this.on('focus', function(field) {field.focus(true, 10)});
    },

    setValue: function(value) {
        if(! value || value == '00:00') {
            value = this.emptyOnZero ? '' : '00:00';
        } else if(! value.toString().match(/:/)) {
            if (this.baseUnit == 'seconds') {
                value = value/60;
            }
            var hours = Math.floor(value / 60),
                minutes = value - hours * 60;

            if (hours < 10) {
               hours = '0' + hours;
            }

            if (minutes < 10) {
                minutes = '0' + minutes;
            }

            if (minutes !== 0) {
                value = hours + ':' + minutes;
            } else {
                value = hours + ':00';
            }
        }

        Tine.Timetracker.DurationSpinner.superclass.setValue.call(this, value);
    },
    
    validateValue: function(value) {
        if (value.search(/:/) != -1) {
            var parts = value.split(':'),
                hours = parseInt(parts[0]),
                minutes = parseInt(parts[1]);

            return 'NaN' != hours && 'NaN' != minutes;
        }
    },

    getValue: function() {
        var value = Tine.Timetracker.DurationSpinner.superclass.getValue.call(this);
        value = value.replace(',', '.');

        if(value && typeof value == 'string') {
            if (value.search(/:/) != -1) {
                var parts = value.split(':'),
                    hours = parseInt(parts[0]),
                    minutes = parseInt(parts[1]);

                if (0 > hours) {
                    hours = 0;
                }

                if (0 > minutes) {
                    minutes = 0;
                }

                if (minutes > 0) {
                    value = hours * 60 + minutes;
                } else {
                    value = hours * 60;
                }
            } else if (value > 0) {
                    value = value * 60;
            } else {
                this.markInvalid(i18n._('Not a valid time'));
                return;
            }
        }

        if (this.baseUnit == 'seconds') {
            value = value* 60;
        }

        this.setValue(value);
        return value;
    }
});

Ext.reg('tinedurationspinner', Tine.Timetracker.DurationSpinner);


/***/ }),

/***/ 829:
/***/ (function(module, exports) {

/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */

Ext.ns('Tine.Timetracker.Model');

/**
 * Model of a grant
 */
Tine.Timetracker.Model.TimeaccountGrant = Tine.Tinebase.data.Record.create([
    {name: 'id'},
    {name: 'account_id'},
    {name: 'account_type'},
    {name: 'account_name'},
    {name: 'bookOwnGrant',        type: 'boolean'},
    {name: 'viewAllGrant',        type: 'boolean'},
    {name: 'bookAllGrant',        type: 'boolean'},
    {name: 'manageBillableGrant', type: 'boolean'},
    {name: 'exportGrant',         type: 'boolean'},
    {name: 'adminGrant',          type: 'boolean'}
], {
    appName: 'Timetracker',
    modelName: 'TimeaccountGrant',
    idProperty: 'id',
    titleProperty: 'account_name',
    // ngettext('Timeaccount Grant', 'Timeaccount Grants', n); gettext('Timeaccount Grant');
    recordName: 'Timeaccount Grant',
    recordsName: 'Timeaccount Grants'
});

Ext.override(Tine.widgets.container.GrantsGrid, {
    'bookOwnGrantTitle': 'Book Own', // _('Book Own')
    'bookOwnGrantDescription': 'The grant to add Timesheets to this Timeaccount', // _('The grant to add Timesheets to this Timeaccount')
    'viewAllGrantTitle': 'View All', // _('View All')
    'viewAllGrantDescription': 'The grant to view Timesheets of other users', // _('The grant to view Timesheets of other users')
    'bookAllGrantTitle': 'Book All', // _('Book All')
    'bookAllGrantDescription': 'The grant to add Timesheets for other users', // _('The grant to add Timesheets for other users')
    'manageBillableGrantTitle': 'Manage Clearing', // _('Manage Clearing')
    'manageBillableGrantDescription': 'The grant to manage clearing of Timesheets', // _('The grant to manage clearing of Timesheets')
});


/***/ }),

/***/ 830:
/***/ (function(module, exports) {

/*
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 */
 
Ext.ns('Tine.Timetracker');

Tine.widgets.grid.RendererManager.register('Timetracker', 'Timesheet', 'timeaccount_closed', function(row, index, record) {
    var isopen = (record.data.timeaccount_id.is_open == '1');
    return Tine.Tinebase.common.booleanRenderer(!isopen);
});

Tine.widgets.grid.RendererManager.register('Timetracker', 'Timesheet', 'timeaccount_id', function(row, index, record) {
    var record = new Tine.Timetracker.Model.Timeaccount(record.get('timeaccount_id'));
    var closedText = record.get('is_open') ? '' : (' (' + Tine.Tinebase.appMgr.get('Timetracker').i18n._('closed') + ')');
    return record.get('number') ? (record.get('number') + ' - ' + record.get('title') + closedText) : '';
});

Tine.Tinebase.data.TitleRendererManager.register('Timetracker', 'Timeaccount', function(record) {
    var closedText = record.get('is_open') ? '' : (' (' + Tine.Tinebase.appMgr.get('Timetracker').i18n._('closed') + ')');
    return record.get('number') ? (record.get('number') + ' - ' + record.get('title') + closedText) : '';
});

Tine.Tinebase.data.TitleRendererManager.register('Timetracker', 'Timesheet', function(record) {
    var timeaccount = record.get('timeaccount_id'),
        description = Ext.util.Format.ellipsis(record.get('description'), 30, true),
        timeaccountTitle = '';

    if (timeaccount) {
        if (typeof(timeaccount.get) !== 'function') {
            timeaccount = new Tine.Timetracker.Model.Timeaccount(timeaccount);
        }
        timeaccountTitle = timeaccount.getTitle();
    }

    timeaccountTitle = timeaccountTitle ? '[' + timeaccountTitle + '] ' : '';
    return timeaccountTitle + description;
});

Tine.widgets.grid.RendererManager.register('Timetracker', 'Timeaccount', 'status', function(row, index, record) {
    return Tine.Tinebase.appMgr.get('Timetracker').i18n._hidden(record.get('status'));
});

Tine.widgets.grid.RendererManager.register('Timetracker', 'Timeaccount', 'is_open', function(row, index, record) {
    var i18n = Tine.Tinebase.appMgr.get('Timetracker').i18n;
    return record.get('is_open') ? i18n._('open') : i18n._('closed');
});

// add renderer for invoice position gridpanel
Tine.Timetracker.HumanHourRenderer = function(value) {
    return Ext.util.Format.round(value, 2);
};

Tine.Timetracker.registerRenderers = function() {
    
    if (! Tine.hasOwnProperty('Sales') || ! Tine.Sales.hasOwnProperty('InvoicePositionQuantityRendererRegistry')) {
        Tine.Timetracker.registerRenderers.defer(10);
        return false;
    }
    
    Tine.Sales.InvoicePositionQuantityRendererRegistry.register('Timetracker_Model_Timeaccount', 'hour', Tine.Timetracker.HumanHourRenderer);
};

Tine.Timetracker.registerRenderers();

Tine.Timetracker.registerAccountables = function() {
    if (! Tine.hasOwnProperty('Sales') || ! Tine.Sales.hasOwnProperty('AccountableRegistry')) {
        Tine.Timetracker.registerAccountables.defer(10);
        return false;
    }
    
    Tine.Sales.AccountableRegistry.register('Timetracker', 'Timeaccount');
};

Tine.Timetracker.registerAccountables();

/***/ }),

/***/ 831:
/***/ (function(module, exports) {

/**
 * Tine 2.0
 *
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Michael Spahn <M.Spahn@bitExpert.de>
 * @copyright   Copyright (c) 2016 Metaways Infosystems GmbH (http://www.metaways.de)
 * @copyright   Copyright (c) 2016 bitExpert AG (http://bitexpert.de)
 *
 */

Ext.ns('Tine', 'Tine.Timetracker');

/**
 * @namespace   Tine.Timetracker
 * @class       Tine.Timetracker.TreePanel
 * @extends     Tine.Tinebase.Application
 * Timetracker TreePanel<br>
 *
 * @author Michael Spahn <M.Spahn@bitExpert.de>
 *
 */
Tine.Timetracker.TimeaccountFavoritesPanel = function (config) {
    Ext.apply(this, config);
    Tine.Timetracker.TimeaccountFavoritesPanel.superclass.constructor.call(this);
};

Ext.extend(Tine.Timetracker.TimeaccountFavoritesPanel, Ext.tree.TreePanel, {
    /**
     * Autoscrolling
     */
    autoScroll: true,

    /**
     * Border
     */
    border: false,

    /**
     * Context menu for leaf
     */
    contextMenuLeaf: null,

    /**
     * Delete fav action
     */
    action_delete: null,

    /**
     * The current node of context menu
     */
    ctxNode: null,

    /**
     * init this treePanel
     */
    initComponent: function () {
        if (!this.app) {
            this.app = Tine.Tinebase.appMgr.get('Timetracker');
        }

        var favorites = this.getTimetrackerNodes();

        this.root = {
            path: '/',
            cls: 'tinebase-tree-hide-collapsetool',
            text: this.app.i18n._('Timeaccount Favorites'),
            iconCls: 'folder',
            expanded: true,
            children: favorites
        };

        this.initContextMenu();

        this.on('dblclick', this.onDoubleClick, this);
        this.on('contextmenu', this.onContextMenu, this);

        Tine.Timetracker.TimeaccountFavoritesPanel.superclass.initComponent.call(this);
    },

    /**
     * Set up context menu
     */
    initContextMenu: function () {
        this.action_delete = new Ext.Action({
            text: String.format(i18n.ngettext('Delete {0}', 'Delete {0}', 1), this.app.i18n._('Favorite')),
            iconCls: 'action_delete',
            handler: this.deleteNode,
            scope: this,
            allowMultiple: false
        });

        this.contextMenuLeaf = new Ext.menu.Menu({
            items: [
                this.action_delete
            ]
        });
    },

    /**
     * Delete selected favorite
     *
     * @returns {boolean}
     */
    deleteNode: function() {
        if (!this.ctxNode.attributes && !this.ctxNode.attributes.favId) {
            return false;
        }

        Ext.Ajax.request({
            url: 'index.php',
            scope: this,
            params: {
                method: 'Timetracker.deleteTimeAccountFavorite',
                favId: this.ctxNode.attributes.favId
            },
            success : function(_result, _request) {
                if (_result.responseText) {
                    Tine.Timetracker.registry.set('timeaccountFavorites', JSON.parse(_result.responseText).timeaccountFavorites);

                    var rootNode = Ext.getCmp('TimeaccountFavoritesPanel').getRootNode();

                    if (!rootNode.hasChildNodes()) {
                        rootNode.removeAll();
                    }

                    rootNode.removeAll();
                    rootNode.appendChild(Tine.Timetracker.registry.get('timeaccountFavorites'));

                    rootNode.expand();
                }
            },
            failure: function(result) {
                Tine.Tinebase.ExceptionHandler.handleRequestException(result);
            }
        });
    },

    /**
     * @param node
     * @param event
     * @returns {boolean}
     */
    onContextMenu: function (node, event) {
        if (!node.leaf) {
            return false;
        }

        this.ctxNode = node;

        this.contextMenuLeaf.showAt(event.getXY());
    },

    /**
     * get core data nodes
     ** @returns Array
     */
    getTimetrackerNodes: function () {
        if (!Tine.Timetracker.registry.get('timeaccountFavorites')) {
            return [];
        }

        return Tine.Timetracker.registry.get('timeaccountFavorites');
    },

    /**
     * @param node
     * @param e
     * @returns {boolean}
     */
    onDoubleClick: function (node, e) {
        if (!node.leaf) {
            return false;
        }

        var record = new Tine.Timetracker.Model.Timesheet(Tine.Timetracker.Model.Timesheet.getDefaultData(), 0);
        record.set('timeaccount_id', node.attributes.timeaccount);

        var popupWindow = Tine.Timetracker.TimesheetEditDialog.openWindow({
                plugins: null,
                record: record,
                recordId: null
            }
        );
    }
});


/***/ }),

/***/ 832:
/***/ (function(module, exports) {

/* 
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Michael Spahn <M.Spahn@bitExpert.de>
 * @copyright   Copyright (c) 2016 Metaways Infosystems GmbH (http://www.metaways.de)
 */

Ext.ns('Tine.Timetracker');

/**
 * Timetracker TimesheetWestPanel west panel
 * 
 * @namespace   Tine.Timetracker
 * @class       Tine.Timetracker.TimesheetWestPanel
 * @extends     Tine.widgets.mainscreen.TimesheetWestPanel
 * 
 * @author      Michael Spahn <M.Spahn@bitExpert.de>
 */
Tine.Timetracker.TimesheetWestPanel = Ext.extend(Tine.widgets.mainscreen.WestPanel, {

    getAdditionalItems: function() {
        var items = [];

        if (this.app.featureEnabled('featureTimeaccountBookmark')) {
            items.push(new Tine.Timetracker.TimeaccountFavoritesPanel({
                height: 'auto',
                id: 'TimeaccountFavoritesPanel'
            }));
        }

        return items;
    }
});


/***/ }),

/***/ 833:
/***/ (function(module, exports) {

/* 
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2016 Metaways Infosystems GmbH (http://www.metaways.de)
 */

Ext.ns('Tine.Timetracker');

/**
 * Timetracker TimeaccountWestPanel west panel
 *
 * @namespace   Tine.Timetracker
 * @class       Tine.Timetracker.TimeaccountWestPanel
 * @extends     Tine.widgets.mainscreen.TimeaccountWestPanel
 *
 * @author      Michael Spahn <M.Spahn@bitExpert.de>
 */
Tine.Timetracker.TimeaccountWestPanel = Ext.extend(Tine.widgets.mainscreen.WestPanel, {
    hasContainerTreePanel: false,

    getAdditionalItems: function () {
        var items = [];

        // if (this.app.featureEnabled('featureTimeaccountBookmark')) {
        //     items.push(new Tine.Timetracker.TimeaccountFavoritesPanel({
        //         height: 'auto',
        //         id: 'TimeaccountFavoritesPanel'
        //     }));
        // }

        return items;
    }
});


/***/ }),

/***/ 834:
/***/ (function(module, exports) {

/* 
 * Tine 2.0
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2012 Metaways Infosystems GmbH (http://www.metaways.de)
 */

Ext.ns('Tine.Timetracker');

/**
 * @namespace   Tine.Timetracker
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @class       Tine.Timetracker.TimeaccountPickerCombo
 * @extends     Tine.Tinebase.widgets.form.RecordPickerComboBox
 * 
 * adds show closed handling
 */

Tine.Timetracker.TimeaccountPickerCombo = Ext.extend(Tine.Tinebase.widgets.form.RecordPickerComboBox, {
    /**
     * @cfg {Bool} showClosed
     */
    showClosed: false,
    
    /**
     * @property showClosedBtn
     * @type Ext.Button
     */
    showClosedBtn: null,
    
    sortBy: 'number',
    
    initComponent: function() {
        this.recordProxy = Tine.Timetracker.timeaccountBackend;
        this.recordClass = Tine.Timetracker.Model.Timeaccount;
        
        Tine.Timetracker.TimeaccountPickerCombo.superclass.initComponent.apply(this, arguments);
    },
    
    initList: function() {
        Tine.Timetracker.TimeaccountPickerCombo.superclass.initList.apply(this, arguments);
        
        if (this.pageTb && ! this.showClosedBtn) {
            this.showClosedBtn = new Tine.widgets.grid.FilterButton({
                text: this.app.i18n._('Show closed'),
                iconCls: 'action_showArchived',
                field: 'is_open',
                invert: true,
                pressed: this.showClosed,
                scope: this,
                handler: function() {
                    this.showClosed = this.showClosedBtn.pressed;
                    this.store.load();
                }
                
            });
            
            this.pageTb.add('-', this.showClosedBtn);
            this.pageTb.doLayout();
        }
    },
    
    /**
     * apply showClosed value
     */
    onStoreBeforeLoadRecords: function(o, options, success, store) {
        if (Tine.Timetracker.TimeaccountPickerCombo.superclass.onStoreBeforeLoadRecords.apply(this, arguments) !== false) {
            if (this.showClosedBtn) {
                this.showClosedBtn.setValue(options.params.filter);
            }
        }
    },
    
    /**
     * append showClosed value
     */
    onBeforeLoad: function (store, options) {
        Tine.Timetracker.TimeaccountPickerCombo.superclass.onBeforeLoad.apply(this, arguments);

        if (this.showClosedBtn) {
            Ext.each(store.baseParams.filter, function(filter, idx) {
                if (filter.field == 'is_open'){
                    store.baseParams.filter.remove(filter);
                }
            }, this);
            
            if (this.showClosedBtn.getValue().value === true) {
                store.baseParams.filter.push(this.showClosedBtn.getValue());
            }
        }
    }
});

Tine.widgets.form.RecordPickerManager.register('Timetracker', 'Timeaccount', Tine.Timetracker.TimeaccountPickerCombo);

/***/ }),

/***/ 835:
/***/ (function(module, exports) {

/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 */
 
Ext.ns('Tine.Timetracker');

Tine.Timetracker.TimeAccountStatusFilterModel = Ext.extend(Tine.widgets.grid.FilterModel, {
    field: 'is_open',
    valueType: 'bool',
    defaultValue: 1,
    
    /**
     * @private
     */
    initComponent: function() {
        Tine.Timetracker.TimeAccountStatusFilterModel.superclass.initComponent.call(this);
        
        this.app = Tine.Tinebase.appMgr.get('Timetracker');
        this.label = this.label ? this.label : this.app.i18n._("Time Account - Status");
        this.operators = ['equals'];
    },
   
    /**
     * value renderer
     * 
     * @param {Ext.data.Record} filter line
     * @param {Ext.Element} element to render to 
     */
    valueRenderer: function(filter, el) {
        // value
        var value = new Ext.form.ComboBox({
            filter: filter,
            width: this.filterValueWidth,
            id: 'tw-ftb-frow-valuefield-' + filter.id,
            value: filter.data.value ? filter.data.value : this.defaultValue,
            renderTo: el,
            mode: 'local',
            forceSelection: true,
            blurOnSelect: true,
            triggerAction: 'all',
            store: [[0, this.app.i18n._('closed')], [1, this.app.i18n._('open')]]
        });
        value.on('specialkey', function(field, e){
             if(e.getKey() == e.ENTER){
                 this.onFiltertrigger();
             }
        }, this);
        
        return value;
    }
});
Tine.widgets.grid.FilterToolbar.FILTERS['timetracker.timeaccountstatus'] = Tine.Timetracker.TimeAccountStatusFilterModel;


/***/ }),

/***/ 836:
/***/ (function(module, exports) {

/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 */
 
Ext.ns('Tine.Timetracker');

Tine.Timetracker.TimeAccountBilledFilterModel = Ext.extend(Tine.widgets.grid.FilterModel, {
    field: 'timeaccount_status',
    valueType: 'string',
    defaultValue: 'to bill',
    
    /**
     * @private
     */
    initComponent: function() {
        Tine.Timetracker.TimeAccountBilledFilterModel.superclass.initComponent.call(this);
        
        this.app = Tine.Tinebase.appMgr.get('Timetracker');
        this.label = this.label ? this.label : this.app.i18n._("Time Account - Billed");
        this.operators = ['equals'];
    },
   
    /**
     * value renderer
     * 
     * @param {Ext.data.Record} filter line
     * @param {Ext.Element} element to render to 
     */
    valueRenderer: function(filter, el) {
        var value = new Ext.form.ComboBox({
            filter: filter,
            width: this.filterValueWidth,
            id: 'tw-ftb-frow-valuefield-' + filter.id,
            value: filter.data.value ? filter.data.value : this.defaultValue,
            renderTo: el,
            mode: 'local',
            forceSelection: true,
            blurOnSelect: true,
            triggerAction: 'all',
            store: [
                ['not yet billed', this.app.i18n._('not yet billed')], 
                ['to bill', this.app.i18n._('to bill')],
                ['billed', this.app.i18n._('billed')]
            ]
        });
        value.on('specialkey', function(field, e){
             if(e.getKey() == e.ENTER){
                 this.onFiltertrigger();
             }
        }, this);
        
        return value;
    }
});
Tine.widgets.grid.FilterToolbar.FILTERS['timetracker.timeaccountbilled'] = Tine.Timetracker.TimeAccountBilledFilterModel;


/***/ }),

/***/ 837:
/***/ (function(module, exports) {

/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Alexander Stintzing <a.stintzing@metaways.de>
 * @copyright   Copyright (c) 2012 Metaways Infosystems GmbH (http://www.metaways.de)
 */
 
Ext.ns('Tine.Timetracker');

/**
 * @namespace   Tine.Timetracker
 * @class       Tine.Timetracker.TimeaccountContractFilterModel
 * @extends     Tine.widgets.grid.ForeignRecordFilter
 * 
 * @author      Alexander Stintzing <a.stintzing@metaways.de>
 */
Tine.Timetracker.TimeaccountContractFilterModel = Ext.extend(Tine.widgets.grid.ForeignRecordFilter, {
    
    // private
    field: 'contract',
    valueType: 'relation',
    
    /**
     * @private
     */
    initComponent: function() {
        this.app = Tine.Tinebase.appMgr.get('Timetracker');
        this.label = this.app.i18n._('Contract');
        this.foreignRecordClass = 'Sales.Contract',
        this.pickerConfig = {emptyText: this.app.i18n._('without contract'), allowBlank: true};

        Tine.Timetracker.TimeaccountContractFilterModel.superclass.initComponent.call(this);
    }
});

Tine.widgets.grid.FilterToolbar.FILTERS['timetracker.timeaccountcontract'] = Tine.Timetracker.TimeaccountContractFilterModel;

/***/ }),

/***/ 838:
/***/ (function(module, exports) {

/**
 * Tine 2.0
 *
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Alexander Stintzing <a.stintzing@metaways.de>
 * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
 */

Ext.ns('Tine.Timetracker');

/**
 * @namespace   Tine.Timetracker
 * @class       Tine.Timetracker.TimeaccountResponsibleFilterModel
 * @extends     Tine.widgets.grid.ForeignRecordFilter
 *
 * @author      Alexander Stintzing <a.stintzing@metaways.de>
 */
Tine.Timetracker.TimeaccountResponsibleFilterModel = Ext.extend(Tine.widgets.grid.ForeignRecordFilter, {

    // private
    field: 'responsible',
    valueType: 'relation',

    /**
     * @private
     */
    initComponent: function() {
        this.app = Tine.Tinebase.appMgr.get('Timetracker');
        this.label = this.app.i18n._('Responsible');
        this.foreignRecordClass = 'Addressbook.Contact',
            this.pickerConfig = {emptyText: this.app.i18n._('without responsible'), allowBlank: true};

        Tine.Timetracker.TimeaccountResponsibleFilterModel.superclass.initComponent.call(this);
    }
});

Tine.widgets.grid.FilterToolbar.FILTERS['timetracker.timeaccountresponsible'] = Tine.Timetracker.TimeaccountResponsibleFilterModel;

/***/ }),

/***/ 839:
/***/ (function(module, exports) {

/*
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.namespace('Tine.Timetracker');

/**
 * Timeaccount grid panel
 * 
 * @namespace   Tine.Timetracker
 * @class       Tine.Timetracker.TimeaccountGridPanel
 * @extends     Tine.widgets.grid.GridPanel
 * 
 * <p>Timeaccount Grid Panel</p>
 * <p><pre>
 * TODO         copy action needs to copy the acl too
 * </pre></p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schuele <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2009 Metaways Infosystems GmbH (http://www.metaways.de)
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Timetracker.TimeaccountGridPanel
 */
Tine.Timetracker.TimeaccountGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
    defaultSortInfo: {field: 'creation_time', direction: 'DESC'},
    gridConfig: {
        autoExpandColumn: 'title'
    },
    copyEditAction: true,
    defaultFilters: [{
        field: 'query',
        operator: 'contains',
        value: ''
    }, {
        field: 'is_open',
        operator: 'equals',
        value: true
    }],
    action_bookmark: null,

    initComponent: function() {
        Tine.Timetracker.TimeaccountGridPanel.superclass.initComponent.call(this);

        this.action_addInNewWindow.setDisabled(! Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts'));
        this.action_editInNewWindow.requiredGrant = 'editGrant';
    },

    /**
     * @private
     */
    initActions: function() {
        Tine.Timetracker.TimesheetGridPanel.superclass.initActions.call(this);

        this.action_bookmark = new Ext.Action({
            text: i18n._('Bookmark Timeaccount'),
            iconCls: 'action_add',
            requiredGrant: 'readGrant',
            scope: this,
            disabled: true,
            allowMultiple: false,
            visible: false,
            handler: this.addBookmark
        });

        // register actions in updater
        this.actionUpdater.addActions([
            this.action_bookmark
        ]);
    },

    addBookmark: function () {
        var grid = this,
            app = this.app,
            selectionModel = grid.selectionModel;

        Ext.each(selectionModel.getSelections(), function (record) {
            Ext.Ajax.request({
                url: 'index.php',
                scope: this,
                params: {
                    method: 'Timetracker.addTimeAccountFavorite',
                    timeaccountId: record.get('id')
                },
                success : function(_result, _request) {
                    if (_result.responseText) {
                        Tine.Timetracker.registry.set('timeaccountFavorites', JSON.parse(_result.responseText).timeaccountFavorites);

                        var rootNode = Ext.getCmp('TimeaccountFavoritesPanel').getRootNode();

                        if (!rootNode.hasChildNodes()) {
                            rootNode.removeAll();
                        }

                        rootNode.removeAll();
                        rootNode.appendChild(Tine.Timetracker.registry.get('timeaccountFavorites'));

                        rootNode.expand();
                    }
                },
                failure: function(result) {
                    Tine.Tinebase.ExceptionHandler.handleRequestException(result);
                }
            });
        });
    },

    /**
     * add custom items to context menu
     *
     * @return {Array}
     */
    getContextMenuItems: function() {
        var items = [
            '-', {
                text: Tine.Tinebase.appMgr.get('Timetracker').i18n._('Close Timeaccount'),
                iconCls: 'action_close',
                scope: this,
                disabled: !Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts'),
                itemId: 'closeAccount',
                handler: this.onCloseTimeaccount.createDelegate(this)
            }
        ];

        if (this.app.featureEnabled('featureTimeaccountBookmark')) {
            items.push(this.action_bookmark);
        }

        return items;
    },

    /**
     * Closes selected timeaccount
     */
    onCloseTimeaccount: function () {
        var grid = this,
            recordProxy = this.recordProxy,
            selectionModel = grid.selectionModel;

        Ext.each(selectionModel.getSelections(), function (record) {
            recordProxy.loadRecord(record, {
                success: function (record) {
                    record.set('is_open', false);
                    recordProxy.saveRecord(record, {
                        success: function () {
                            grid.store.reload();
                            grid.store.remove(record);
                        }
                    });
                }
            });
        });
    }
});


/***/ }),

/***/ 840:
/***/ (function(module, exports) {

/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 */
 
Ext.ns('Tine.Timetracker');

Tine.Timetracker.TimeAccountFilterModel = Ext.extend(Tine.widgets.grid.ForeignRecordFilter, {
    
    /**
     * @cfg {Record} foreignRecordClass needed for explicit defined filters
     */
    foreignRecordClass : 'Timetracker.Timeaccount',
    
    /**
     * @cfg {String} linkType {relation|foreignId} needed for explicit defined filters
     */
    linkType: 'foreignId',
    
    /**
     * @cfg {String} filterName server side filterGroup Name, needed for explicit defined filters
     */
    filterName: 'TimeaccountFilter',
    
    /**
     * @cfg {String} ownField for explicit filterRow
     */
    ownField: 'timeaccount_id',
    
    /**
     * @private
     */
    initComponent: function() {
        this.app = Tine.Tinebase.appMgr.get('Timetracker');
        this.label = this.app.i18n.n_('Timeaccount', 'Timeaccounts', 1);
        
        this.pickerConfig = this.pickerConfig || {};
        
        Tine.Timetracker.TimeAccountFilterModel.superclass.initComponent.call(this);
    }
});

Tine.widgets.grid.FilterToolbar.FILTERS['timetracker.timeaccount'] = Tine.Timetracker.TimeAccountFilterModel;

/***/ }),

/***/ 841:
/***/ (function(module, exports) {

/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Cornelius Weiss <c.weiss@metaways.de>
 * @copyright   Copyright (c) 2007-2017 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.namespace('Tine.Timetracker');

Tine.Timetracker.TimeaccountEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {
    
    /**
     * @private
     */
    windowNamePrefix: 'TimeaccountEditWindow_',
    appName: 'Timetracker',
    recordClass: Tine.Timetracker.Model.Timeaccount,
    recordProxy: Tine.Timetracker.timeaccountBackend,
    useInvoice: false,
    displayNotes: true,

    windowWidth: 800,
    windowHeight: 500,

    /**
     * don't eval grants as TAs have very special grants and we only show the dialog to users with MANAGE right
     */
    evalGrants: false,
    
    /**
     * overwrite update toolbars function (we don't have record grants yet)
     */
    updateToolbars: function() {
    },
    
    initComponent: function() {
        var salesApp = Tine.Tinebase.appMgr.get('Sales');
        this.useInvoice = salesApp
            && salesApp.featureEnabled('invoicesModule')
            && Tine.Tinebase.common.hasRight('manage', 'Sales', 'invoices')
            && Tine.Sales.Model.Invoice;
        Tine.Timetracker.TimeaccountEditDialog.superclass.initComponent.call(this);
    },

    isMultipleValid: function() {
        return true;
    },

    /**
     * containerProperty (all contracts in one container) exists, so overwrite creating selector here
     */
    initContainerSelector: Ext.emptyFn,

    /**
     * TODO generalize this
     */
    onRecordLoad: function() {
        // make sure grants grid is initialized
        this.getGrantsGrid();
        
        var grants = this.record.get('grants') || [];
        this.grantsGrid.getStore().loadData({results: grants});
        Tine.Timetracker.TimeaccountEditDialog.superclass.onRecordLoad.call(this);
        
        if (! this.copyRecord && ! this.record.id) {
            this.window.setTitle(this.app.i18n._('Add New Timeaccount'));
        }
    },

    /**
     * TODO generalize this
     */
    onRecordUpdate: function() {
        Tine.Timetracker.TimeaccountEditDialog.superclass.onRecordUpdate.call(this);
        this.record.set('grants', '');
        
        var grants = [];
        this.grantsGrid.getStore().each(function(_record){
            grants.push(_record.data);
        });
        
        this.record.set('grants', grants);
    },
    
    /**
     * returns dialog
     * 
     * NOTE: when this method gets called, all initalisation is done.
     */
    getFormItems: function() {
        
        var secondRow = [{
            fieldLabel: this.app.i18n._('Unit'),
            name: 'price_unit'
        }, {
            xtype: 'extuxmoneyfield',
            fieldLabel: this.app.i18n._('Unit Price'),
            name: 'price',
            allowNegative: false
        }, {
            xtype: 'numberfield',
            fieldLabel: this.app.i18n._('Budget'),
            name: 'budget',
            allowNegative: false,
            value: 0
        }, {
            fieldLabel: this.app.i18n._('Status'),
            name: 'is_open',
            xtype: 'combo',
            mode: 'local',
            forceSelection: true,
            triggerAction: 'all',
            store: [[0, this.app.i18n._('closed')], [1, this.app.i18n._('open')]]
        }, {
            fieldLabel: this.app.i18n._('Billed'),
            name: 'status',
            xtype: 'combo',
            mode: 'local',
            forceSelection: true,
            triggerAction: 'all',
            value: 'not yet billed',
            store: [
                ['not yet billed', this.app.i18n._('not yet billed')], 
                ['to bill', this.app.i18n._('to bill')],
                ['billed', this.app.i18n._('billed')]
            ],
            listeners: {
                scope: this,
                select: this.onBilledChange.createDelegate(this)
            }
        }];
        
        secondRow.push({
            columnWidth: 1/3,
            disabled: false,
            fieldLabel: this.app.i18n._('Cleared In'),
            name: 'billed_in'
        });
        
        secondRow.push({
            fieldLabel: this.app.i18n._('Booking deadline'),
            name: 'deadline',
            xtype: 'combo',
            mode: 'local',
            forceSelection: true,
            triggerAction: 'all',
            value: 'none',
            store: [
                ['none', this.app.i18n._('none')], 
                ['lastweek', this.app.i18n._('last week')]
            ]
        });
        
        secondRow.push({
            xtype: 'extuxclearabledatefield',
            name: 'cleared_at',
            fieldLabel: this.app.i18n._('Cleared at')
        });
        
        if (Tine.Tinebase.appMgr.get('Sales')) {
            secondRow.push({
                xtype: 'tinerelationpickercombo',
                fieldLabel: this.app.i18n._('Cost Center'),
                editDialog: this,
                allowBlank: true,
                app: 'Sales',
                recordClass: Tine.Sales.Model.CostCenter,
                relationType: 'COST_CENTER',
                relationDegree: 'sibling',
                modelUnique: true
            });
        }
        
        var lastRow = [{
            columnWidth: 1/3,
            editDialog: this,
            xtype: 'tinerelationpickercombo',
            fieldLabel: this.app.i18n._('Responsible Person'),
            allowBlank: true,
            app: 'Addressbook',
            recordClass: Tine.Addressbook.Model.Contact,
            relationType: 'RESPONSIBLE',
            relationDegree: 'sibling',
            modelUnique: true
        }, {
            hideLabel: true,
            boxLabel: this.app.i18n._('Timesheets are billable'),
            name: 'is_billable',
            xtype: 'checkbox',
            columnWidth: 1/3
        }];
        
        if (this.useInvoice) {
            lastRow.push(Tine.widgets.form.RecordPickerManager.get('Sales', 'Invoice', {
                columnWidth: 1/3,
                disabled: true,
                fieldLabel: this.app.i18n._('Invoice'),
                name: 'invoice_id'
            }));
        }
        return {
            xtype: 'tabpanel',
            border: false,
            plain:true,
            activeTab: 0,
            plugins: [{
                ptype : 'ux.tabpanelkeyplugin'
            }],
            defaults: {
                hideMode: 'offsets'
            },
            items:[{
                title: this.app.i18n.ngettext('Time Account', 'Time Accounts', 1),
                autoScroll: true,
                border: false,
                frame: true,
                layout: 'border',
                items: [{
                    region: 'center',
                    xtype: 'columnform',
                    labelAlign: 'top',
                    formDefaults: {
                        xtype:'textfield',
                        anchor: '100%',
                        labelSeparator: '',
                        columnWidth: .333
                    },
                    items: [[{
                        fieldLabel: this.app.i18n._('Number'),
                        name: 'number',
                        allowBlank: false,
                        maxLength: 127
                        }, {
                        columnWidth: .666,
                        fieldLabel: this.app.i18n._('Title'),
                        name: 'title',
                        maxLength: 255,
                        allowBlank: false
                        }], [{
                        columnWidth: 1,
                        fieldLabel: this.app.i18n._('Description'),
                        xtype: 'textarea',
                        name: 'description',
                        height: 150
                        }], secondRow, lastRow] 
                }, {
                    // activities and tags
                    layout: 'ux.multiaccordion',
                    animate: true,
                    region: 'east',
                    width: 210,
                    split: true,
                    collapsible: true,
                    collapseMode: 'mini',
                    header: false,
                    margins: '0 5 0 5',
                    border: true,
                    items: [
                    new Tine.widgets.tags.TagPanel({
                        app: 'Timetracker',
                        border: false,
                        bodyStyle: 'border:1px solid #B5B8C8;'
                    })]
                }]
            },{
                title: this.app.i18n._('Access'),
                layout: 'fit',
                items: [this.getGrantsGrid()]
            }, new Tine.widgets.activities.ActivitiesTabPanel({
                app: this.appName,
                record_id: (! this.copyRecord) ? this.record.id : null,
                record_model: this.appName + '_Model_' + this.recordClass.getMeta('modelName')
            })]
        };
    },

    getGrantsGrid: function() {
        if (! this.grantsGrid) {
            this.grantsGrid = new Tine.widgets.container.GrantsGrid({
                selectType: 'both',
                title:  this.app.i18n._('Permissions'),
                alwaysShowAdminGrant: true,
                hasAccountPrefix: true,
                selectAnyone: false,
                selectTypeDefault: 'group',
                recordClass: Tine.Timetracker.Model.TimeaccountGrant
            });
        }
        return this.grantsGrid;
    },
    
    /**
     * is called is billed field changes
     */
    onBilledChange: function(combo, record, index) {
        if (combo.getValue() == 'billed') {
            if (! this.getForm().findField('cleared_at').getValue()) {
                this.getForm().findField('cleared_at').setValue(new Date());
            }
        }
    },
    
    doCopyRecord: function() {
        Tine.Timetracker.TimeaccountEditDialog.superclass.doCopyRecord.call(this);
        
        this.record.set('status', 'not yet billed');
    }
});


/***/ }),

/***/ 842:
/***/ (function(module, exports) {

﻿/*
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.namespace('Tine.Timetracker');

/**
 * Timesheet grid panel
 * 
 * @namespace   Tine.Timetracker
 * @class       Tine.Timetracker.TimesheetGridPanel
 * @extends     Tine.widgets.grid.GridPanel
 * 
 * <p>Timesheet Grid Panel</p>
 * <p><pre>
 * </pre></p>
 * 
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * 
 * @param       {Object} config
 * @constructor
 * Create a new Tine.Timetracker.TimesheetGridPanel
 */
Tine.Timetracker.TimesheetGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
    /**
     * record class
     * @cfg {Tine.Timetracker.Model.Timesheet} recordClass
     */
    recordClass: Tine.Timetracker.Model.Timesheet,

    /**
     * @private grid cfg
     */
    defaultSortInfo: {field: 'start_date', direction: 'DESC'},
    gridConfig: {
        autoExpandColumn: 'description'
    },
    copyEditAction: true,
    multipleEdit: true,

    /**
     * @private
     */

    /**
     * activates copy action
     */
    copyEditAction: true,

    /**
     * only allow multi edit with manage_timeaccounts right (because of timeaccount handling in edit dlg)
     */
    multipleEditRequiredRight: 'manage_timeaccounts',

    initComponent: function() {
        this.defaultFilters = [
            {field: 'start_date', operator: 'within', value: 'weekThis'},
            {field: 'account_id', operator: 'equals', value: Tine.Tinebase.registry.get('currentAccount')}
        ];

        this.initDetailsPanel();
        
        // only eval grants in action updater if user does not have the right to manage timeaccounts
        this.evalGrants = ! Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts');


        Tine.Timetracker.TimesheetGridPanel.superclass.initComponent.call(this);
    },

    /**
     * @private
     */
    initDetailsPanel: function() {
        this.detailsPanel = new Tine.widgets.grid.DetailsPanel({
            gridpanel: this,
            
            // use default Tpl for default and multi view
            defaultTpl: new Ext.XTemplate(
                '<div class="preview-panel-timesheet-nobreak">',
                    '<!-- Preview timeframe -->',           
                    '<div class="preview-panel preview-panel-timesheet-left">',
                        '<div class="bordercorner_1"></div>',
                        '<div class="bordercorner_2"></div>',
                        '<div class="bordercorner_3"></div>',
                        '<div class="bordercorner_4"></div>',
                        '<div class="preview-panel-declaration">' /*+ this.app.i18n._('timeframe')*/ + '</div>',
                        '<div class="preview-panel-timesheet-leftside preview-panel-left">',
                            '<span class="preview-panel-bold">',
                            /*'First Entry'*/'<br/>',
                            /*'Last Entry*/'<br/>',
                            /*'Duration*/'<br/>',
                            '<br/>',
                            '</span>',
                        '</div>',
                        '<div class="preview-panel-timesheet-rightside preview-panel-left">',
                            '<span class="preview-panel-nonbold">',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                            '</span>',
                        '</div>',
                    '</div>',
                    '<!-- Preview summary -->',
                    '<div class="preview-panel-timesheet-right">',
                        '<div class="bordercorner_gray_1"></div>',
                        '<div class="bordercorner_gray_2"></div>',
                        '<div class="bordercorner_gray_3"></div>',
                        '<div class="bordercorner_gray_4"></div>',
                        '<div class="preview-panel-declaration">'/* + this.app.i18n._('summary')*/ + '</div>',
                        '<div class="preview-panel-timesheet-leftside preview-panel-left">',
                            '<span class="preview-panel-bold">',
                            this.app.i18n._('Total Timesheets') + '<br/>',
                            this.app.i18n._('Billable Timesheets') + '<br/>',
                            this.app.i18n._('Total Time') + '<br/>',
                            this.app.i18n._('Time of Billable Timesheets') + '<br/>',
                            '</span>',
                        '</div>',
                        '<div class="preview-panel-timesheet-rightside preview-panel-left">',
                            '<span class="preview-panel-nonbold">',
                            '{count}<br/>',
                            '{countbillable}<br/>',
                            '{sum}<br/>',
                            '{sumbillable}<br/>',
                            '</span>',
                        '</div>',
                    '</div>',
                '</div>'            
            ),
            
            showDefault: function(body) {
                
                var data = {
                    count: this.gridpanel.store.proxy.jsonReader.jsonData.totalcount,
                    countbillable: (this.gridpanel.store.proxy.jsonReader.jsonData.totalcountbillable) ? this.gridpanel.store.proxy.jsonReader.jsonData.totalcountbillable : 0,
                    sum:  Tine.Tinebase.common.minutesRenderer(this.gridpanel.store.proxy.jsonReader.jsonData.totalsum),
                    sumbillable: Tine.Tinebase.common.minutesRenderer(this.gridpanel.store.proxy.jsonReader.jsonData.totalsumbillable)
                };
                
                this.defaultTpl.overwrite(body, data);
            },
            
            showMulti: function(sm, body) {
                
                var data = {
                    count: sm.getCount(),
                    countbillable: 0,
                    sum: 0,
                    sumbillable: 0
                };
                sm.each(function(record){
                    
                    data.sum = data.sum + parseInt(record.data.duration);
                    if (record.data.is_billable_combined == '1') {
                        data.countbillable++;
                        data.sumbillable = data.sumbillable + parseInt(record.data.duration);
                    }
                });
                data.sum = Tine.Tinebase.common.minutesRenderer(data.sum);
                data.sumbillable = Tine.Tinebase.common.minutesRenderer(data.sumbillable);
                
                this.defaultTpl.overwrite(body, data);
            },
            
            tpl: new Ext.XTemplate(
                '<div class="preview-panel-timesheet-nobreak">',    
                    '<!-- Preview beschreibung -->',
                    '<div class="preview-panel preview-panel-timesheet-left">',
                        '<div class="bordercorner_1"></div>',
                        '<div class="bordercorner_2"></div>',
                        '<div class="bordercorner_3"></div>',
                        '<div class="bordercorner_4"></div>',
                        '<div class="preview-panel-declaration">' /* + this.app.i18n._('Description') */ + '</div>',
                        '<div class="preview-panel-timesheet-description preview-panel-left">',
                            '<span class="preview-panel-nonbold">',
                             '{[this.encode(values.description)]}',
                            '<br/>',
                            '</span>',
                        '</div>',
                    '</div>',
                    '<!-- Preview detail-->',
                    '<div class="preview-panel-timesheet-right">',
                        '<div class="bordercorner_gray_1"></div>',
                        '<div class="bordercorner_gray_2"></div>',
                        '<div class="bordercorner_gray_3"></div>',
                        '<div class="bordercorner_gray_4"></div>',
                        '<div class="preview-panel-declaration">' /* + this.app.i18n._('Detail') */ + '</div>',
                        '<div class="preview-panel-timesheet-leftside preview-panel-left">',
                        // @todo add custom fields here
                        /*
                            '<span class="preview-panel-bold">',
                            'Ansprechpartner<br/>',
                            'Newsletter<br/>',
                            'Ticketnummer<br/>',
                            'Ticketsubjekt<br/>',
                            '</span>',
                        */
                        '</div>',
                        '<div class="preview-panel-timesheet-rightside preview-panel-left">',
                            '<span class="preview-panel-nonbold">',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                            '<br/>',
                            '</span>',
                        '</div>',
                    '</div>',
                '</div>',{
                encode: function(value, type, prefix) {
                    if (value) {
                        if (type) {
                            switch (type) {
                                case 'longtext':
                                    value = Ext.util.Format.ellipsis(value, 150);
                                    break;
                                default:
                                    value += type;
                            }
                        }
                        
                        var encoded = Ext.util.Format.htmlEncode(value);
                        encoded = Ext.util.Format.nl2br(encoded);
                        
                        return encoded;
                    } else {
                        return '';
                    }
                }
            })
        });
    },

    /**
     * @private
     */
    initActions: function() {
        var hiddenQuickTag = false,
            quicktagName,
            quicktagId;

        quicktagId = Tine.Timetracker.registry.get('quicktagId');
        quicktagName = Tine.Timetracker.registry.get('quicktagName');

        if (!quicktagId || !quicktagName) {
            hiddenQuickTag = true;
        }

        this.actions_massQuickTag = new Ext.Action({
            hidden: hiddenQuickTag,
            requiredGrant: 'editGrant',
            text: String.format(
                this.app.i18n._('Assign \'{0}\' Tag'),
                quicktagName
            ),
            disabled: true,
            allowMultiple: true,
            handler: this.onApplyQuickTag.createDelegate(this),
            iconCls: 'action_tag',
            scope: this
        });

        this.actions_export = new Ext.Action({
            text: this.app.i18n._('Export Timesheets'),
            iconCls: 'action_export',
            scope: this,
            requiredGrant: 'exportGrant',
            disabled: true,
            allowMultiple: true,
            actionUpdater: this.updateExportAction,
            menu: {
                items: [
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as ODS'),
                        format: 'ods',
                        iconCls: 'tinebase-action-export-ods',
                        exportFunction: 'Timetracker.exportTimesheets',
                        gridPanel: this
                    }),
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as CSV'),
                        format: 'csv',
                        iconCls: 'tinebase-action-export-csv',
                        exportFunction: 'Timetracker.exportTimesheets',
                        gridPanel: this
                    }),
                    new Tine.widgets.grid.ExportButton({
                        text: this.app.i18n._('Export as ...'),
                        iconCls: 'tinebase-action-export-xls',
                        exportFunction: 'Timetracker.exportTimesheets',
                        showExportDialog: true,
                        gridPanel: this
                    })
                ]
            }
        });
        
        // register actions in updater
        this.actionUpdater.addActions([
            this.actions_export,
            this.actions_massQuickTag
        ]);
        
        Tine.Timetracker.TimesheetGridPanel.superclass.initActions.call(this);
    },

    /**
     * Apply quick tag to current selection
     */
    onApplyQuickTag: function() {
        var quickTagId,
            filter,
            filterModel,
            me;

        me = this;

        // Tag to assign
        quickTagId = Tine.Timetracker.registry.get('quicktagId');

        // Get filter model for current selection
        filter = this.selectionModel.getSelectionFilter();
        filterModel = this.recordClass.getMeta('appName') + '_Model_' +  this.recordClass.getMeta('modelName') + 'Filter';

        // Send request to backend
        Ext.Ajax.request({
            scope: this,
            timeout: 1800000,
            success: function(response, options) {
                // In case of success, just reload grid
                me.getStore().reload();
            },
            params: {
                method: 'Tinebase.attachTagToMultipleRecords',
                filterData: filter,
                filterName: filterModel,
                tag: quickTagId
            },
            failure: function(response, options) {
                Tine.Tinebase.ExceptionHandler.handleRequestException(response, options);
            }
        });
    },

    /**
     * check user exportGrant for timeaccounts
     * NOTE: manage_timeaccounts ALWAYS allows to export
     *
     * @param action
     * @param grants
     * @param records
     * @returns {boolean}
     */
    updateExportAction: function(action, grants, records) {
        // export should be allowed always if user is allowed to manage timeaccounts
        if (Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts')) {
            action.setDisabled(false);

            // stop further events
            return false;
        }

        // By default disallow export, this apply for example, if there is no selection yet
        // E.g. filter changes and so on
        var exportGrant = false;

        // We need to go through all timeaccounts and check if the user is trying to export a timesheet of a timeaccount
        // where he has no permission to export.
        Ext.each(records, function (record) {
            var timeaccount = record.get('timeaccount_id');
            var c = timeaccount.container_id;
            if (c.hasOwnProperty('account_grants')) {
                var grants = c.account_grants;

                if (!grants.exportGrant) {
                    exportGrant = false;

                    // stop loop
                    return false;
                } else {
                    // If there was at least one selection which had the exportGrant, allow to export
                    exportGrant = true;
                }
            }
        });

        var disable = !exportGrant;
        action.setDisabled(disable);

        // stop further events
        return false;
    },
    
    /**
     * add custom items to context menu
     * 
     * @return {Array}
     */
    getContextMenuItems: function() {
        var items = [
            '-',
            this.actions_massQuickTag,
            this.actions_export
        ];
        
        return items;
    }
});


/***/ }),

/***/ 843:
/***/ (function(module, exports) {

/**
 * Tine 2.0
 * 
 * @package     Timetracker
 * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
 * @author      Philipp Schüle <p.schuele@metaways.de>
 * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
 *
 */
 
Ext.namespace('Tine.Timetracker');

/**
 * Timetracker Edit Dialog
 */
Tine.Timetracker.TimesheetEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog, {

    /**
     * @private
     */
    windowNamePrefix: 'TimesheetEditWindow_',
    appName: 'Timetracker',
    recordClass: Tine.Timetracker.Model.Timesheet,
    recordProxy: Tine.Timetracker.timesheetBackend,
    tbarItems: null,
    evalGrants: false,
    useInvoice: false,
    displayNotes: true,
    context: { 'skipClosedCheck': false },

    windowWidth: 800,
    windowHeight: 500,

    
    /**
     * overwrite update toolbars function (we don't have record grants yet)
     */
    updateToolbars: function(record) {
        this.onTimeaccountUpdate();
        Tine.Timetracker.TimesheetEditDialog.superclass.updateToolbars.call(this, record, 'timeaccount_id');
    },

    onRecordLoad: function() {
        // interrupt process flow until dialog is rendered
        if (! this.rendered) {
            this.onRecordLoad.defer(250, this);
            return;
        }

        if (! this.record.id) {
            // @todo: this should be handled by default values
            this.record.set('account_id', Tine.Tinebase.registry.get('currentAccount'));
            this.record.set('start_date', new Date());
        }

        Tine.Timetracker.TimesheetEditDialog.superclass.onRecordLoad.call(this);

        // TODO get timeaccount from filter if set
        var timeaccount = this.record.get('timeaccount_id');
        if (timeaccount) {
            this.onTimeaccountUpdate(null, new Tine.Timetracker.Model.Timeaccount(timeaccount));
        }
    },

    /**
     * this gets called when initializing and if a new timeaccount is chosen
     * 
     * @param {} field
     * @param {} timeaccount
     */
    onTimeaccountUpdate: function(field, timeaccount) {
        // check for manage_timeaccounts right
        var manageRight = Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts');
        
        var notBillable = false;
        var notClearable = false;

        // TODO timeaccount.get('account_grants') contains [Object object] -> why is that so? this should be fixed
        var grants = this.record.get('timeaccount_id')
            ? this.record.get('timeaccount_id').account_grants
            : (timeaccount && timeaccount.get('container_id') && timeaccount.get('container_id').account_grants
                ? timeaccount.get('container_id').account_grants
                :  {});
        
        if (grants) {
            var setDisabled = ! (grants.bookAllGrant || grants.adminGrant || manageRight);
            var accountField = this.getForm().findField('account_id');
            accountField.setDisabled(setDisabled);
            // set account id to the current user, if he doesn't have the right to edit other users timesheets
            if (setDisabled) {
                if (this.copyRecord && (this.record.get('account_id') != Tine.Tinebase.registry.get('currentAccount').accountId)) {
                    accountField.setValue(Tine.Tinebase.registry.get('currentAccount'));
                }
            }
            notBillable = ! (grants.manageBillableGrant || grants.adminGrant || manageRight);
            notClearable = ! (grants.adminGrant || manageRight);
            this.getForm().findField('billed_in').setDisabled(! (grants.adminGrant || manageRight));
        }

        if (timeaccount && timeaccount.data) {
            notBillable = notBillable || timeaccount.data.is_billable == "0" || timeaccount.get('is_billable') == "0";
            
            // clearable depends on timeaccount is_billable as well (changed by ps / 2009-09-01, behaviour was inconsistent)
            notClearable = notClearable || timeaccount.data.is_billable == "0" || timeaccount.get('is_billable') == "0";

            if (timeaccount.data.is_billable == "0" || timeaccount.get('is_billable') == "0") {
                this.getForm().findField('is_billable').setValue(false);
            }

            this.getForm().findField('timeaccount_description').setValue(timeaccount.data.description);
        }

        this.getForm().findField('is_billable').setDisabled(notBillable);
        this.getForm().findField('is_cleared').setDisabled(notClearable);
        
        if (this.record.id == 0 && timeaccount) {
            // set is_billable for new records according to the timeaccount setting
            this.getForm().findField('is_billable').setValue(timeaccount.data.is_billable);
        }
    },
    
    /**
     * Always set is_billable if timeaccount is billable. This is needed for copied sheets where the
     * original is set to not billable
     */
    onAfterRecordLoad: function() {
        Tine.Timetracker.TimesheetEditDialog.superclass.onAfterRecordLoad.call(this);
        if (this.record.id == 0 && this.record.get('timeaccount_id') && this.record.get('timeaccount_id').is_billable) {
            this.getForm().findField('is_billable').setValue(this.record.get('timeaccount_id').is_billable);
        }

        var focusFieldName = this.record.get('timeaccount_id') ? 'duration' : 'timeaccount_id',
            focusField = this.getForm().findField(focusFieldName);

        focusField.focus(true, 250);
    },

    /**
     * this gets called when initializing and if cleared checkbox is changed
     *
     * @param {} field
     * @param {} newValue
     *
     * @todo    add prompt later?
     */
    onClearedUpdate: function(field, checked) {
        if (!this.useMultiple) {
            this.getForm().findField('billed_in').setDisabled(! checked);
        }
    },

    initComponent: function() {
        var salesApp = Tine.Tinebase.appMgr.get('Sales');
        this.useInvoice = Tine.Tinebase.appMgr.get('Sales')
            && salesApp.featureEnabled('invoicesModule')
            && Tine.Tinebase.common.hasRight('manage', 'Sales', 'invoices')
            && Tine.Sales.Model.Invoice;
        
        Tine.Timetracker.TimesheetEditDialog.superclass.initComponent.call(this);
    },

    /**
     * overwrites the isValid method on multipleEdit
     */
    isMultipleValid: function() {
        var valid = true;
        var keys = ['timeaccount_id', 'description', 'account_id'];
        Ext.each(keys, function(key) {
            var field = this.getForm().findField(key);
            if(field.edited && ! field.validate()) {
                field.markInvalid();
                valid = false;
            }
        }, this);
        return valid;
    },

    /**
     * returns dialog
     * 
     * NOTE: when this method gets called, all initialization is done.
     */
    getFormItems: function() {
        var lastRow = [new Tine.Addressbook.SearchCombo({
            allowBlank: false,
            forceSelection: true,
            columnWidth: 1,
            disabled: true,
            useAccountRecord: true,
            userOnly: true,
            nameField: 'n_fileas',
            fieldLabel: this.app.i18n._('Account'),
            name: 'account_id'
        }), {
            columnWidth: .25,
            disabled: (this.useMultiple) ? false : true,
            boxLabel: this.app.i18n._('Billable'),
            name: 'is_billable',
            xtype: 'checkbox'
        }, {
            columnWidth: .25,
            disabled: (this.useMultiple) ? false : true,
            boxLabel: this.app.i18n._('Cleared'),
            name: 'is_cleared',
            xtype: 'checkbox',
            listeners: {
                scope: this,
                check: this.onClearedUpdate
            }
        }, {
                columnWidth: .5,
                disabled: true,
                fieldLabel: this.app.i18n._('Cleared In'),
                name: 'billed_in'
            }];
        
        if (this.useInvoice) {
            lastRow.push(Tine.widgets.form.RecordPickerManager.get('Sales', 'Invoice', {
                columnWidth: .5,
                disabled: true,
                fieldLabel: this.app.i18n._('Invoice'),
                name: 'invoice_id'
            }));
        }
        
        
        return {
            xtype: 'tabpanel',
            border: false,
            plain:true,
            activeTab: 0,
            plugins: [{
                ptype : 'ux.tabpanelkeyplugin'
            }],
            defaults: {
                hideMode: 'offsets'
            },
            items:[
                {
                title: this.app.i18n.ngettext('Timesheet', 'Timesheets', 1),
                autoScroll: true,
                border: false,
                frame: true,
                layout: 'border',
                items: [{
                    region: 'center',
                    xtype: 'columnform',
                    labelAlign: 'top',
                    formDefaults: {
                        xtype:'textfield',
                        anchor: '100%',
                        labelSeparator: '',
                        columnWidth: .333
                    },
                    items: [[Tine.widgets.form.RecordPickerManager.get('Timetracker', 'Timeaccount', {
                        columnWidth: 1,
                        fieldLabel: this.app.i18n.ngettext('Time Account', 'Time Accounts', 1),
                        emptyText: this.app.i18n._('Select Time Account...'),
                        allowBlank: false,
                        forceSelection: true,
                        name: 'timeaccount_id',
                        listeners: {
                            scope: this,
                            select: this.onTimeaccountUpdate
                        },
                        lazyInit: false
                    })], [{
                        fieldLabel: this.app.i18n._('Duration'),
                        name: 'duration',
                        selectOnFocus: true,
                        allowBlank: false,
                        xtype: 'tinedurationspinner'
                        }, {
                        fieldLabel: this.app.i18n._('Date'),
                        name: 'start_date',
                        allowBlank: false,
                        xtype: 'datefield'
                        }, {
                        fieldLabel: this.app.i18n._('Start'),
                        emptyText: this.app.i18n._('not set'),
                        name: 'start_time',
                        xtype: 'timefield'
                    }], [{
                        columnWidth: 1,
                        fieldLabel: this.app.i18n._('Description'),
                        emptyText: this.app.i18n._('Enter description...'),
                        name: 'description',
                        allowBlank: false,
                        xtype: 'textarea',
                        height: 150
                    }], lastRow] 
                }, {
                    // activities and tags
                    layout: 'ux.multiaccordion',
                    animate: true,
                    region: 'east',
                    width: 210,
                    split: true,
                    collapsible: true,
                    collapseMode: 'mini',
                    header: false,
                    margins: '0 5 0 5',
                    border: true,
                    items: [
                        new Tine.widgets.tags.TagPanel({
                            app: 'Timetracker',
                            border: false,
                            bodyStyle: 'border:1px solid #B5B8C8;'
                        })
                    ]
                }]
            }, {
                    title: this.app.i18n._('Timeaccount'),
                    autoScroll: true,
                    border: false,
                    frame: true,
                    layout: 'border',
                    items: [{
                        region: 'center',
                        layout: 'fit',
                        height: 400,
                        flex: 1,
                        border: false,
                        style: 'padding-bottom: 5px;',
                        items: [{
                            xtype: 'textarea',
                            name: 'timeaccount_description',
                            grow: false,
                            preventScrollbars: false,
                            fieldLabel: this.app.i18n._('Description'),
                            readOnly: true
                        }]
                    }]
                }, new Tine.widgets.activities.ActivitiesTabPanel({
                app: this.appName,
                record_id: (! this.copyRecord) ? this.record.id : null,
                record_model: this.appName + '_Model_' + this.recordClass.getMeta('modelName')
            })]
        };
    },
    
    /**
     * returns additional save params
     *
     * @returns {{checkBusyConflicts: boolean}}
     */
    getAdditionalSaveParams: function() {
        return {
            context: this.context
        };
    },
    
    /**
     * show error if request fails
     * 
     * @param {} response
     * @param {} request
     * @private
     */
    onRequestFailed: function(response, request) {
        this.saving = false;
        
        if (response.code && response.code == 902) {
            // deadline exception
            Ext.MessageBox.alert(
                this.app.i18n._('Failed'), 
                String.format(this.app.i18n._('Could not save {0}.'), this.i18nRecordName) 
                    + ' ( ' + this.app.i18n._('Booking deadline for this Timeaccount has been exceeded.') /* + ' ' + response.message  */ + ')'
            );
        } else if (response.code && response.code == 444) {
            //Time Account is closed
            if(Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts')) {
                this.onClosedWarning.apply(this, arguments);
            } else {
                Ext.MessageBox.alert(
                    this.app.i18n._('Closed Timeaccount Warning!'), 
                    String.format(this.app.i18n._('The selected Time Account is already closed.'))
                );
            }
        } else {
            // call default exception handler
            Tine.Tinebase.ExceptionHandler.handleRequestException(response);
        }
        this.loadMask.hide();
    },
    
    onClosedWarning: function() {
        Ext.Msg.confirm(this.app.i18n._('Closed Timeaccount Warning!'),
            this.app.i18n._('The selected Time Account is already closed. Do you wish to continue anyway?'),
            function(btn) {
                if (btn == 'yes') {
                    this.context = { 'skipClosedCheck': true };
                    this.onApplyChanges(true);
                }
            }, this);
    }
});


/***/ }),

/***/ 844:
/***/ (function(module, exports, __webpack_require__) {

// style-loader: Adds some css to the DOM by adding a <style> tag

// load the styles
var content = __webpack_require__(845);
if(typeof content === 'string') content = [[module.i, content, '']];
// Prepare cssTransformation
var transform;

var options = {"hmr":true}
options.transform = transform
// add the styles to the DOM
var update = __webpack_require__(1)(content, options);
if(content.locals) module.exports = content.locals;
// Hot Module Replacement
if(false) {
	// When the styles change, update the <style> tags
	if(!content.locals) {
		module.hot.accept("!!../../Tinebase/js/node_modules/css-loader/index.js!./Timetracker.css", function() {
			var newContent = require("!!../../Tinebase/js/node_modules/css-loader/index.js!./Timetracker.css");
			if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
			update(newContent);
		});
	}
	// When the module is disposed, remove the <style> tags
	module.hot.dispose(function() { update(); });
}

/***/ }),

/***/ 845:
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__(0)(undefined);
// imports


// module
exports.push([module.i, "/**\n * Tine 2.0\n * \n * @package     Timetracker\n * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3\n * @author      Philipp Schuele <p.schuele@metaways.de>\n * @copyright   Copyright (c) 2007-2008 Metaways Infosystems GmbH (http://www.metaways.de)\n * @version     $Id: Tasks.css 3797 2008-08-11 08:33:21Z c.weiss@metaways.de $\n *\n */\n \n \n.TimetrackerIconCls {\n    background-image:url(" + __webpack_require__(846) + ") !important;\n}\n\n.TimetrackerTimesheet {\n    background-image:url(" + __webpack_require__(847) + ") !important;\n}\n\n.TimetrackerTimeaccount {\n    background-image:url(" + __webpack_require__(109) + ") !important;\n}\n\n.preview-panel-timesheet-description {\n    width: 98%;\n    position: relative;\n    height: 97px;\n    padding:3px;\n    position: relative;\n    }\n.preview-panel-timesheet-left {\n    float:left;\n    position: relative;\n    width:48%;\n    height: 102px;\n    padding:5px;\n    margin:5px 2px 5px 5px;\n    font-family:verdana;\n    }\n.preview-panel-timesheet-right {\n    float:left;\n    position: relative;\n    width:48%;\n    height: 97px;\n    padding:5px;\n    margin:5px 1px 5px 2px;\n    border: solid 3px #e5e6fe;\n    font-family:verdana;\n    }\n.preview-panel-timesheet-leftside {\n    width:180px;\n    padding: 5px;\n    }\n.preview-panel-timesheet-rightside {\n    width:auto;\n    padding: 5px;\n    }\n.preview-panel-timesheet-nobreak {\n    min-width:900px;\n    }", ""]);

// exports


/***/ }),

/***/ 846:
/***/ (function(module, exports) {

module.exports = "\"data:image/svg+xml,%3C?xml version='1.0' encoding='utf-8'?%3E %3C!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E %3Csvg version='1.1' id='Ebene_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 42.5 42.5' style='enable-background:new 0 0 42.5 42.5;' xml:space='preserve' width='42.5' height='42.5'%3E %3Cpath id='Icon_Zeiterfassung' d='M23.8,14.2v9.9c0,0.2-0.1,0.4-0.2,0.5s-0.3,0.2-0.5,0.2H16c-0.2,0-0.4-0.1-0.5-0.2 s-0.2-0.3-0.2-0.5v-1.4c0-0.2,0.1-0.4,0.2-0.5S15.8,22,16,22h5v-7.8c0-0.2,0.1-0.4,0.2-0.5s0.3-0.2,0.5-0.2h1.4 c0.2,0,0.4,0.1,0.5,0.2S23.8,14,23.8,14.2z M35.6,22c0-2.6-0.7-5.1-2-7.3s-3.1-4-5.3-5.3s-4.7-2-7.3-2s-5.1,0.7-7.3,2 s-4,3.1-5.3,5.3s-2,4.7-2,7.3s0.7,5.1,2,7.3s3.1,4,5.3,5.3s4.7,2,7.3,2s5.1-0.7,7.3-2s4-3.1,5.3-5.3S35.6,24.6,35.6,22z M38,22 c0,3.1-0.8,5.9-2.3,8.5c-1.5,2.6-3.6,4.7-6.2,6.2C26.9,38.2,24.1,39,21,39c-3.1,0-5.9-0.8-8.5-2.3c-2.6-1.5-4.7-3.6-6.2-6.2 S4,25.1,4,22c0-3.1,0.8-5.9,2.3-8.5s3.6-4.7,6.2-6.2S17.9,5,21,5c3.1,0,5.9,0.8,8.5,2.3s4.7,3.6,6.2,6.2C37.2,16.1,38,18.9,38,22z'/%3E %3C/svg%3E\""

/***/ }),

/***/ 847:
/***/ (function(module, exports) {

module.exports = "\"data:image/svg+xml,%3C?xml version='1.0' encoding='utf-8'?%3E %3C!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E %3Csvg version='1.1' id='Ebene_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 42.5 42.5' style='enable-background:new 0 0 42.5 42.5;' xml:space='preserve' width='42.5' height='42.5'%3E %3Cstyle type='text/css'%3E .st0%7Bfill:none;%7D %3C/style%3E %3Cg%3E %3Cpath class='st0' d='M28,12.2l-5.5-5.5c-0.1-0.1-0.4-0.3-0.7-0.4v6.6h6.6C28.3,12.6,28.2,12.4,28,12.2z'/%3E %3Cpath d='M17.7,33H6.2V6.2h13.4v7.3c0,0.5,0.2,0.9,0.5,1.2s0.7,0.5,1.2,0.5h7.3v1.2c0.8,0,1.5,0.1,2.2,0.3v-3.1c0-0.5-0.1-1-0.3-1.5 c-0.2-0.6-0.5-1-0.8-1.3l-5.4-5.4c-0.3-0.3-0.8-0.6-1.3-0.8C22.3,4.1,21.8,4,21.3,4H5.7C5.2,4,4.8,4.2,4.5,4.5S4,5.2,4,5.7v27.9 c0,0.5,0.2,0.9,0.5,1.2s0.7,0.5,1.2,0.5H19c-0.3-0.4-0.7-0.9-0.9-1.4C17.9,33.6,17.8,33.3,17.7,33z M21.9,6.4 c0.3,0.1,0.6,0.2,0.7,0.4l5.5,5.5c0.1,0.1,0.3,0.4,0.4,0.7h-6.6V6.4z'/%3E %3C/g%3E %3Cpath id='Icon_Zeiterfassung_3_' d='M30.8,23.6v7.9c0,0.2-0.1,0.3-0.2,0.4c-0.1,0.1-0.2,0.2-0.4,0.2h-5.7c-0.2,0-0.3-0.1-0.4-0.2 c-0.1-0.1-0.2-0.2-0.2-0.4v-1.1c0-0.2,0.1-0.3,0.2-0.4s0.2-0.2,0.4-0.2h4v-6.2c0-0.2,0.1-0.3,0.2-0.4c0.1-0.1,0.2-0.2,0.4-0.2h1.1 c0.2,0,0.3,0.1,0.4,0.2C30.8,23.3,30.8,23.4,30.8,23.6z M36.2,27.9c0-1.4-0.4-2.7-1.1-3.9c-0.7-1.2-1.7-2.2-2.9-2.9 s-2.5-1.1-3.9-1.1s-2.7,0.4-3.9,1.1c-1.2,0.7-2.2,1.7-2.9,2.9c-0.7,1.2-1.1,2.5-1.1,3.9c0,1.4,0.4,2.7,1.1,3.9s1.7,2.2,2.9,2.9 c1.2,0.7,2.5,1.1,3.9,1.1s2.7-0.4,3.9-1.1s2.2-1.7,2.9-2.9C35.8,30.7,36.2,29.4,36.2,27.9z M38.2,27.9c0,1.8-0.4,3.4-1.3,5 c-0.9,1.5-2.1,2.7-3.6,3.6c-1.5,0.9-3.2,1.3-5,1.3c-1.8,0-3.4-0.4-5-1.3c-1.5-0.9-2.7-2.1-3.6-3.6c-0.9-1.5-1.3-3.2-1.3-5 c0-1.8,0.4-3.4,1.3-5c0.9-1.5,2.1-2.7,3.6-3.6c1.5-0.9,3.2-1.3,5-1.3c1.8,0,3.4,0.4,5,1.3s2.7,2.1,3.6,3.6 C37.8,24.5,38.2,26.2,38.2,27.9z'/%3E %3C/svg%3E\""

/***/ })

/******/ });
});