PageSourceSearch

https://mpk.czest.pl/public/cloud/core/js/js.js?v=42bc9a437a4186d3a159aca89981aff3

js mpk.czest.pl collected 2026-10-04 20:40:35 UTC 90,097 bytes, 3,232 lines download raw bytes

1/**
2 * Disable console output unless DEBUG mode is enabled.
3 * Add
4 *      'debug' => true,
5 * To the definition of $CONFIG in config/config.php to enable debug mode.
6 * The undefined checks fix the broken ie8 console
7 */
8
9/* global oc_isadmin */
10
11var oc_debug;
12var oc_webroot;
13
14var oc_requesttoken = document.getElementsByTagName('head')[0].getAttribute('data-requesttoken');
15
16window.oc_config = window.oc_config || {};
17
18if (typeof oc_webroot === "undefined") {
19	oc_webroot = location.pathname;
20	var pos = oc_webroot.indexOf('/index.php/');
21	if (pos !== -1) {
22		oc_webroot = oc_webroot.substr(0, pos);
23	}
24	else {
25		oc_webroot = oc_webroot.substr(0, oc_webroot.lastIndexOf('/'));
26	}
27}
28
29/**
30 * Sanitizes a HTML string by replacing all potential dangerous characters with HTML entities
31 * @param {string} s String to sanitize
32 * @return {string} Sanitized string
33 */
34function escapeHTML(s) {
35	return s.toString().split('&').join('&amp;').split('<').join('&lt;').split('>').join('&gt;').split('"').join('&quot;').split('\'').join('&#039;');
36}
37
38/**
39 * Get the path to download a file
40 * @param {string} file The filename
41 * @param {string} dir The directory the file is in - e.g. $('#dir').val()
42 * @return {string} Path to download the file
43 * @deprecated use Files.getDownloadURL() instead
44 */
45function fileDownloadPath(dir, file) {
46	return OC.filePath('files', 'ajax', 'download.php') + '?files=' + encodeURIComponent(file) + '&dir=' + encodeURIComponent(dir);
47}
48
49/** @namespace */
50var OC = {
51	PERMISSION_CREATE: 4,
52	PERMISSION_READ: 1,
53	PERMISSION_UPDATE: 2,
54	PERMISSION_DELETE: 8,
55	PERMISSION_SHARE: 16,
56	PERMISSION_ALL: 31,
57	TAG_FAVORITE: '_$!<Favorite>!$_',
58	/* jshint camelcase: false */
59	/**
60	 * Relative path to ownCloud root.
61	 * For example: "/owncloud"
62	 *
63	 * @type string
64	 *
65	 * @deprecated since 8.2, use OC.getRootPath() instead
66	 * @see OC#getRootPath
67	 */
68	webroot: oc_webroot,
69
70	/**
71	 * Capabilities
72	 *
73	 * @type array
74	 */
75	_capabilities: window.oc_capabilities || null,
76
77	appswebroots: (typeof oc_appswebroots !== 'undefined') ? oc_appswebroots : false,
78	/**
79	 * Currently logged in user or null if none
80	 *
81	 * @type String
82	 * @deprecated use {@link OC.getCurrentUser} instead
83	 */
84	currentUser: (typeof oc_user !== 'undefined') ? oc_user.uid : false,
85	config: window.oc_config,
86	appConfig: window.oc_appconfig || {},
87	theme: window.oc_defaults || {},
88	coreApps: ['', 'admin', 'log', 'core/search', 'settings', 'core', '3rdparty'],
89	requestToken: oc_requesttoken,
90	menuSpeed: 50,
91	currentTheme: window.theme || {},
92
93	/**
94	 * Get an absolute url to a file in an app
95	 * @param {string} app the id of the app the file belongs to
96	 * @param {string} file the file path relative to the app folder
97	 * @return {string} Absolute URL to a file
98	 */
99	linkTo: function (app, file) {
100		return OC.filePath(app, '', file);
101	},
102
103	/**
104	 * Creates a relative url for remote use
105	 * @param {string} service id
106	 * @return {string} the url
107	 */
108	linkToRemoteBase: function (service) {
109		return OC.getRootPath() + '/remote.php/' + service;
110	},
111
112	/**
113	 * @brief Creates an absolute url for remote use
114	 * @param {string} service id
115	 * @return {string} the url
116	 */
117	linkToRemote: function (service) {
118		return window.location.protocol + '//' + window.location.host + OC.linkToRemoteBase(service);
119	},
120
121	/**
122	 * Gets the base path for the given OCS API service.
123	 * @param {string} service name
124	 * @param {int} version OCS API version
125	 * @return {string} OCS API base path
126	 */
127	linkToOCS: function (service, version) {
128		version = (version !== 2) ? 1 : 2;
129		return OC.getProtocol() + '://' + OC.getHost() + OC.getRootPath() + '/ocs/v' + version + '.php/' + service + '/';
130	},
131
132	/**
133	 * Generates the absolute url for the given relative url, which can contain parameters.
134	 * Parameters will be URL encoded automatically.
135	 * @param {string} url
136	 * @param [params] params
137	 * @param [options] options
138	 * @param {bool} [options.escape=true] enable/disable auto escape of placeholders (by default enabled)
139	 * @return {string} Absolute URL for the given relative URL
140	 */
141	generateUrl: function (url, params, options) {
142		var defaultOptions = {
143				escape: true
144			},
145			allOptions = options || {};
146		_.defaults(allOptions, defaultOptions);
147
148		var _build = function (text, vars) {
149			var vars = vars || [];
150			return text.replace(/{([^{}]*)}/g,
151				function (a, b) {
152					var r = (vars[b]);
153					if (allOptions.escape) {
154						return (typeof r === 'string' || typeof r === 'number') ? encodeURIComponent(r) : encodeURIComponent(a);
155					} else {
156						return (typeof r === 'string' || typeof r === 'number') ? r : a;
157					}
158				}
159			);
160		};
161		if (url.charAt(0) !== '/') {
162			url = '/' + url;
163
164		}
165
166		var webRoot = OC.getRootPath();
167		if (oc_config.modRewriteWorking == true) {
168			return webRoot + _build(url, params);
169		}
170
171		return webRoot + '/index.php' + _build(url, params);
172	},
173
174	/**
175	 * Get the absolute url for a file in an app
176	 * @param {string} app the id of the app
177	 * @param {string} type the type of the file to link to (e.g. css,img,ajax.template)
178	 * @param {string} file the filename
179	 * @return {string} Absolute URL for a file in an app
180	 */
181	filePath: function (app, type, file) {
182		var isCore = OC.coreApps.indexOf(app) !== -1,
183			link = OC.getRootPath();
184		if (file.substring(file.length - 3) === 'php' && !isCore) {
185			link += '/index.php/apps/' + app;
186			if (file != 'index.php') {
187				link += '/';
188				if (type) {
189					link += encodeURI(type + '/');
190				}
191				link += file;
192			}
193		} else if (file.substring(file.length - 3) !== 'php' && !isCore) {
194			link = OC.appswebroots[app];
195			if (type) {
196				link += '/' + type + '/';
197			}
198			if (link.substring(link.length - 1) !== '/') {
199				link += '/';
200			}
201			link += file;
202		} else {
203			if ((app == 'settings' || app == 'core' || app == 'search') && type == 'ajax') {
204				link += '/index.php/';
205			}
206			else {
207				link += '/';
208			}
209			if (!isCore) {
210				link += 'apps/';
211			}
212			if (app !== '') {
213				app += '/';
214				link += app;
215			}
216			if (type) {
217				link += type + '/';
218			}
219			link += file;
220		}
221		return link;
222	},
223
224    /**
225     * Check if a user file is allowed to be handled.
226     * @param {string} file to check
227     */
228	fileIsBlacklisted: function(file) {
229		return !!(file.match(oc_config.blacklist_files_regex));
230	},
231
232	/**
233	 * Redirect to the target URL, can also be used for downloads.
234	 * @param {string} targetURL URL to redirect to
235	 */
236	redirect: function (targetURL) {
237		window.location = targetURL;
238	},
239
240	/**
241	 * Reloads the current page
242	 */
243	reload: function () {
244		window.location.reload();
245	},
246
247	/**
248	 * Protocol that is used to access this ownCloud instance
249	 * @return {string} Used protocol
250	 */
251	getProtocol: function () {
252		return window.location.protocol.split(':')[0];
253	},
254
255	/**
256	 * Returns the host used to access this ownCloud instance
257	 * Host is sometimes the same as the hostname but now always.
258	 *
259	 * Examples:
260	 * http://example.com => example.com
261	 * https://example.com => example.com
262	 * http://example.com:8080 => example.com:8080
263	 *
264	 * @return {string} host
265	 *
266	 * @since 8.2
267	 */
268	getHost: function () {
269		return window.location.host;
270	},
271
272	/**
273	 * Returns the hostname used to access this ownCloud instance
274	 * The hostname is always stripped of the port
275	 *
276	 * @return {string} hostname
277	 * @since 9.0
278	 */
279	getHostName: function () {
280		return window.location.hostname;
281	},
282
283	/**
284	 * Returns the port number used to access this ownCloud instance
285	 *
286	 * @return {int} port number
287	 *
288	 * @since 8.2
289	 */
290	getPort: function () {
291		return window.location.port;
292	},
293
294	/**
295	 * Returns the web root path where this ownCloud instance
296	 * is accessible, with a leading slash.
297	 * For example "/owncloud".
298	 *
299	 * @return {string} web root path
300	 *
301	 * @since 8.2
302	 */
303	getRootPath: function () {
304		return OC.webroot;
305	},
306
307	/**
308	 * Returns the capabilities
309	 *
310	 * @return {array} capabilities
311	 */
312	getCapabilities: function() {
313		return OC._capabilities;
314	},
315
316	/**
317	 * Returns the currently logged in user or null if there is no logged in
318	 * user (public page mode)
319	 *
320	 * @return {OC.CurrentUser} user spec
321	 * @since 9.0.0
322	 */
323	getCurrentUser: function () {
324		if (!_.isUndefined(window.oc_user)) {
325			return oc_user;
326		}
327		return {
328			uid: null,
329			displayName: null,
330			email: null,
331			groups: [],
332		};
333	},
334
335	/**
336	 * get the absolute path to an image file
337	 * if no extension is given for the image, it will automatically decide
338	 * between .png and .svg based on what the browser supports
339	 * @param {string} app the app id to which the image belongs
340	 * @param {string} file the name of the image file
341	 * @return {string}
342	 */
343	imagePath: function (app, file) {
344		if (file.indexOf('.') == -1) {//if no extension is given, use svg
345			file += '.svg';
346		}
347		return OC.filePath(app, 'img', file);
348	},
349
350	/**
351	 * URI-Encodes a file path but keep the path slashes.
352	 *
353	 * @param path path
354	 * @return encoded path
355	 */
356	encodePath: function (path) {
357		if (!path) {
358			return path;
359		}
360		var parts = path.split('/');
361		var result = [];
362		for (var i = 0; i < parts.length; i++) {
363			result.push(encodeURIComponent(parts[i]));
364		}
365		return result.join('/');
366	},
367
368	/**
369	 * Load a script for the server and load it. If the script is already loaded,
370	 * the event handler will be called directly
371	 * @param {string} app the app id to which the script belongs
372	 * @param {string} script the filename of the script
373	 * @param ready event handler to be called when the script is loaded
374	 */
375	addScript: function (app, script, ready) {
376		var deferred, path = OC.filePath(app, 'js', script + '.js');
377		if (!OC.addScript.loaded[path]) {
378			if (ready) {
379				deferred = $.getScript(path, ready);
380			} else {
381				deferred = $.getScript(path);
382			}
383			OC.addScript.loaded[path] = deferred;
384		} else {
385			if (ready) {
386				ready();
387			}
388		}
389		return OC.addScript.loaded[path];
390	},
391	/**
392	 * Loads a CSS file
393	 * @param {string} app the app id to which the css style belongs
394	 * @param {string} style the filename of the css file
395	 */
396	addStyle: function (app, style) {
397		var path = OC.filePath(app, 'css', style + '.css');
398		if (OC.addStyle.loaded.indexOf(path) === -1) {
399			OC.addStyle.loaded.push(path);
400			if (document.createStyleSheet) {
401				document.createStyleSheet(path);
402			} else {
403				style = $('<link rel="stylesheet" type="text/css" href="' + path + '"/>');
404				$('head').append(style);
405			}
406		}
407	},
408
409	/**
410	 * Loads translations for the given app asynchronously.
411	 *
412	 * @param {String} app app name
413	 * @param {Function} callback callback to call after loading
414	 * @return {Promise}
415	 */
416	addTranslations: function (app, callback) {
417		return OC.L10N.load(app, callback);
418	},
419
420	/**
421	 * Returns the base name of the given path.
422	 * For example for "/abc/somefile.txt" it will return "somefile.txt"
423	 *
424	 * @param {String} path
425	 * @return {String} base name
426	 */
427	basename: function (path) {
428		return path.replace(/\\/g, '/').replace(/.*\//, '');
429	},
430
431	/**
432	 * Returns true if the email regexp matches the email address else false returned
433	 * For example if email is "[email protected]", it will return true.
434	 * If email address is "[email protected]", then false will be returned.
435	 *
436	 * @param emailAddress
437	 * @returns {boolean}
438	 *
439	 * @since 10.1.0
440	 */
441	validateEmail: function(emailAddress) {
442		var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@\.]{2,}$/;
443		return (emailRegex.exec(emailAddress) !== null);
444	},
445
446	/**
447	 * Returns the dir name of the given path.
448	 * For example for "/abc/somefile.txt" it will return "/abc"
449	 *
450	 * @param {String} path
451	 * @return {String} dir name
452	 */
453	dirname: function (path) {
454		return path.replace(/\\/g, '/').replace(/\/[^\/]*$/, '');
455	},
456
457	/**
458	 * Returns whether the given paths are the same, without
459	 * leading, trailing or doubled slashes and also removing
460	 * the dot sections.
461	 *
462	 * @param {String} path1 first path
463	 * @param {String} path2 second path
464	 * @return {bool} true if the paths are the same
465	 *
466	 * @since 9.0
467	 */
468	isSamePath: function (path1, path2) {
469		var filterDot = function (p) {
470			return p !== '.';
471		};
472		var pathSections1 = _.filter((path1 || '').split('/'), filterDot);
473		var pathSections2 = _.filter((path2 || '').split('/'), filterDot);
474		path1 = OC.joinPaths.apply(OC, pathSections1);
475		path2 = OC.joinPaths.apply(OC, pathSections2);
476		return path1 === path2;
477	},
478
479	/**
480	 * Join path sections
481	 *
482	 * @param {...String} path sections
483	 *
484	 * @return {String} joined path, any leading or trailing slash
485	 * will be kept
486	 *
487	 * @since 8.2
488	 */
489	joinPaths: function () {
490		if (arguments.length < 1) {
491			return '';
492		}
493		var path = '';
494		// convert to array
495		var args = Array.prototype.slice.call(arguments);
496		// discard empty arguments
497		args = _.filter(args, function (arg) {
498			return arg.length > 0;
499		});
500		if (args.length < 1) {
501			return '';
502		}
503
504		var lastArg = args[args.length - 1];
505		var leadingSlash = args[0].charAt(0) === '/';
506		var trailingSlash = lastArg.charAt(lastArg.length - 1) === '/';
507		var sections = [];
508		var i;
509		for (i = 0; i < args.length; i++) {
510			sections = sections.concat(args[i].split('/'));
511		}
512		var first = !leadingSlash;
513		for (i = 0; i < sections.length; i++) {
514			if (sections[i] !== '') {
515				if (first) {
516					first = false;
517				} else {
518					path += '/';
519				}
520				path += sections[i];
521			}
522		}
523
524		if (trailingSlash) {
525			// add it back
526			path += '/';
527		}
528		return path;
529	},
530
531	/**
532	 * Do a search query and display the results
533	 * @param {string} query the search query
534	 */
535	search: function (query) {
536		OC.Search.search(query, null, 0, 30);
537	},
538	/**
539	 * Dialog helper for jquery dialogs.
540	 *
541	 * @namespace OC.dialogs
542	 */
543	dialogs: OCdialogs,
544	/**
545	 * Parses a URL query string into a JS map
546	 * @param {string} queryString query string in the format param1=1234&param2=abcde&param3=xyz
547	 * @return {Object.<string, string>} map containing key/values matching the URL parameters
548	 */
549	parseQueryString: function (queryString) {
550		var parts,
551			pos,
552			components,
553			result = {},
554			key,
555			value;
556		if (!queryString) {
557			return null;
558		}
559		pos = queryString.indexOf('?');
560		if (pos >= 0) {
561			queryString = queryString.substr(pos + 1);
562		}
563		parts = queryString.replace(/\+/g, '%20').split('&');
564		for (var i = 0; i < parts.length; i++) {
565			// split on first equal sign
566			var part = parts[i];
567			pos = part.indexOf('=');
568			if (pos >= 0) {
569				components = [
570					part.substr(0, pos),
571					part.substr(pos + 1)
572				];
573			}
574			else {
575				// key only
576				components = [part];
577			}
578			if (!components.length) {
579				continue;
580			}
581			key = decodeURIComponent(components[0]);
582			if (!key) {
583				continue;
584			}
585			// if equal sign was there, return string
586			if (components.length > 1) {
587				result[key] = decodeURIComponent(components[1]);
588			}
589			// no equal sign => null value
590			else {
591				result[key] = null;
592			}
593		}
594		return result;
595	},
596
597	/**
598	 * Builds a URL query from a JS map.
599	 * @param {Object.<string, string>} params map containing key/values matching the URL parameters
600	 * @return {string} String containing a URL query (without question) mark
601	 */
602	buildQueryString: function (params) {
603		if (!params) {
604			return '';
605		}
606		return $.map(params, function (value, key) {
607			var s = encodeURIComponent(key);
608			if (value !== null && typeof(value) !== 'undefined') {
609				s += '=' + encodeURIComponent(value);
610			}
611			return s;
612		}).join('&');
613	},
614
615	/**
616	 * Opens a popup with the setting for an app.
617	 * @param {string} appid The ID of the app e.g. 'calendar', 'contacts' or 'files'.
618	 * @param {boolean|string} loadJS If true 'js/settings.js' is loaded. If it's a string
619	 * it will attempt to load a script by that name in the 'js' directory.
620	 * @param {boolean} [cache] If true the javascript file won't be forced refreshed. Defaults to true.
621	 * @param {string} [scriptName] The name of the PHP file to load. Defaults to 'settings.php' in
622	 * the root of the app directory hierarchy.
623	 */
624	appSettings: function (args) {
625		if (typeof args === 'undefined' || typeof args.appid === 'undefined') {
626			throw {name: 'MissingParameter', message: 'The parameter appid is missing'};
627		}
628		var props = {scriptName: 'settings.php', cache: true};
629		$.extend(props, args);
630		var settings = $('#appsettings');
631		if (settings.length === 0) {
632			throw {
633				name: 'MissingDOMElement',
634				message: 'There has be be an element with id "appsettings" for the popup to show.'
635			};
636		}
637		var popup = $('#appsettings_popup');
638		if (popup.length === 0) {
639			$('body').prepend('<div class="popup hidden" id="appsettings_popup"></div>');
640			popup = $('#appsettings_popup');
641			popup.addClass(settings.hasClass('topright') ? 'topright' : 'bottomleft');
642		}
643		if (popup.is(':visible')) {
644			popup.hide().remove();
645		} else {
646			var arrowclass = settings.hasClass('topright') ? 'up' : 'left';
647			var jqxhr = $.get(OC.filePath(props.appid, '', props.scriptName), function (data) {
648				popup.html(data).ready(function () {
649					popup.prepend('<span class="arrow ' + arrowclass + '"></span><h2>' + t('core', 'Settings') + '</h2><a class="close"></a>').show();
650					popup.find('.close').bind('click', function () {
651						popup.remove();
652					});
653					if (typeof props.loadJS !== 'undefined') {
654						var scriptname;
655						if (props.loadJS === true) {
656							scriptname = 'settings.js';
657						} else if (typeof props.loadJS === 'string') {
658							scriptname = props.loadJS;
659						} else {
660							throw {
661								name: 'InvalidParameter',
662								message: 'The "loadJS" parameter must be either boolean or a string.'
663							};
664						}
665						if (props.cache) {
666							$.ajaxSetup({cache: true});
667						}
668						$.getScript(OC.filePath(props.appid, 'js', scriptname))
669							.fail(function (jqxhr, settings, e) {
670								throw e;
671							});
672					}
673				}).show();
674			}, 'html');
675		}
676	},
677
678	/**
679	 * For menu toggling
680	 * @todo Write documentation
681	 */
682	registerMenu: function ($toggle, $menuEl) {
683		var self = this;
684		$menuEl.addClass('menu');
685		$toggle.on('click.menu', function (event) {
686			// prevent the link event (append anchor to URL)
687			event.preventDefault();
688
689			if ($menuEl.is(OC._currentMenu)) {
690				self.hideMenus();
691				return;
692			}
693			// another menu was open?
694			else if (OC._currentMenu) {
695				// close it
696				self.hideMenus();
697			}
698			$menuEl.slideToggle(OC.menuSpeed);
699			OC._currentMenu = $menuEl;
700			OC._currentMenuToggle = $toggle;
701		});
702	},
703
704	/**
705	 *  @todo Write documentation
706	 */
707	unregisterMenu: function ($toggle, $menuEl) {
708		// close menu if opened
709		if ($menuEl.is(OC._currentMenu)) {
710			this.hideMenus();
711		}
712		$toggle.off('click.menu').removeClass('menutoggle');
713		$menuEl.removeClass('menu');
714	},
715
716	/**
717	 * Hides any open menus
718	 *
719	 * @param {Function} complete callback when the hiding animation is done
720	 */
721	hideMenus: function (complete) {
722		if (OC._currentMenu) {
723			var lastMenu = OC._currentMenu;
724			OC._currentMenu.trigger(new $.Event('beforeHide'));
725			OC._currentMenu.slideUp(OC.menuSpeed, function () {
726				lastMenu.trigger(new $.Event('afterHide'));
727				if (complete) {
728					complete.apply(this, arguments);
729				}
730			});
731		}
732		OC._currentMenu = null;
733		OC._currentMenuToggle = null;
734	},
735
736	/**
737	 * Shows a given element as menu
738	 *
739	 * @param {Object} [$toggle=null] menu toggle
740	 * @param {Object} $menuEl menu element
741	 * @param {Function} complete callback when the showing animation is done
742	 */
743	showMenu: function ($toggle, $menuEl, complete) {
744		if ($menuEl.is(OC._currentMenu)) {
745			return;
746		}
747		this.hideMenus();
748		OC._currentMenu = $menuEl;
749		OC._currentMenuToggle = $toggle;
750		$menuEl.trigger(new $.Event('beforeShow'));
751		$menuEl.show();
752		$menuEl.trigger(new $.Event('afterShow'));
753		// no animation
754		if (_.isFunction(complete)) {
755			complete();
756		}
757	},
758
759	/**
760	 * Wrapper for matchMedia
761	 *
762	 * This is makes it possible for unit tests to
763	 * stub matchMedia (which doesn't work in PhantomJS)
764	 * @private
765	 */
766	_matchMedia: function (media) {
767		if (window.matchMedia) {
768			return window.matchMedia(media);
769		}
770		return false;
771	},
772
773	/**
774	 * Returns the user's locale
775	 *
776	 * @return {String} locale string
777	 */
778	getLocale: function () {
779		return $('html').prop('lang');
780	},
781
782	/**
783	 * Returns whether the current user is an administrator
784	 *
785	 * @return {bool} true if the user is an admin, false otherwise
786	 * @since 9.0.0
787	 */
788	isUserAdmin: function () {
789		return oc_isadmin;
790	},
791
792	/**
793	 * Process ajax error, redirects to main page
794	 * if an error/auth error status was returned.
795	 */
796	_processAjaxError: function (xhr) {
797		var self = this;
798		// purposefully aborted request ?
799		// this._userIsNavigatingAway needed to distinguish ajax calls cancelled by navigating away
800		// from calls cancelled by failed cross-domain ajax due to SSO redirect
801		if (xhr.status === 0 && (xhr.statusText === 'abort' || xhr.statusText === 'timeout' || self._reloadCalled)) {
802			return;
803		}
804
805		if (_.contains([0, 302, 303, 307, 401], xhr.status)) {
806			// sometimes "beforeunload" happens later, so need to defer the reload a bit
807			setTimeout(function () {
808				if (!self._userIsNavigatingAway && !self._reloadCalled) {
809					OC.Notification.show(t('core', 'Problem loading page, reloading in 5 seconds'));
810					setTimeout(OC.reload, 5000);
811					// only call reload once
812					self._reloadCalled = true;
813				}
814			}, 100);
815		}
816	},
817
818	/**
819	 * Registers XmlHttpRequest object for global error processing.
820	 *
821	 * This means that if this XHR object returns 401 or session timeout errors,
822	 * the current page will automatically be reloaded.
823	 *
824	 * @param {XMLHttpRequest} xhr
825	 */
826	registerXHRForErrorProcessing: function (xhr) {
827		var loadCallback = function () {
828			if (xhr.readyState !== 4) {
829				return;
830			}
831
832			if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
833				return;
834			}
835
836			// fire jquery global ajax error handler
837			$(document).trigger(new $.Event('ajaxError'), xhr);
838		};
839
840		var errorCallback = function () {
841			// fire jquery global ajax error handler
842			$(document).trigger(new $.Event('ajaxError'), xhr);
843		};
844
845		if (xhr.addEventListener) {
846			xhr.addEventListener('load', loadCallback);
847			xhr.addEventListener('error', errorCallback);
848		}
849
850	}
851};
852
853/**
854 * Current user attributes
855 *
856 * @typedef {Object} OC.CurrentUser
857 *
858 * @property {String} uid user id
859 * @property {String} displayName display name
860 * @property {Array} groups users group ids
861 */
862
863/**
864 * @namespace OC.Plugins
865 */
866OC.Plugins = {
867	/**
868	 * @type Array.<OC.Plugin>
869	 */
870	_plugins: {},
871
872	/**
873	 * Register plugin
874	 *
875	 * @param {String} targetName app name / class name to hook into
876	 * @param {OC.Plugin} plugin
877	 * @param {String} priority priority of the plugin. sortPluginsByPriority()
878	 * can be used to sort given plugins by their priority.
879	 */
880	register: function (targetName, plugin, priority) {
881		if (!priority) {
882			priority = 50;
883		}
884		var plugins = this._plugins[targetName];
885		if (!plugins) {
886			plugins = this._plugins[targetName] = [];
887		}
888		plugins.push({plugin: plugin, priority: priority});
889	},
890
891	/**
892	 * Returns all plugin registered to the given target
893	 * name / app name / class name.
894	 *
895	 * @param {String} targetName app name / class name to hook into
896	 * @return {Array.<OC.Plugin>} array of plugins
897	 */
898	getPlugins: function (targetName) {
899		return this._plugins[targetName] || [];
900	},
901
902	/**
903	 * Sort a list of given plugins by their priority.
904	 * A higher priority means the plugin is listed before others.
905	 *
906	 * @param {Array.<OC.Plugin>} plugins
907	 */
908	sortPluginsByPriority: function (plugins) {
909		return plugins.sort(function(a, b) {
910			if (a.priority > b.priority) {
911				return -1;
912			}
913			if (a.priority < b.priority) {
914				return 1;
915			}
916			return 0;
917		});
918	},
919
920	/**
921	 * Call attach() on all plugins registered to the given target name.
922	 *
923	 * @param {String} targetName app name / class name
924	 * @param {Object} object to be extended
925	 * @param {Object} [options] options
926	 */
927	attach: function (targetName, targetObject, options) {
928		var plugins = this.getPlugins(targetName);
929		plugins = this.sortPluginsByPriority(plugins);
930		for (var i = 0; i < plugins.length; i++) {
931			var plugin = plugins[i].plugin;
932			if (plugin.attach) {
933				plugin.attach(targetObject, options);
934			}
935		}
936	},
937
938	/**
939	 * Call detach() on all plugins registered to the given target name.
940	 *
941	 * @param {String} targetName app name / class name
942	 * @param {Object} object to be extended
943	 * @param {Object} [options] options
944	 */
945	detach: function (targetName, targetObject, options) {
946		var plugins = this.getPlugins(targetName);
947		plugins = this.sortPluginsByPriority(plugins);
948		for (var i = 0; i < plugins.length; i++) {
949			var plugin = plugins[i].plugin;
950			if (plugin.detach) {
951				plugin.detach(targetObject, options);
952			}
953		}
954	},
955
956	/**
957	 * Plugin
958	 *
959	 * @todo make this a real class in the future
960	 * @typedef {Object} OC.Plugin
961	 *
962	 * @property {String} name plugin name
963	 * @property {Function} attach function that will be called when the
964	 * plugin is attached
965	 * @property {Function} [detach] function that will be called when the
966	 * plugin is detached
967	 */
968
969};
970
971/**
972 * @namespace OC.search
973 */
974OC.search.customResults = {};
975/**
976 * @deprecated use get/setFormatter() instead
977 */
978OC.search.resultTypes = {};
979
980OC.addStyle.loaded = [];
981OC.addScript.loaded = [];
982
983/**
984 * A little class to manage a status field for a "saving" process.
985 * It can be used to display a starting message (e.g. "Saving...") and then
986 * replace it with a green success message or a red error message.
987 *
988 * @namespace OC.msg
989 */
990OC.msg = {
991	/**
992	 * Displayes a "Saving..." message in the given message placeholder
993	 *
994	 * @param {Object} selector    Placeholder to display the message in
995	 */
996	startSaving: function (selector) {
997		this.startAction(selector, t('core', 'Saving...'));
998	},
999
1000	/**
1001	 * Displayes a custom message in the given message placeholder
1002	 *
1003	 * @param {Object} selector    Placeholder to display the message in
1004	 * @param {string} message    Plain text message to display (no HTML allowed)
1005	 */
1006	startAction: function (selector, message) {
1007		$(selector).text(message)
1008			.removeClass('success')
1009			.removeClass('error')
1010			.stop(true, true)
1011			.show();
1012	},
1013
1014	/**
1015	 * Displayes an success/error message in the given selector
1016	 *
1017	 * @param {Object} selector    Placeholder to display the message in
1018	 * @param {Object} response    Response of the server
1019	 * @param {Object} response.data    Data of the servers response
1020	 * @param {string} response.data.message    Plain text message to display (no HTML allowed)
1021	 * @param {string} response.status    is being used to decide whether the message
1022	 * is displayed as an error/success
1023	 */
1024	finishedSaving: function (selector, response) {
1025		this.finishedAction(selector, response);
1026	},
1027
1028	/**
1029	 * Displayes an success/error message in the given selector
1030	 *
1031	 * @param {Object} selector    Placeholder to display the message in
1032	 * @param {Object} response    Response of the server
1033	 * @param {Object} response.data Data of the servers response
1034	 * @param {string} response.data.message Plain text message to display (no HTML allowed)
1035	 * @param {string} response.status is being used to decide whether the message
1036	 * is displayed as an error/success
1037	 */
1038	finishedAction: function (selector, response) {
1039		if (response.status === "success") {
1040			this.finishedSuccess(selector, response.data.message);
1041		} else {
1042			this.finishedError(selector, response.data.message);
1043		}
1044	},
1045
1046	/**
1047	 * Displayes an success message in the given selector
1048	 *
1049	 * @param {Object} selector Placeholder to display the message in
1050	 * @param {string} message Plain text success message to display (no HTML allowed)
1051	 */
1052	finishedSuccess: function (selector, message) {
1053		$(selector).text(message)
1054			.addClass('success')
1055			.removeClass('error')
1056			.stop(true, true)
1057			.delay(3000)
1058			.fadeOut(900)
1059			.show();
1060	},
1061
1062	/**
1063	 * Displayes an error message in the given selector
1064	 *
1065	 * @param {Object} selector Placeholder to display the message in
1066	 * @param {string} message Plain text error message to display (no HTML allowed)
1067	 */
1068	finishedError: function (selector, message) {
1069		$(selector).text(message)
1070			.addClass('error')
1071			.removeClass('success')
1072			.show();
1073	}
1074};
1075
1076/**
1077 * @todo Write documentation
1078 * @namespace
1079 */
1080OC.Notification = {
1081	queuedNotifications: [],
1082	getDefaultNotificationFunction: null,
1083
1084	/**
1085	 * @type Array.<int> array of notification timers
1086	 */
1087	notificationTimers: [],
1088
1089	/**
1090	 * @param callback
1091	 * @todo Write documentation
1092	 */
1093	setDefault: function (callback) {
1094		OC.Notification.getDefaultNotificationFunction = callback;
1095	},
1096
1097	/**
1098	 * Hides a notification.
1099	 *
1100	 * If a row is given, only hide that one.
1101	 * If no row is given, hide all notifications.
1102	 *
1103	 * @param {jQuery} [$row] notification row
1104	 * @param {Function} [callback] callback
1105	 */
1106	hide: function ($row, callback) {
1107		var self = this;
1108		var $notification = $('#notification');
1109
1110		if (_.isFunction($row)) {
1111			// first arg is the callback
1112			callback = $row;
1113			$row = undefined;
1114		}
1115
1116		if (!$row) {
1117			console.warn('Missing argument $row in OC.Notification.hide() call, caller needs to be adjusted to only dismiss its own notification');
1118			// assume that the row to be hidden is the first one
1119			$row = $notification.find('.row:first');
1120		}
1121
1122		if ($row && $notification.find('.row').length > 1) {
1123			// remove the row directly
1124			$row.remove();
1125			if (callback) {
1126				callback.call();
1127			}
1128			return;
1129		}
1130
1131		_.defer(function () {
1132			// fade out is supposed to only fade when there is a single row
1133			// however, some code might call hide() and show() directly after,
1134			// which results in more than one element
1135			// in this case, simply delete that one element that was supposed to
1136			// fade out
1137			//
1138			// FIXME: remove once all callers are adjusted to only hide their own notifications
1139			if ($notification.find('.row').length > 1) {
1140				$row.remove();
1141				return;
1142			}
1143
1144			// else, fade out whatever was present
1145			$notification.fadeOut('400', function () {
1146				if (self.isHidden()) {
1147					if (self.getDefaultNotificationFunction) {
1148						self.getDefaultNotificationFunction.call();
1149					}
1150				}
1151				if (callback) {
1152					callback.call();
1153				}
1154				$notification.empty();
1155			});
1156		});
1157	},
1158
1159	/**
1160	 * Shows a notification as HTML without being sanitized before.
1161	 * If you pass unsanitized user input this may lead to a XSS vulnerability.
1162	 * Consider using show() instead of showHTML()
1163	 *
1164	 * @param {string} html Message to display
1165	 * @param {Object} [options] options
1166	 * @param {string] [options.type] notification type
1167	 * @param {int} [options.timeout=0] timeout value, defaults to 0 (permanent)
1168	 * @return {jQuery} jQuery element for notification row
1169	 */
1170	showHtml: function (html, options) {
1171		options = options || {};
1172		_.defaults(options, {
1173			timeout: 0
1174		});
1175
1176		var self = this;
1177		var $notification = $('#notification');
1178		if (this.isHidden()) {
1179			$notification.fadeIn().css('display', 'inline-block');
1180		}
1181		var $row = $('<div class="row"></div>');
1182		if (options.type) {
1183			$row.addClass('type-' + options.type);
1184		}
1185		if (options.type === 'error') {
1186			// add a close button
1187			var $closeButton = $('<a class="action close icon-close" href="#"></a>');
1188			$closeButton.attr('alt', t('core', 'Dismiss'));
1189			$row.append($closeButton);
1190			$closeButton.one('click', function () {
1191				self.hide($row);
1192				return false;
1193			});
1194			$row.addClass('closeable');
1195		}
1196
1197		$row.prepend(html);
1198		$notification.append($row);
1199
1200		if (options.timeout > 0) {
1201			// register timeout to vanish notification
1202			this.notificationTimers.push(setTimeout(function () {
1203				self.hide($row);
1204			}, (options.timeout * 1000)));
1205		}
1206
1207		return $row;
1208	},
1209
1210	/**
1211	 * Shows a sanitized notification
1212	 *
1213	 * @param {string} text Message to display
1214	 * @param {Object} [options] options
1215	 * @param {string} [options.type] notification type
1216	 * @param {int} [options.timeout=0] timeout value, defaults to 0 (permanent)
1217	 * @return {jQuery} jQuery element for notification row
1218	 */
1219	show: function (text, options) {
1220		return this.showHtml($('<div/>').text(text).html(), options);
1221	},
1222
1223	/**
1224	 * Shows a notification that disappears after x seconds, default is
1225	 * 7 seconds
1226	 *
1227	 * @param {string} text Message to show
1228	 * @param {array} [options] options array
1229	 * @param {int} [options.timeout=7] timeout in seconds, if this is 0 it will show the message permanently
1230	 * @param {boolean} [options.isHTML=false] an indicator for HTML notifications (true) or text (false)
1231	 * @param {string] [options.type] notification type
1232	 */
1233	showTemporary: function (text, options) {
1234		var self = this;
1235		var defaults = {
1236			isHTML: false,
1237			timeout: 7
1238		};
1239		options = options || {};
1240		// merge defaults with passed in options
1241		_.defaults(options, defaults);
1242
1243		var $row;
1244		if (options.isHTML) {
1245			$row = this.showHtml(text, options);
1246		} else {
1247			$row = this.show(text, options);
1248		}
1249		return $row;
1250	},
1251
1252	/**
1253	 * Returns whether a notification is hidden.
1254	 * @return {boolean}
1255	 */
1256	isHidden: function () {
1257		return !$("#notification").find('.row').length;
1258	}
1259};
1260
1261/**
1262 * Breadcrumb class
1263 *
1264 * @namespace
1265 *
1266 * @deprecated will be replaced by the breadcrumb implementation
1267 * of the files app in the future
1268 */
1269OC.Breadcrumb = {
1270	container: null,
1271	/**
1272	 * @todo Write documentation
1273	 * @param dir
1274	 * @param leafName
1275	 * @param leafLink
1276	 */
1277	show: function (dir, leafName, leafLink) {
1278		if (!this.container) {//default
1279			this.container = $('#controls');
1280		}
1281		this._show(this.container, dir, leafName, leafLink);
1282	},
1283	_show: function (container, dir, leafname, leaflink) {
1284		var self = this;
1285
1286		this._clear(container);
1287
1288		// show home + path in subdirectories
1289		if (dir) {
1290			//add home
1291			var link = OC.linkTo('files', 'index.php');
1292
1293			var crumb = $('<div/>');
1294			crumb.addClass('crumb');
1295
1296			var crumbLink = $('<a/>');
1297			crumbLink.attr('href', link);
1298
1299			var crumbImg = $('<img/>');
1300			crumbImg.attr('src', OC.imagePath('core', 'places/home'));
1301			crumbLink.append(crumbImg);
1302			crumb.append(crumbLink);
1303			container.prepend(crumb);
1304
1305			//add path parts
1306			var segments = dir.split('/');
1307			var pathurl = '';
1308			jQuery.each(segments, function (i, name) {
1309				if (name !== '') {
1310					pathurl = pathurl + '/' + name;
1311					var link = OC.linkTo('files', 'index.php') + '?dir=' + encodeURIComponent(pathurl);
1312					self._push(container, name, link);
1313				}
1314			});
1315		}
1316
1317		//add leafname
1318		if (leafname && leaflink) {
1319			this._push(container, leafname, leaflink);
1320		}
1321	},
1322
1323	/**
1324	 * @todo Write documentation
1325	 * @param {string} name
1326	 * @param {string} link
1327	 */
1328	push: function (name, link) {
1329		if (!this.container) {//default
1330			this.container = $('#controls');
1331		}
1332		return this._push(OC.Breadcrumb.container, name, link);
1333	},
1334	_push: function (container, name, link) {
1335		var crumb = $('<div/>');
1336		crumb.addClass('crumb').addClass('last');
1337
1338		var crumbLink = $('<a/>');
1339		crumbLink.attr('href', link);
1340		crumbLink.text(name);
1341		crumb.append(crumbLink);
1342
1343		var existing = container.find('div.crumb');
1344		if (existing.length) {
1345			existing.removeClass('last');
1346			existing.last().after(crumb);
1347		} else {
1348			container.prepend(crumb);
1349		}
1350		return crumb;
1351	},
1352
1353	/**
1354	 * @todo Write documentation
1355	 */
1356	pop: function () {
1357		if (!this.container) {//default
1358			this.container = $('#controls');
1359		}
1360		this.container.find('div.crumb').last().remove();
1361		this.container.find('div.crumb').last().addClass('last');
1362	},
1363
1364	/**
1365	 * @todo Write documentation
1366	 */
1367	clear: function () {
1368		if (!this.container) {//default
1369			this.container = $('#controls');
1370		}
1371		this._clear(this.container);
1372	},
1373	_clear: function (container) {
1374		container.find('div.crumb').remove();
1375	}
1376};
1377
1378if (typeof localStorage !== 'undefined' && localStorage !== null) {
1379	/**
1380	 * User and instance aware localstorage
1381	 * @namespace
1382	 */
1383	OC.localStorage = {
1384		namespace: 'oc_' + OC.currentUser + '_' + OC.webroot + '_',
1385
1386		/**
1387		 * Whether the storage contains items
1388		 * @param {string} name
1389		 * @return {boolean}
1390		 */
1391		hasItem: function (name) {
1392			return OC.localStorage.getItem(name) !== null;
1393		},
1394
1395		/**
1396		 * Add an item to the storage
1397		 * @param {string} name
1398		 * @param {string} item
1399		 */
1400		setItem: function (name, item) {
1401			return localStorage.setItem(OC.localStorage.namespace + name, JSON.stringify(item));
1402		},
1403
1404		/**
1405		 * Removes an item from the storage
1406		 * @param {string} name
1407		 * @param {string} item
1408		 */
1409		removeItem: function (name, item) {
1410			return localStorage.removeItem(OC.localStorage.namespace + name);
1411		},
1412
1413		/**
1414		 * Get an item from the storage
1415		 * @param {string} name
1416		 * @return {null|string}
1417		 */
1418		getItem: function (name) {
1419			var item = localStorage.getItem(OC.localStorage.namespace + name);
1420			if (item === null) {
1421				return null;
1422			} else {
1423				return JSON.parse(item);
1424			}
1425		}
1426	};
1427} else {
1428	//dummy localstorage
1429	OC.localStorage = {
1430		hasItem: function () {
1431			return false;
1432		},
1433		setItem: function () {
1434			return false;
1435		},
1436		getItem: function () {
1437			return null;
1438		}
1439	};
1440}
1441
1442/**
1443 * prototypical inheritance functions
1444 * @todo Write documentation
1445 * usage:
1446 * MySubObject=object(MyObject)
1447 */
1448function object(o) {
1449	function F() {
1450	}
1451
1452	F.prototype = o;
1453	return new F();
1454}
1455
1456/**
1457 * Initializes core
1458 */
1459function initCore() {
1460	/**
1461	 * Disable automatic evaluation of responses for $.ajax() functions (and its
1462	 * higher-level alternatives like $.get() and $.post()).
1463	 *
1464	 * If a response to a $.ajax() request returns a content type of "application/javascript"
1465	 * JQuery would previously execute the response body. This is a pretty unexpected
1466	 * behaviour and can result in a bypass of our Content-Security-Policy as well as
1467	 * multiple unexpected XSS vectors.
1468	 */
1469	$.ajaxSetup({
1470		contents: {
1471			script: false
1472		}
1473	});
1474
1475	$('#body-login form label').css({
1476		color: $('p.info').css('color')
1477	});
1478
1479	if ($('#body-login .v-align').length > 0 ) {
1480		$('#body-login .v-align').fadeIn(600);
1481		setTimeout(function () {
1482			$('#body-login footer *').addClass('show');
1483		}, 250);
1484	}
1485
1486	/**
1487	 * Set users locale to moment.js as soon as possible
1488	 */
1489	moment.locale(OC.getLocale());
1490
1491	/*
1492	 * Override browser locales with OC locale in ajax requests
1493	 */
1494	$.ajaxSetup({
1495		beforeSend: function(xhr) {
1496			xhr.setRequestHeader('Accept-Language', OC.getLocale());
1497		}
1498	});
1499
1500	var userAgent = window.navigator.userAgent;
1501	var msie = userAgent.indexOf('MSIE ');
1502	var trident = userAgent.indexOf('Trident/');
1503	var edge = userAgent.indexOf('Edge/');
1504
1505	if (msie > 0 || trident > 0) {
1506		// (IE 10 or older) || IE 11
1507		$('html').addClass('ie');
1508	} else if (edge > 0) {
1509		// for edge
1510		$('html').addClass('edge');
1511	}
1512
1513	$(window).on('unload.main', function () {
1514		OC._unloadCalled = true;
1515	});
1516	$(window).on('beforeunload.main', function () {
1517		// super-trick thanks to http://stackoverflow.com/a/4651049
1518		// in case another handler displays a confirmation dialog (ex: navigating away
1519		// during an upload), there are two possible outcomes: user clicked "ok" or
1520		// "cancel"
1521
1522		// first timeout handler is called after unload dialog is closed
1523		setTimeout(function () {
1524			OC._userIsNavigatingAway = true;
1525
1526			// second timeout event is only called if user cancelled (Chrome),
1527			// but in other browsers it might still be triggered, so need to
1528			// set a higher delay...
1529			setTimeout(function () {
1530				if (!OC._unloadCalled) {
1531					OC._userIsNavigatingAway = false;
1532				}
1533			}, 10000);
1534		}, 1);
1535	});
1536	$(document).on('ajaxError.main', function (event, request, settings) {
1537		if (settings && settings.allowAuthErrors) {
1538			return;
1539		}
1540		OC._processAjaxError(request);
1541	});
1542
1543	/**
1544	 * Calls the server periodically to ensure that session doesn't
1545	 * time out
1546	 */
1547	function initSessionHeartBeat() {
1548		// max interval in seconds set to 24 hours
1549		var maxInterval = 24 * 3600;
1550		// interval in seconds
1551		var interval = 900;
1552		if (oc_config.session_lifetime) {
1553			interval = Math.floor(oc_config.session_lifetime / 2);
1554		}
1555		// minimum one minute
1556		if (interval < 60) {
1557			interval = 60;
1558		}
1559		if (interval > maxInterval) {
1560			interval = maxInterval;
1561		}
1562		var url = OC.generateUrl('/heartbeat');
1563		var heartBeatTimeout = null;
1564		var heartBeat = function () {
1565			clearInterval(heartBeatTimeout);
1566			heartBeatTimeout = setInterval(function () {
1567				$.post(url);
1568			}, interval * 1000);
1569		};
1570		heartBeat();
1571	}
1572
1573	/**
1574	 * Check mouse movement to send a heartbeat if the mouse moved
1575	 */
1576	function initMouseTrack() {
1577		var interval = 120;  // 2 minutes
1578		if (oc_config.session_lifetime) {
1579			interval = Math.floor(oc_config.session_lifetime / 2);
1580		}
1581		interval = Math.min(Math.max(interval, 60), 3600);  // ensure interval is in [60, 3600]
1582
1583		var extendableHeartbeat = null;
1584		var extendableMouseMoved = false;
1585		var mouseMoved = false;
1586		var heartbeatTimestamp = 0;
1587
1588		// If the mouse has moved within this interval, set a timeout to send
1589		// a heartbeat request. If the mouse moves again in the next interval,
1590		// the timeout will be extended. Note that the heartbeat request might
1591		// be delayed indefinitely.
1592		setInterval(function() {
1593			if (extendableMouseMoved) {
1594				clearTimeout(extendableHeartbeat);
1595				extendableHeartbeat = setTimeout(function() {
1596					if (!extendableMouseMoved) {
1597						// if the mouse has moved, either this timeout has been cleared and
1598						// a new one is being created, or a new timeout will be created soon
1599						// (if this timeout is executed before the setInterval function)
1600						var currentTimestamp = Date.now();
1601						if (currentTimestamp > heartbeatTimestamp + (30 * 1000)) {
1602							// if a heartbeat has been sent recently, don't send a new one
1603							console.log('I1 sending heartbeat');
1604							$.post(OC.generateUrl('/heartbeat'));
1605							heartbeatTimestamp = currentTimestamp;
1606						} else {
1607							console.log('I1 skipping heartbeat');
1608						}
1609					} else {
1610						console.log('I1 delaying hearbeat because mouse moved.');
1611					}
1612				}, 30 * 1000);
1613			}
1614			extendableMouseMoved = false;
1615		}, 30 * 1000);
1616
1617		// If the mouse has been moved within this interval, send a heartbeat
1618		// immediately. This will prevent the session to expire.
1619		setInterval(function() {
1620			if (mouseMoved) {
1621				var currentTimestamp = Date.now();
1622				if (currentTimestamp > heartbeatTimestamp + (interval * 1000)) {
1623					// if a heartbeat has been sent recently, don't send a new one
1624					console.log('I2 sending heartbeat');
1625					$.post(OC.generateUrl('/heartbeat'));
1626					heartbeatTimestamp = currentTimestamp;
1627				} else {
1628					console.log('I2 skipping heartbeat');
1629				}
1630			}
1631			mouseMoved = false;  // no need to wait for the response
1632		}, interval * 1000);
1633
1634		// set a couple of variables to indicate that the mouse has moved
1635		$(window).on('mousemove.sessiontrack', function() {
1636			mouseMoved = true;
1637			extendableMouseMoved = true;
1638		});
1639	}
1640
1641	// session heartbeat (defaults to enabled)
1642	if (typeof(oc_config.session_keepalive) === 'undefined' || !!oc_config.session_keepalive) {
1643
1644		initSessionHeartBeat();
1645	} else {
1646		initMouseTrack();
1647	}
1648
1649	if (oc_config.session_forced_logout_timeout !== undefined && oc_config.session_forced_logout_timeout > 0) {
1650		$(window).on('beforeunload.sessiontrack', function() {
1651			var requestData = {
1652				method: 'POST',
1653				headers: {
1654					'Content-Type': 'application/json',
1655					'X-Requested-With': 'XMLHttpRequest'
1656				},
1657				body: JSON.stringify({t: oc_config.session_forced_logout_timeout}),
1658				credentials: 'same-origin',
1659				keepalive: true
1660			};
1661			var r = undefined;
1662			if (typeof Request !== "undefined") {
1663				// IE 11 doesn't have support "Request", so we'll fallback to ajax
1664				r = new Request(OC.generateUrl('/heartbeat'), requestData);
1665			}
1666			if (r === undefined || r.keepalive === undefined) {
1667				// firefox doesn't support keepalive (checked 11th Apr 2022)
1668				// try a sync ajax call instead
1669				$.ajax({
1670					method: 'POST',
1671					url: OC.generateUrl('/heartbeat'),
1672					data: {t: oc_config.session_forced_logout_timeout},
1673					async: false
1674				});
1675			} else {
1676				fetch(r);
1677			}
1678		});
1679	}
1680
1681	OC.registerMenu($('#expand'), $('#expanddiv'));
1682
1683	/**
1684	 * This event gets fired, if your focused element is for example the navbar
1685	 * and you click on an iframe.
1686	 * So we can close the open menus, for example the #settings menu
1687	 */
1688	$(window).on('blur.closemenus',function(){
1689		OC.hideMenus();
1690	});
1691
1692	// toggle for menus
1693	$(document).on('mouseup.closemenus', function (event) {
1694		var $el = $(event.target);
1695		if ($el.closest('.menu').length || $el.closest('.menutoggle').length) {
1696			// don't close when clicking on the menu directly or a menu toggle
1697			return false;
1698		}
1699
1700		OC.hideMenus();
1701	});
1702
1703	/**
1704	 * Set up the main menu toggle to react to media query changes.
1705	 * If the screen is small enough, the main menu becomes a toggle.
1706	 * If the screen is bigger, the main menu is not a toggle any more.
1707	 */
1708	function setupMainMenu() {
1709		// toggle the navigation
1710		var $toggle = $('#header .header-appname-container');
1711		var $navigation = $('#navigation');
1712
1713		// init the menu
1714		OC.registerMenu($toggle, $navigation);
1715		$toggle.data('oldhref', $toggle.attr('href'));
1716		$toggle.attr('href', '#');
1717		$navigation.hide();
1718
1719		// show loading feedback
1720		$navigation.delegate('a', 'click', function (event) {
1721			var $app = $(event.target);
1722			if (!$app.is('a')) {
1723				$app = $app.closest('a');
1724			}
1725			if (!event.ctrlKey) {
1726				$app.addClass('app-loading');
1727			} else {
1728				// Close navigation when opening app in
1729				// a new tab
1730				OC.hideMenus();
1731			}
1732		});
1733	}
1734
1735	function setupUserMenu() {
1736		var $menu = $('#header #settings');
1737
1738		$menu.delegate('a', 'click', function (event) {
1739			var $page = $(event.target);
1740			if (!$page.is('a')) {
1741				$page = $page.closest('a');
1742			}
1743			$page.find('img').remove();
1744			$page.find('div').remove(); // prevent odd double-clicks
1745			$page.prepend($('<div/>').addClass('icon-loading-small-dark'));
1746		});
1747	}
1748
1749	setupMainMenu();
1750	setupUserMenu();
1751
1752	// just add snapper for logged in users
1753	if ($('#app-navigation').length && !$('html').hasClass('lte9')) {
1754
1755		// App sidebar on mobile
1756		var snapper = new Snap({
1757			element: document.getElementById('app-content'),
1758			disable: 'right',
1759			maxPosition: 250,
1760			minDragDistance: 100
1761		});
1762		$('#app-content').prepend('<div id="app-navigation-toggle" class="icon-menu" style="display:none;"></div>');
1763		$('#app-navigation-toggle').click(function () {
1764			if (snapper.state().state == 'left') {
1765				snapper.close();
1766			} else {
1767				snapper.open('left');
1768			}
1769		});
1770		// close sidebar when switching navigation entry
1771		var $appNavigation = $('#app-navigation');
1772		$appNavigation.delegate('a, :button', 'click', function (event) {
1773			var $target = $(event.target);
1774			// don't hide navigation when changing settings or adding things
1775			if ($target.is('.app-navigation-noclose') ||
1776				$target.closest('.app-navigation-noclose').length) {
1777				return;
1778			}
1779			if ($target.is('.add-new') ||
1780				$target.closest('.add-new').length) {
1781				return;
1782			}
1783			if ($target.is('#app-settings') ||
1784				$target.closest('#app-settings').length) {
1785				return;
1786			}
1787			snapper.close();
1788		});
1789
1790		var toggleSnapperOnSize = function () {
1791			if ($(window).width() > 768) {
1792				snapper.close();
1793				snapper.disable();
1794			} else {
1795				snapper.enable();
1796			}
1797		};
1798
1799		$(window).resize(_.debounce(toggleSnapperOnSize, 250));
1800
1801		// initial call
1802		toggleSnapperOnSize();
1803
1804		// adjust controls bar width
1805		var adjustControlsWidth = function () {
1806			if ($('#controls').length) {
1807				var controlsWidth;
1808				// if there is a scrollbar …
1809				if ($('#app-content').get(0).scrollHeight > $('#app-content').height()) {
1810					if ($(window).width() > 768) {
1811						controlsWidth = $('#content').width() - $('#app-navigation').width() - getScrollBarWidth();
1812						if (!$('#app-sidebar').hasClass('hidden') && !$('#app-sidebar').hasClass('disappear')) {
1813							controlsWidth -= $('#app-sidebar').width();
1814						}
1815					} else {
1816						controlsWidth = $('#content').width() - getScrollBarWidth();
1817					}
1818				} else { // if there is none
1819					if ($(window).width() > 768) {
1820						controlsWidth = $('#content').width() - $('#app-navigation').width();
1821						if (!$('#app-sidebar').hasClass('hidden') && !$('#app-sidebar').hasClass('disappear')) {
1822							controlsWidth -= $('#app-sidebar').width();
1823						}
1824					} else {
1825						controlsWidth = $('#content').width();
1826					}
1827				}
1828				$('#controls').css('width', controlsWidth);
1829				$('#controls').css('min-width', controlsWidth);
1830			}
1831		};
1832
1833		$(window).resize(_.debounce(adjustControlsWidth, 250));
1834
1835		$('body').delegate('#app-content', 'apprendered appresized', adjustControlsWidth);
1836
1837	}
1838}
1839
1840/**
1841 * Disable the use of globalEval in jQuery 2.1.4.
1842 * This is required for API compatibility, yet should not be available all the
1843 * same.
1844 *
1845 * @see https://github.com/jquery/jquery/issues/2432 for further details.
1846 */
1847$.fn.globalEval = function(){};
1848
1849/**
1850 * Make htmlPrefilter an identity function in jQuery 2.1.4.
1851 *
1852 * @see https://github.com/advisories/GHSA-gxr4-xjj5-5px2
1853 */
1854$.fn.htmlPrefilter = function(html) {
1855	return html;
1856};
1857
1858$(document).ready(initCore);
1859
1860/**
1861 * Filter Jquery selector by attribute value
1862 */
1863$.fn.filterAttr = function (attr_name, attr_value) {
1864	return this.filter(function () {
1865		return $(this).attr(attr_name) === attr_value;
1866	});
1867};
1868
1869/**
1870 * Returns a human readable file size
1871 * @param {number} size Size in bytes
1872 * @param {boolean} skipSmallSizes return '< 1 kB' for small files
1873 * @return {string}
1874 */
1875function humanFileSize(size, skipSmallSizes) {
1876	var humanList = ['B', 'KB', 'MB', 'GB', 'TB'];
1877	// Calculate Log with base 1024: size = 1024 ** order
1878	var order = size > 0 ? Math.floor(Math.log(size) / Math.log(1024)) : 0;
1879	// Stay in range of the byte sizes that are defined
1880	order = Math.min(humanList.length - 1, order);
1881	var readableFormat = humanList[order];
1882	var relativeSize = (size / Math.pow(1024, order)).toFixed(1);
1883	if (skipSmallSizes === true && order === 0) {
1884		if (relativeSize !== "0.0") {
1885			return '< 1 KB';
1886		} else {
1887			return '0 KB';
1888		}
1889	}
1890	if (order < 2) {
1891		relativeSize = parseFloat(relativeSize).toFixed(0);
1892	}
1893	else if (relativeSize.substr(relativeSize.length - 2, 2) === '.0') {
1894		relativeSize = relativeSize.substr(0, relativeSize.length - 2);
1895	}
1896	return relativeSize + ' ' + readableFormat;
1897}
1898
1899/**
1900 * Format an UNIX timestamp to a human understandable format
1901 * @param {number} timestamp UNIX timestamp
1902 * @return {string} Human readable format
1903 */
1904function formatDate(timestamp) {
1905	return OC.Util.formatDate(timestamp);
1906}
1907
1908//
1909/**
1910 * Get the value of a URL parameter
1911 * @link http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery
1912 * @param {string} name URL parameter
1913 * @return {string}
1914 */
1915function getURLParameter(name) {
1916	return decodeURI(
1917		(RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) || [, null])[1]
1918	);
1919}
1920
1921/**
1922 * Takes an absolute timestamp and return a string with a human-friendly relative date
1923 * @param {number} timestamp A Unix timestamp
1924 */
1925function relative_modified_date(timestamp) {
1926	/*
1927	 Were multiplying by 1000 to bring the timestamp back to its original value
1928	 per https://github.com/owncloud/core/pull/10647#discussion_r16790315
1929	 */
1930	return OC.Util.relativeModifiedDate(timestamp * 1000);
1931}
1932
1933/**
1934 * Utility functions
1935 * @namespace
1936 */
1937OC.Util = {
1938	// TODO: remove original functions from global namespace
1939	humanFileSize: humanFileSize,
1940
1941	/**
1942	* regular expression to parse size in bytes from a humanly readable string
1943	* see computerFileSize(string)
1944	*/
1945	_computerFileSizeRegexp: /^[\s+]?([0-9]*)(\.([0-9]+))?( +)?([kmgtp]?b?)$/i,
1946
1947	/**
1948	 * Returns a file size in bytes from a humanly readable string
1949	 * Makes 2kB to 2048.
1950	 * Inspired by computerFileSize in helper.php
1951	 * @param  {string} string file size in human readable format
1952	 * @return {number} or null if string could not be parsed
1953	 *
1954	 *
1955	 */
1956	computerFileSize: function (string) {
1957		if (typeof string !== 'string') {
1958			return null;
1959		}
1960
1961		var s = string.toLowerCase().trim();
1962		var bytes = null;
1963
1964		var bytesArray = {
1965			'b': 1,
1966			'k': 1024,
1967			'kb': 1024,
1968			'mb': 1024 * 1024,
1969			'm': 1024 * 1024,
1970			'gb': 1024 * 1024 * 1024,
1971			'g': 1024 * 1024 * 1024,
1972			'tb': 1024 * 1024 * 1024 * 1024,
1973			't': 1024 * 1024 * 1024 * 1024,
1974			'pb': 1024 * 1024 * 1024 * 1024 * 1024,
1975			'p': 1024 * 1024 * 1024 * 1024 * 1024
1976		};
1977
1978		var matches = s.match(this._computerFileSizeRegexp);
1979		if (matches !== null) {
1980			bytes = parseFloat(s);
1981			if (!isFinite(bytes)) {
1982				return null;
1983			}
1984		} else {
1985			return null;
1986		}
1987		if (matches[5]) {
1988			bytes = bytes * bytesArray[matches[5]];
1989		}
1990
1991		bytes = Math.round(bytes);
1992		return bytes;
1993	},
1994
1995	/**
1996	 * @param timestamp
1997	 * @param format
1998	 * @returns {string} timestamp formatted as requested
1999	 */
2000	formatDate: function (timestamp, format) {
2001		format = format || "LLL";
2002		return moment(timestamp).format(format);
2003	},
2004
2005	/**
2006	 * @param timestamp
2007	 * @returns {string} human readable difference from now
2008	 */
2009	relativeModifiedDate: function (timestamp) {
2010		var diff = moment().diff(moment(timestamp));
2011		if (diff >= 0 && diff < 45000) {
2012			return t('core', 'seconds ago');
2013		}
2014		return moment(timestamp).fromNow();
2015	},
2016	/**
2017	 * Returns whether the browser supports SVG
2018	 * @deprecated SVG is always supported (since 9.0)
2019	 * @return {boolean} true if the browser supports SVG, false otherwise
2020	 */
2021	hasSVGSupport: function () {
2022		return true
2023	},
2024	/**
2025	 * If SVG is not supported, replaces the given icon's extension
2026	 * from ".svg" to ".png".
2027	 * If SVG is supported, return the image path as is.
2028	 * @param {string} file image path with svg extension
2029	 * @deprecated SVG is always supported (since 9.0)
2030	 * @return {string} fixed image path with png extension if SVG is not supported
2031	 */
2032	replaceSVGIcon: function (file) {
2033		return file;
2034	},
2035	/**
2036	 * Replace SVG images in all elements that have the "svg" class set
2037	 * with PNG images.
2038	 *
2039	 * @param $el root element from which to search, defaults to $('body')
2040	 * @deprecated SVG is always supported (since 9.0)
2041	 */
2042	replaceSVG: function ($el) {
2043	},
2044
2045	/**
2046	 * Fix image scaling for IE8, since background-size is not supported.
2047	 *
2048	 * This scales the image to the element's actual size, the URL is
2049	 * taken from the "background-image" CSS attribute.
2050	 *
2051	 * @deprecated IE8 isn't supported since 9.0
2052	 * @param {Object} $el image element
2053	 */
2054	scaleFixForIE8: function ($el) {
2055	},
2056
2057	/**
2058	 * Returns whether this is IE
2059	 *
2060	 * @deprecated Use bowser.mise instead (since 9.1)
2061	 * @return {bool} true if this is IE, false otherwise
2062	 */
2063	isIE: function () {
2064		return bowser.msie;
2065	},
2066
2067	/**
2068	 * Returns whether this is IE8
2069	 *
2070	 * @deprecated IE8 isn't supported since 9.0
2071	 * @return {bool} false (IE8 isn't supported anymore)
2072	 */
2073	isIE8: function () {
2074		return false;
2075	},
2076
2077	/**
2078	 * Returns the width of a generic browser scrollbar
2079	 *
2080	 * @return {int} width of scrollbar
2081	 */
2082	getScrollBarWidth: function () {
2083		if (this._scrollBarWidth) {
2084			return this._scrollBarWidth;
2085		}
2086
2087		var inner = document.createElement('p');
2088		inner.style.width = "100%";
2089		inner.style.height = "200px";
2090
2091		var outer = document.createElement('div');
2092		outer.style.position = "absolute";
2093		outer.style.top = "0px";
2094		outer.style.left = "0px";
2095		outer.style.visibility = "hidden";
2096		outer.style.width = "200px";
2097		outer.style.height = "150px";
2098		outer.style.overflow = "hidden";
2099		outer.appendChild(inner);
2100
2101		document.body.appendChild(outer);
2102		var w1 = inner.offsetWidth;
2103		outer.style.overflow = 'scroll';
2104		var w2 = inner.offsetWidth;
2105		if (w1 === w2) {
2106			w2 = outer.clientWidth;
2107		}
2108
2109		document.body.removeChild(outer);
2110
2111		this._scrollBarWidth = (w1 - w2);
2112
2113		return this._scrollBarWidth;
2114	},
2115
2116	/**
2117	 * Remove the time component from a given date
2118	 *
2119	 * @param {Date} date date
2120	 * @return {Date} date with stripped time
2121	 */
2122	stripTime: function (date) {
2123		// FIXME: likely to break when crossing DST
2124		// would be better to use a library like momentJS
2125		return new Date(date.getFullYear(), date.getMonth(), date.getDate());
2126	},
2127
2128	_chunkify: function (t) {
2129		// Adapted from http://my.opera.com/GreyWyvern/blog/show.dml/1671288
2130		var tz = [], x = 0, y = -1, n = 0, code, c;
2131
2132		while (x < t.length) {
2133			c = t.charAt(x);
2134			// only include the dot in strings
2135			var m = ((!n && c === '.') || (c >= '0' && c <= '9'));
2136			if (m !== n) {
2137				// next chunk
2138				y++;
2139				tz[y] = '';
2140				n = m;
2141			}
2142			tz[y] += c;
2143			x++;
2144		}
2145		return tz;
2146	},
2147	/**
2148	 * Compare two strings to provide a natural sort
2149	 * @param a first string to compare
2150	 * @param b second string to compare
2151	 * @return -1 if b comes before a, 1 if a comes before b
2152	 * or 0 if the strings are identical
2153	 */
2154	naturalSortCompare: function (a, b) {
2155		var x;
2156		var aa = OC.Util._chunkify(a);
2157		var bb = OC.Util._chunkify(b);
2158
2159		for (x = 0; aa[x] && bb[x]; x++) {
2160			if (aa[x] !== bb[x]) {
2161				var aNum = Number(aa[x]), bNum = Number(bb[x]);
2162				// note: == is correct here
2163				if (aNum == aa[x] && bNum == bb[x]) {
2164					return aNum - bNum;
2165				} else {
2166					// Forcing 'en' locale to match the server-side locale which is
2167					// always 'en'.
2168					//
2169					// Note: This setting isn't supported by all browsers but for the ones
2170					// that do there will be more consistency between client-server sorting
2171					return aa[x].localeCompare(bb[x], 'en');
2172				}
2173			}
2174		}
2175		return aa.length - bb.length;
2176	},
2177	/**
2178	 * Calls the callback in a given interval until it returns true
2179	 * @param {function} callback
2180	 * @param {integer} interval in milliseconds
2181	 */
2182	waitFor: function (callback, interval) {
2183		var internalCallback = function () {
2184			if (callback() !== true) {
2185				setTimeout(internalCallback, interval);
2186			}
2187		};
2188
2189		internalCallback();
2190	},
2191	/**
2192	 * Checks if a cookie with the given name is present and is set to the provided value.
2193	 * @param {string} name name of the cookie
2194	 * @param {string} value value of the cookie
2195	 * @return {boolean} true if the cookie with the given name has the given value
2196	 */
2197	isCookieSetToValue: function (name, value) {
2198		var cookies = document.cookie.split(';');
2199		for (var i = 0; i < cookies.length; i++) {
2200			var cookie = cookies[i].split('=');
2201			if (cookie[0].trim() === name && cookie[1].trim() === value) {
2202				return true;
2203			}
2204		}
2205		return false;
2206	},
2207
2208	/**
2209	 * Checks if an element is completely visible and scrolls the screen if not
2210	 * @param {jQuery} jQuery element that has to be displayed
2211	 * @param {jQuery} scroll container if null scrollContainer will be set to $('#app-content')
2212	 */
2213	scrollIntoView: function (toViewEl, scrollContainer) {
2214
2215		var toViewElTopLocation = toViewEl.offset().top;
2216		var toViewElHeight = toViewEl.outerHeight();
2217		var toViewElBottomLocation = toViewElTopLocation + toViewElHeight + 50;
2218		var windowHeight = $(window).height();
2219
2220		if (scrollContainer === null) {
2221			scrollContainer = $('#app-content');
2222		}
2223
2224		if (toViewElBottomLocation > windowHeight) {
2225			var currentPosition = scrollContainer[0].scrollTop;
2226			var scrollDistance = toViewElBottomLocation - windowHeight;
2227			scrollContainer.stop();
2228			scrollContainer.animate({
2229				scrollTop: currentPosition + scrollDistance
2230			}, 700);
2231		}
2232	}
2233};
2234
2235/**
2236 * Utility class for the history API,
2237 * includes fallback to using the URL hash when
2238 * the browser doesn't support the history API.
2239 *
2240 * @namespace
2241 */
2242OC.Util.History = {
2243	_handlers: [],
2244
2245	/**
2246	 * Push the current URL parameters to the history stack
2247	 * and change the visible URL.
2248	 * Note: this includes a workaround for IE8/IE9 that uses
2249	 * the hash part instead of the search part.
2250	 *
2251	 * @param params to append to the URL, can be either a string
2252	 * or a map
2253	 * @param {boolean} [replace=false] whether to replace instead of pushing
2254	 */
2255	_pushState: function (params, replace) {
2256		var strParams;
2257		if (typeof(params) === 'string') {
2258			strParams = params;
2259		}
2260		else {
2261			strParams = OC.buildQueryString(params);
2262		}
2263		if (window.history.pushState) {
2264			var url = location.pathname + '?' + strParams;
2265			if (replace) {
2266				window.history.replaceState(params, '', url);
2267			} else {
2268				window.history.pushState(params, '', url);
2269			}
2270		}
2271		// use URL hash for IE8
2272		else {
2273			window.location.hash = '?' + strParams;
2274			// inhibit next onhashchange that just added itself
2275			// to the event queue
2276			this._cancelPop = true;
2277		}
2278	},
2279
2280	/**
2281	 * Push the current URL parameters to the history stack
2282	 * and change the visible URL.
2283	 * Note: this includes a workaround for IE8/IE9 that uses
2284	 * the hash part instead of the search part.
2285	 *
2286	 * @param params to append to the URL, can be either a string
2287	 * or a map
2288	 */
2289	pushState: function (params) {
2290		return this._pushState(params, false);
2291	},
2292
2293	/**
2294	 * Push the current URL parameters to the history stack
2295	 * and change the visible URL.
2296	 * Note: this includes a workaround for IE8/IE9 that uses
2297	 * the hash part instead of the search part.
2298	 *
2299	 * @param params to append to the URL, can be either a string
2300	 * or a map
2301	 */
2302	replaceState: function (params) {
2303		return this._pushState(params, true);
2304	},
2305
2306	/**
2307	 * Add a popstate handler
2308	 *
2309	 * @param handler function
2310	 */
2311	addOnPopStateHandler: function (handler) {
2312		this._handlers.push(handler);
2313	},
2314
2315	/**
2316	 * Parse a query string from the hash part of the URL.
2317	 * (workaround for IE8 / IE9)
2318	 */
2319	_parseHashQuery: function () {
2320		var hash = window.location.hash,
2321			pos = hash.indexOf('?');
2322		if (pos >= 0) {
2323			return hash.substr(pos + 1);
2324		}
2325		if (hash.length) {
2326			// remove hash sign
2327			return hash.substr(1);
2328		}
2329		return '';
2330	},
2331
2332	_decodeQuery: function (query) {
2333		return query.replace(/\+/g, ' ');
2334	},
2335
2336	/**
2337	 * Parse the query/search part of the URL.
2338	 * Also try and parse it from the URL hash (for IE8)
2339	 *
2340	 * @return map of parameters
2341	 */
2342	parseUrlQuery: function () {
2343		var query = this._parseHashQuery(),
2344			params;
2345		// try and parse from URL hash first
2346		if (query) {
2347			params = OC.parseQueryString(this._decodeQuery(query));
2348		}
2349		// else read from query attributes
2350		params = _.extend(params || {}, OC.parseQueryString(this._decodeQuery(location.search)));
2351		return params || {};
2352	},
2353
2354	_onPopState: function (e) {
2355		if (this._cancelPop) {
2356			this._cancelPop = false;
2357			return;
2358		}
2359		var params;
2360		if (!this._handlers.length) {
2361			return;
2362		}
2363		params = (e && e.state);
2364		if (_.isString(params)) {
2365			params = OC.parseQueryString(params);
2366		} else if (!params) {
2367			params = this.parseUrlQuery() || {};
2368		}
2369		for (var i = 0; i < this._handlers.length; i++) {
2370			this._handlers[i](params);
2371		}
2372	}
2373};
2374
2375// fallback to hashchange when no history support
2376if (window.history.pushState) {
2377	window.onpopstate = _.bind(OC.Util.History._onPopState, OC.Util.History);
2378}
2379else {
2380	$(window).on('hashchange', _.bind(OC.Util.History._onPopState, OC.Util.History));
2381}
2382
2383/**
2384 * Get a variable by name
2385 * @param {string} name
2386 * @return {*}
2387 */
2388OC.get = function (name) {
2389	var namespaces = name.split(".");
2390	var tail = namespaces.pop();
2391	var context = window;
2392
2393	for (var i = 0; i < namespaces.length; i++) {
2394		context = context[namespaces[i]];
2395		if (!context) {
2396			return false;
2397		}
2398	}
2399	return context[tail];
2400};
2401
2402/**
2403 * Set a variable by name
2404 * @param {string} name
2405 * @param {*} value
2406 */
2407OC.set = function (name, value) {
2408	var namespaces = name.split(".");
2409	var tail = namespaces.pop();
2410	var context = window;
2411
2412	for (var i = 0; i < namespaces.length; i++) {
2413		if (!context[namespaces[i]]) {
2414			context[namespaces[i]] = {};
2415		}
2416		context = context[namespaces[i]];
2417	}
2418	context[tail] = value;
2419};
2420
2421// fix device width on windows phone
2422(function () {
2423	if ("-ms-user-select" in document.documentElement.style && navigator.userAgent.match(/IEMobile\/10\.0/)) {
2424		var msViewportStyle = document.createElement("style");
2425		msViewportStyle.appendChild(
2426			document.createTextNode("@-ms-viewport{width:auto!important}")
2427		);
2428		document.getElementsByTagName("head")[0].appendChild(msViewportStyle);
2429	}
2430})();
2431
2432/**
2433 * Namespace for apps
2434 * @namespace OCA
2435 */
2436window.OCA = {};
2437
2438/**
2439 * select a range in an input field
2440 * @link http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area
2441 * @param {type} start
2442 * @param {type} end
2443 */
2444jQuery.fn.selectRange = function (start, end) {
2445	return this.each(function () {
2446		if (this.setSelectionRange) {
2447			this.focus();
2448			this.setSelectionRange(start, end);
2449		} else if (this.createTextRange) {
2450			var range = this.createTextRange();
2451			range.collapse(true);
2452			range.moveEnd('character', end);
2453			range.moveStart('character', start);
2454			range.select();
2455		}
2456	});
2457};
2458
2459/**
2460 * check if an element exists.
2461 * allows you to write if ($('#myid').exists()) to increase readability
2462 * @link http://stackoverflow.com/questions/31044/is-there-an-exists-function-for-jquery
2463 */
2464jQuery.fn.exists = function () {
2465	return this.length > 0;
2466};
2467
2468/**
2469 * @deprecated use OC.Util.getScrollBarWidth() instead
2470 */
2471function getScrollBarWidth() {
2472	return OC.Util.getScrollBarWidth();
2473}
2474
2475/**
2476 * jQuery tipsy shim for the bootstrap tooltip
2477 */
2478jQuery.fn.tipsy = function (argument) {
2479	console.warn('Deprecation warning: tipsy is deprecated. Use tooltip instead.');
2480	if (typeof argument === 'object' && argument !== null) {
2481
2482		// tipsy defaults
2483		var options = {
2484			placement: 'bottom',
2485			delay: {'show': 0, 'hide': 0},
2486			trigger: 'hover',
2487			html: false,
2488			container: 'body'
2489		};
2490		if (argument.gravity) {
2491			switch (argument.gravity) {
2492				case 'n':
2493				case 'nw':
2494				case 'ne':
2495					options.placement = 'bottom';
2496					break;
2497				case 's':
2498				case 'sw':
2499				case 'se':
2500					options.placement = 'top';
2501					break;
2502				case 'w':
2503					options.placement = 'right';
2504					break;
2505				case 'e':
2506					options.placement = 'left';
2507					break;
2508			}
2509		}
2510		if (argument.trigger) {
2511			options.trigger = argument.trigger;
2512		}
2513		if (argument.delayIn) {
2514			options.delay["show"] = argument.delayIn;
2515		}
2516		if (argument.delayOut) {
2517			options.delay["hide"] = argument.delayOut;
2518		}
2519		if (argument.html) {
2520			options.html = true;
2521		}
2522		if (argument.fallback) {
2523			options.title = argument.fallback;
2524		}
2525		// destroy old tooltip in case the title has changed
2526		jQuery.fn.tooltip.call(this, 'destroy');
2527		jQuery.fn.tooltip.call(this, options);
2528	} else {
2529		this.tooltip(argument);
2530		jQuery.fn.tooltip.call(this, argument);
2531	}
2532	return this;
2533}
2534
2535jQuery.extend = jQuery.fn.extend = function() {
2536	var options, name, src, copy, copyIsArray, clone,
2537		target = arguments[0] || {},
2538		i = 1,
2539		length = arguments.length,
2540		deep = false;
2541
2542	// Handle a deep copy situation
2543	if ( typeof target === "boolean" ) {
2544		deep = target;
2545
2546		// Skip the boolean and the target
2547		target = arguments[ i ] || {};
2548		i++;
2549	}
2550
2551	// Handle case when target is a string or something (possible in deep copy)
2552	if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
2553		target = {};
2554	}
2555
2556	// Extend jQuery itself if only one argument is passed
2557	if ( i === length ) {
2558		target = this;
2559		i--;
2560	}
2561
2562	for ( ; i < length; i++ ) {
2563		// Only deal with non-null/undefined values
2564		if ( (options = arguments[ i ]) != null ) {
2565			// Extend the base object
2566			for ( name in options ) {
2567				src = target[ name ];
2568				copy = options[ name ];
2569
2570				// Prevent never-ending loop
2571				if ( name === "__proto__" || target === copy ) {
2572					continue;
2573				}
2574
2575				// Recurse if we're merging plain objects or arrays
2576				if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
2577					if ( copyIsArray ) {
2578						copyIsArray = false;
vendor: 4,493 bytes, lines 2579-2748
2579						clone = src && jQuery.isArray(src) ? src : [];
2580
2581					} else {
2582						clone = src && jQuery.isPlainObject(src) ? src : {};
2583					}
2584
2585					// Never move original objects, clone them
2586					target[ name ] = jQuery.extend( deep, clone, copy );
2587
2588					// Don't bring in undefined values
2589				} else if ( copy !== undefined ) {
2590					target[ name ] = copy;
2591				}
2592			}
2593		}
2594	}
2595
2596	// Return the modified object
2597	return target;
2598};
2599
2600function isWindow( obj ) {
2601	return obj != null && obj === obj.window;
2602}
2603
2604function getDimensions( elem ) {
2605	var raw = elem[ 0 ];
2606	if ( raw.nodeType === 9 ) {
2607		return {
2608			width: elem.width(),
2609			height: elem.height(),
2610			offset: { top: 0, left: 0 }
2611		};
2612	}
2613	if ( isWindow( raw ) ) {
2614		return {
2615			width: elem.width(),
2616			height: elem.height(),
2617			offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
2618		};
2619	}
2620	if ( raw.preventDefault ) {
2621		return {
2622			width: 0,
2623			height: 0,
2624			offset: { top: raw.pageY, left: raw.pageX }
2625		};
2626	}
2627	return {
2628		width: elem.outerWidth(),
2629		height: elem.outerHeight(),
2630		offset: elem.offset()
2631	};
2632}
2633
2634var _position = $.fn.position,
2635	max = Math.max,
2636	abs = Math.abs,
2637	rhorizontal = /left|center|right/,
2638	rvertical = /top|center|bottom/,
2639	roffset = /[\+\-]\d+(\.[\d]+)?%?/,
2640	rposition = /^\w+/,
2641	rpercent = /%$/;
2642
2643function getOffsets( offsets, width, height ) {
2644	return [
2645		parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
2646		parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
2647	];
2648}
2649
2650function parseCss( element, property ) {
2651	return parseInt( $.css( element, property ), 10 ) || 0;
2652}
2653
2654$.fn.position = function( options ) {
2655	if ( !options || !options.of ) {
2656		return _position.apply( this, arguments );
2657	}
2658
2659	// Make a copy, we don't want to modify arguments
2660	options = $.extend( {}, options );
2661
2662	var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
2663
2664		// Make sure string options are treated as CSS selectors
2665		target = typeof options.of === "string" ?
2666			$( document ).find( options.of ) :
2667			$( options.of ),
2668
2669		within = $.position.getWithinInfo( options.within ),
2670		scrollInfo = $.position.getScrollInfo( within ),
2671		collision = ( options.collision || "flip" ).split( " " ),
2672		offsets = {};
2673
2674	dimensions = getDimensions( target );
2675	if ( target[ 0 ].preventDefault ) {
2676
2677		// Force left top to allow flipping
2678		options.at = "left top";
2679	}
2680	targetWidth = dimensions.width;
2681	targetHeight = dimensions.height;
2682	targetOffset = dimensions.offset;
2683
2684	// Clone to reuse original targetOffset later
2685	basePosition = $.extend( {}, targetOffset );
2686
2687	// Force my and at to have valid horizontal and vertical positions
2688	// if a value is missing or invalid, it will be converted to center
2689	$.each( [ "my", "at" ], function() {
2690		var pos = ( options[ this ] || "" ).split( " " ),
2691			horizontalOffset,
2692			verticalOffset;
2693
2694		if ( pos.length === 1 ) {
2695			pos = rhorizontal.test( pos[ 0 ] ) ?
2696				pos.concat( [ "center" ] ) :
2697				rvertical.test( pos[ 0 ] ) ?
2698					[ "center" ].concat( pos ) :
2699					[ "center", "center" ];
2700		}
2701		pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
2702		pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
2703
2704		// Calculate offsets
2705		horizontalOffset = roffset.exec( pos[ 0 ] );
2706		verticalOffset = roffset.exec( pos[ 1 ] );
2707		offsets[ this ] = [
2708			horizontalOffset ? horizontalOffset[ 0 ] : 0,
2709			verticalOffset ? verticalOffset[ 0 ] : 0
2710		];
2711
2712		// Reduce to just the positions without the offsets
2713		options[ this ] = [
2714			rposition.exec( pos[ 0 ] )[ 0 ],
2715			rposition.exec( pos[ 1 ] )[ 0 ]
2716		];
2717	} );
2718
2719	// Normalize collision option
2720	if ( collision.length === 1 ) {
2721		collision[ 1 ] = collision[ 0 ];
2722	}
2723
2724	if ( options.at[ 0 ] === "right" ) {
2725		basePosition.left += targetWidth;
2726	} else if ( options.at[ 0 ] === "center" ) {
2727		basePosition.left += targetWidth / 2;
2728	}
2729
2730	if ( options.at[ 1 ] === "bottom" ) {
2731		basePosition.top += targetHeight;
2732	} else if ( options.at[ 1 ] === "center" ) {
2733		basePosition.top += targetHeight / 2;
2734	}
2735
2736	atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
2737	basePosition.left += atOffset[ 0 ];
2738	basePosition.top += atOffset[ 1 ];
2739
2740	return this.each( function() {
2741		var collisionPosition, using,
2742			elem = $( this ),
2743			elemWidth = elem.outerWidth(),
2744			elemHeight = elem.outerHeight(),
2745			marginLeft = parseCss( this, "marginLeft" ),
2746			marginTop = parseCss( this, "marginTop" ),
2747			collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) +
2748				scrollInfo.width,
vendor: 15,913 bytes, lines 2749-3158
2749			collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) +
2750				scrollInfo.height,
2751			position = $.extend( {}, basePosition ),
2752			myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
2753
2754		if ( options.my[ 0 ] === "right" ) {
2755			position.left -= elemWidth;
2756		} else if ( options.my[ 0 ] === "center" ) {
2757			position.left -= elemWidth / 2;
2758		}
2759
2760		if ( options.my[ 1 ] === "bottom" ) {
2761			position.top -= elemHeight;
2762		} else if ( options.my[ 1 ] === "center" ) {
2763			position.top -= elemHeight / 2;
2764		}
2765
2766		position.left += myOffset[ 0 ];
2767		position.top += myOffset[ 1 ];
2768
2769		collisionPosition = {
2770			marginLeft: marginLeft,
2771			marginTop: marginTop
2772		};
2773
2774		$.each( [ "left", "top" ], function( i, dir ) {
2775			if ( $.ui.position[ collision[ i ] ] ) {
2776				$.ui.position[ collision[ i ] ][ dir ]( position, {
2777					targetWidth: targetWidth,
2778					targetHeight: targetHeight,
2779					elemWidth: elemWidth,
2780					elemHeight: elemHeight,
2781					collisionPosition: collisionPosition,
2782					collisionWidth: collisionWidth,
2783					collisionHeight: collisionHeight,
2784					offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
2785					my: options.my,
2786					at: options.at,
2787					within: within,
2788					elem: elem
2789				} );
2790			}
2791		} );
2792
2793		if ( options.using ) {
2794
2795			// Adds feedback as second argument to using callback, if present
2796			using = function( props ) {
2797				var left = targetOffset.left - position.left,
2798					right = left + targetWidth - elemWidth,
2799					top = targetOffset.top - position.top,
2800					bottom = top + targetHeight - elemHeight,
2801					feedback = {
2802						target: {
2803							element: target,
2804							left: targetOffset.left,
2805							top: targetOffset.top,
2806							width: targetWidth,
2807							height: targetHeight
2808						},
2809						element: {
2810							element: elem,
2811							left: position.left,
2812							top: position.top,
2813							width: elemWidth,
2814							height: elemHeight
2815						},
2816						horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
2817						vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
2818					};
2819				if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
2820					feedback.horizontal = "center";
2821				}
2822				if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
2823					feedback.vertical = "middle";
2824				}
2825				if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
2826					feedback.important = "horizontal";
2827				} else {
2828					feedback.important = "vertical";
2829				}
2830				options.using.call( this, props, feedback );
2831			};
2832		}
2833
2834		elem.offset( $.extend( position, { using: using } ) );
2835	} );
2836};
2837
2838$.datepicker._attachments = function (input, inst) {
2839		var showOn, buttonText, buttonImage,
2840			appendText = this._get(inst, "appendText"),
2841			isRTL = this._get(inst, "isRTL");
2842
2843		if (inst.append) {
2844			inst.append.remove();
2845		}
2846		if (appendText) {
2847			inst.append = $("<span>")
2848				.addClass(this._appendClass)
2849				.text(appendText);
2850			input[isRTL ? "before" : "after"](inst.append);
2851		}
2852
2853		input.off("focus", this._showDatepicker);
2854
2855		if (inst.trigger) {
2856			inst.trigger.remove();
2857		}
2858
2859		showOn = this._get(inst, "showOn");
2860		if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
2861			input.on("focus", this._showDatepicker);
2862		}
2863		if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
2864			buttonText = this._get(inst, "buttonText");
2865			buttonImage = this._get(inst, "buttonImage");
2866
2867			if (this._get(inst, "buttonImageOnly")) {
2868				inst.trigger = $("<img>")
2869					.addClass(this._triggerClass)
2870					.attr({
2871						src: buttonImage,
2872						alt: buttonText,
2873						title: buttonText
2874					});
2875			} else {
2876				inst.trigger = $("<button type='button'>")
2877					.addClass(this._triggerClass);
2878				if (buttonImage) {
2879					inst.trigger.html(
2880						$("<img>")
2881							.attr({
2882								src: buttonImage,
2883								alt: buttonText,
2884								title: buttonText
2885							})
2886					);
2887				} else {
2888					inst.trigger.text(buttonText);
2889				}
2890			}
2891
2892			input[isRTL ? "before" : "after"](inst.trigger);
2893			inst.trigger.on("click", function () {
2894				if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
2895					$.datepicker._hideDatepicker();
2896				} else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
2897					$.datepicker._hideDatepicker();
2898					$.datepicker._showDatepicker(input[0]);
2899				} else {
2900					$.datepicker._showDatepicker(input[0]);
2901				}
2902				return false;
2903			});
2904		}
2905	};
2906	$.datepicker._generateHTML = function( inst ) {
2907		var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
2908			controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
2909			monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
2910			selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
2911			cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
2912			printDate, dRow, tbody, daySettings, otherMonth, unselectable,
2913			tempDate = new Date(),
2914			today = this._daylightSavingAdjust(
2915				new Date( tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() ) ), // clear time
2916			isRTL = this._get( inst, "isRTL" ),
2917			showButtonPanel = this._get( inst, "showButtonPanel" ),
2918			hideIfNoPrevNext = this._get( inst, "hideIfNoPrevNext" ),
2919			navigationAsDateFormat = this._get( inst, "navigationAsDateFormat" ),
2920			numMonths = this._getNumberOfMonths( inst ),
2921			showCurrentAtPos = this._get( inst, "showCurrentAtPos" ),
2922			stepMonths = this._get( inst, "stepMonths" ),
2923			isMultiMonth = ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ),
2924			currentDate = this._daylightSavingAdjust( ( !inst.currentDay ? new Date( 9999, 9, 9 ) :
2925				new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) ),
2926			minDate = this._getMinMaxDate( inst, "min" ),
2927			maxDate = this._getMinMaxDate( inst, "max" ),
2928			drawMonth = inst.drawMonth - showCurrentAtPos,
2929			drawYear = inst.drawYear;
2930
2931		if ( drawMonth < 0 ) {
2932			drawMonth += 12;
2933			drawYear--;
2934		}
2935		if ( maxDate ) {
2936			maxDraw = this._daylightSavingAdjust( new Date( maxDate.getFullYear(),
2937				maxDate.getMonth() - ( numMonths[ 0 ] * numMonths[ 1 ] ) + 1, maxDate.getDate() ) );
2938			maxDraw = ( minDate && maxDraw < minDate ? minDate : maxDraw );
2939			while ( this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 ) ) > maxDraw ) {
2940				drawMonth--;
2941				if ( drawMonth < 0 ) {
2942					drawMonth = 11;
2943					drawYear--;
2944				}
2945			}
2946		}
2947		inst.drawMonth = drawMonth;
2948		inst.drawYear = drawYear;
2949
2950		prevText = this._get( inst, "prevText" );
2951		prevText = ( !navigationAsDateFormat ? prevText : this.formatDate( prevText,
2952			this._daylightSavingAdjust( new Date( drawYear, drawMonth - stepMonths, 1 ) ),
2953			this._getFormatConfig( inst ) ) );
2954
2955		if ( this._canAdjustMonth( inst, -1, drawYear, drawMonth ) ) {
2956			prev = $( "<a>" )
2957				.attr( {
2958					"class": "ui-datepicker-prev ui-corner-all",
2959					"data-handler": "prev",
2960					"data-event": "click",
2961					title: prevText
2962				} )
2963				.append(
2964					$( "<span>" )
2965						.addClass( "ui-icon ui-icon-circle-triangle-" +
2966							( isRTL ? "e" : "w" ) )
2967						.text( prevText )
2968				)[ 0 ].outerHTML;
2969		} else if ( hideIfNoPrevNext ) {
2970			prev = "";
2971		} else {
2972			prev = $( "<a>" )
2973				.attr( {
2974					"class": "ui-datepicker-prev ui-corner-all ui-state-disabled",
2975					title: prevText
2976				} )
2977				.append(
2978					$( "<span>" )
2979						.addClass( "ui-icon ui-icon-circle-triangle-" +
2980							( isRTL ? "e" : "w" ) )
2981						.text( prevText )
2982				)[ 0 ].outerHTML;
2983		}
2984
2985		nextText = this._get( inst, "nextText" );
2986		nextText = ( !navigationAsDateFormat ? nextText : this.formatDate( nextText,
2987			this._daylightSavingAdjust( new Date( drawYear, drawMonth + stepMonths, 1 ) ),
2988			this._getFormatConfig( inst ) ) );
2989
2990		if ( this._canAdjustMonth( inst, +1, drawYear, drawMonth ) ) {
2991			next = $( "<a>" )
2992				.attr( {
2993					"class": "ui-datepicker-next ui-corner-all",
2994					"data-handler": "next",
2995					"data-event": "click",
2996					title: nextText
2997				} )
2998				.append(
2999					$( "<span>" )
3000						.addClass( "ui-icon ui-icon-circle-triangle-" +
3001							( isRTL ? "w" : "e" ) )
3002						.text( nextText )
3003				)[ 0 ].outerHTML;
3004		} else if ( hideIfNoPrevNext ) {
3005			next = "";
3006		} else {
3007			next = $( "<a>" )
3008				.attr( {
3009					"class": "ui-datepicker-next ui-corner-all ui-state-disabled",
3010					title: nextText
3011				} )
3012				.append(
3013					$( "<span>" )
3014						.attr( "class", "ui-icon ui-icon-circle-triangle-" +
3015							( isRTL ? "w" : "e" ) )
3016						.text( nextText )
3017				)[ 0 ].outerHTML;
3018		}
3019
3020		currentText = this._get( inst, "currentText" );
3021		gotoDate = ( this._get( inst, "gotoCurrent" ) && inst.currentDay ? currentDate : today );
3022		currentText = ( !navigationAsDateFormat ? currentText :
3023			this.formatDate( currentText, gotoDate, this._getFormatConfig( inst ) ) );
3024
3025		controls = "";
3026		if ( !inst.inline ) {
3027			controls = $( "<button>" )
3028				.attr( {
3029					type: "button",
3030					"class": "ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all",
3031					"data-handler": "hide",
3032					"data-event": "click"
3033				} )
3034				.text( this._get( inst, "closeText" ) )[ 0 ].outerHTML;
3035		}
3036
3037		buttonPanel = "";
3038		if ( showButtonPanel ) {
3039			buttonPanel = $( "<div class='ui-datepicker-buttonpane ui-widget-content'>" )
3040				.append( isRTL ? controls : "" )
3041				.append( this._isInRange( inst, gotoDate ) ?
3042					$( "<button>" )
3043						.attr( {
3044							type: "button",
3045							"class": "ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all",
3046							"data-handler": "today",
3047							"data-event": "click"
3048						} )
3049						.text( currentText ) :
3050					"" )
3051				.append( isRTL ? "" : controls )[ 0 ].outerHTML;
3052		}
3053
3054		firstDay = parseInt( this._get( inst, "firstDay" ), 10 );
3055		firstDay = ( isNaN( firstDay ) ? 0 : firstDay );
3056
3057		showWeek = this._get( inst, "showWeek" );
3058		dayNames = this._get( inst, "dayNames" );
3059		dayNamesMin = this._get( inst, "dayNamesMin" );
3060		monthNames = this._get( inst, "monthNames" );
3061		monthNamesShort = this._get( inst, "monthNamesShort" );
3062		beforeShowDay = this._get( inst, "beforeShowDay" );
3063		showOtherMonths = this._get( inst, "showOtherMonths" );
3064		selectOtherMonths = this._get( inst, "selectOtherMonths" );
3065		defaultDate = this._getDefaultDate( inst );
3066		html = "";
3067
3068		for ( row = 0; row < numMonths[ 0 ]; row++ ) {
3069			group = "";
3070			this.maxRows = 4;
3071			for ( col = 0; col < numMonths[ 1 ]; col++ ) {
3072				selectedDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, inst.selectedDay ) );
3073				cornerClass = " ui-corner-all";
3074				calender = "";
3075				if ( isMultiMonth ) {
3076					calender += "<div class='ui-datepicker-group";
3077					if ( numMonths[ 1 ] > 1 ) {
3078						switch ( col ) {
3079							case 0: calender += " ui-datepicker-group-first";
3080								cornerClass = " ui-corner-" + ( isRTL ? "right" : "left" ); break;
3081							case numMonths[ 1 ] - 1: calender += " ui-datepicker-group-last";
3082								cornerClass = " ui-corner-" + ( isRTL ? "left" : "right" ); break;
3083							default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
3084						}
3085					}
3086					calender += "'>";
3087				}
3088				calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
3089					( /all|left/.test( cornerClass ) && row === 0 ? ( isRTL ? next : prev ) : "" ) +
3090					( /all|right/.test( cornerClass ) && row === 0 ? ( isRTL ? prev : next ) : "" ) +
3091					this._generateMonthYearHeader( inst, drawMonth, drawYear, minDate, maxDate,
3092						row > 0 || col > 0, monthNames, monthNamesShort ) + // draw month headers
3093					"</div><table class='ui-datepicker-calendar'><thead>" +
3094					"<tr>";
3095				thead = ( showWeek ? "<th class='ui-datepicker-week-col'>" + this._get( inst, "weekHeader" ) + "</th>" : "" );
3096				for ( dow = 0; dow < 7; dow++ ) { // days of the week
3097					day = ( dow + firstDay ) % 7;
3098					thead += "<th scope='col'" + ( ( dow + firstDay + 6 ) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "" ) + ">" +
3099						"<span title='" + dayNames[ day ] + "'>" + dayNamesMin[ day ] + "</span></th>";
3100				}
3101				calender += thead + "</tr></thead><tbody>";
3102				daysInMonth = this._getDaysInMonth( drawYear, drawMonth );
3103				if ( drawYear === inst.selectedYear && drawMonth === inst.selectedMonth ) {
3104					inst.selectedDay = Math.min( inst.selectedDay, daysInMonth );
3105				}
3106				leadDays = ( this._getFirstDayOfMonth( drawYear, drawMonth ) - firstDay + 7 ) % 7;
3107				curRows = Math.ceil( ( leadDays + daysInMonth ) / 7 ); // calculate the number of rows to generate
3108				numRows = ( isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows ); //If multiple months, use the higher number of rows (see #7043)
3109				this.maxRows = numRows;
3110				printDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 - leadDays ) );
3111				for ( dRow = 0; dRow < numRows; dRow++ ) { // create date picker rows
3112					calender += "<tr>";
3113					tbody = ( !showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
3114						this._get( inst, "calculateWeek" )( printDate ) + "</td>" );
3115					for ( dow = 0; dow < 7; dow++ ) { // create date picker days
3116						daySettings = ( beforeShowDay ?
3117							beforeShowDay.apply( ( inst.input ? inst.input[ 0 ] : null ), [ printDate ] ) : [ true, "" ] );
3118						otherMonth = ( printDate.getMonth() !== drawMonth );
3119						unselectable = ( otherMonth && !selectOtherMonths ) || !daySettings[ 0 ] ||
3120							( minDate && printDate < minDate ) || ( maxDate && printDate > maxDate );
3121						tbody += "<td class='" +
3122							( ( dow + firstDay + 6 ) % 7 >= 5 ? " ui-datepicker-week-end" : "" ) + // highlight weekends
3123							( otherMonth ? " ui-datepicker-other-month" : "" ) + // highlight days from other months
3124							( ( printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent ) || // user pressed key
3125							( defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime() ) ?
3126
3127								// or defaultDate is current printedDate and defaultDate is selectedDate
3128								" " + this._dayOverClass : "" ) + // highlight selected day
3129							( unselectable ? " " + this._unselectableClass + " ui-state-disabled" : "" ) +  // highlight unselectable days
3130							( otherMonth && !showOtherMonths ? "" : " " + daySettings[ 1 ] + // highlight custom dates
3131								( printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "" ) + // highlight selected day
3132								( printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "" ) ) + "'" + // highlight today (if different)
3133							( ( !otherMonth || showOtherMonths ) && daySettings[ 2 ] ? " title='" + daySettings[ 2 ].replace( /'/g, "&#39;" ) + "'" : "" ) + // cell title
3134							( unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'" ) + ">" + // actions
3135							( otherMonth && !showOtherMonths ? "&#xa0;" : // display for other months
3136								( unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
3137									( printDate.getTime() === today.getTime() ? " ui-state-highlight" : "" ) +
3138									( printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "" ) + // highlight selected day
3139									( otherMonth ? " ui-priority-secondary" : "" ) + // distinguish dates from other months
3140									"' href='#'>" + printDate.getDate() + "</a>" ) ) + "</td>"; // display selectable date
3141						printDate.setDate( printDate.getDate() + 1 );
3142						printDate = this._daylightSavingAdjust( printDate );
3143					}
3144					calender += tbody + "</tr>";
3145				}
3146				drawMonth++;
3147				if ( drawMonth > 11 ) {
3148					drawMonth = 0;
3149					drawYear++;
3150				}
3151				calender += "</tbody></table>" + ( isMultiMonth ? "</div>" +
3152					( ( numMonths[ 0 ] > 0 && col === numMonths[ 1 ] - 1 ) ? "<div class='ui-datepicker-row-break'></div>" : "" ) : "" );
3153				group += calender;
3154			}
3155			html += group;
3156		}
3157		html += buttonPanel;
3158		inst._keyEvent = false;
3159		return html;
3160	};
3161	$.datepicker._updateAlternate = function( inst ) {
3162		var altFormat, date, dateStr,
3163			altField = this._get( inst, "altField" );
3164		if ( altField ) { // update alternate field too
3165			altFormat = this._get( inst, "altFormat" ) || this._get( inst, "dateFormat" );
3166			date = this._getDate( inst );
3167			dateStr = this.formatDate( altFormat, date, this._getFormatConfig( inst ) );
3168			$( altField ).val( dateStr );
3169			$( document ).find( altField ).val( dateStr );
3170		}
3171	};
3172
3173	$.ui.dialog._setOption = function( key, value ) {
3174		if (key === "disabled") {
3175			return;
3176		}
3177		this._super(key, value);
3178		if (key === "appendTo") {
3179			this.uiDialog.appendTo(this._appendTo());
3180		}
3181		if (key === "buttons") {
3182			this._createButtons();
3183		}
3184		if (key === "closeText") {
3185			this.uiDialogTitlebarClose.button({
3186				// Ensure that we always pass a string
3187				label: $("<a>").text("" + this.options.closeText).html()
3188			});
3189		}
3190	};
3191
3192	$.ui.dialog.dialog_createTitlebar = function() {
3193	var uiDialogTitle;
3194	this.uiDialogTitlebar = $( "<div>" );
3195	this._addClass( this.uiDialogTitlebar,
3196		"ui-dialog-titlebar", "ui-widget-header ui-helper-clearfix" );
3197	this._on( this.uiDialogTitlebar, {
3198		mousedown: function( event ) {
3199			// Don't prevent click on close button (#8838)
3200			// Focusing a dialog that is partially scrolled out of view
3201			// causes the browser to scroll it into view, preventing the click event
3202			if ( !$( event.target ).closest( ".ui-dialog-titlebar-close" ) ) {
3203				// Dialog isn't getting focus when dragging (#8063)
3204				this.uiDialog.trigger( "focus" );
3205			}
3206		}
3207	} );
3208	// Support: IE
3209	// Use type="button" to prevent enter keypresses in textboxes from closing the
3210	// dialog in IE (#9312)
3211	this.uiDialogTitlebarClose = $( "<button type='button'></button>" )
3212		.button( {
3213			label: $( "<a>" ).text( this.options.closeText ).html(),
3214			icon: "ui-icon-closethick",
3215			showLabel: false
3216		} )
3217		.appendTo( this.uiDialogTitlebar );
3218	this._addClass( this.uiDialogTitlebarClose, "ui-dialog-titlebar-close" );
3219	this._on( this.uiDialogTitlebarClose, {
3220		click: function( event ) {
3221			event.preventDefault();
3222			this.close( event );
3223		}
3224	} );
3225	uiDialogTitle = $( "<span>" ).uniqueId().prependTo( this.uiDialogTitlebar );
3226	this._addClass( uiDialogTitle, "ui-dialog-title" );
3227	this._title( uiDialogTitle );
3228	this.uiDialogTitlebar.prependTo( this.uiDialog );
3229	this.uiDialog.attr( {
3230		"aria-labelledby": uiDialogTitle.attr( "id" )
3231	} );
3232};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.