Advertisement
Guest User

Untitled

a guest
Jan 10th, 2020
336
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 364.00 KB | None | 0 0
  1. <details>
  2. <summary>NextCloudPi diagnostics</summary>
  3.  
  4. ```
  5.  
  6. Cannot load Zend OPcache - it was already loaded
  7. NextCloudPi version v1.13.6
  8. NextCloudPi image NextCloudPi_03-09-19
  9. distribution Raspbian GNU/Linux 9 \n \l
  10. automount yes
  11. USB devices sda
  12. datadir /media/USBdrive/ncdata
  13. data in SD no
  14. data filesystem btrfs
  15. data disk usage 27M/231G
  16. rootfs usage 2.1G/29G
  17. swapfile /var/swap
  18. dbdir /var/lib/mysql
  19. Nextcloud check error
  20. HTTPD service up
  21. PHP service up
  22. MariaDB service up
  23. Redis service up
  24. Postfix service up
  25. internet check ok
  26. port check 80 open
  27. port check 443 open
  28. IP ***REMOVED SENSITIVE VALUE***
  29. gateway ***REMOVED SENSITIVE VALUE***
  30. interface wlan0
  31. certificates ***REMOVED SENSITIVE VALUE***
  32. NAT loopback no
  33. uptime 7min
  34.  
  35. ```
  36. </details>
  37.  
  38.  
  39. <details>
  40. <summary>Nextcloud configuration</summary>
  41.  
  42. ```
  43.  
  44. Cannot load Zend OPcache - it was already loaded
  45. Your data directory is invalid
  46. Ensure there is a file called ".ocdata" in the root of the data directory.
  47.  
  48. An unhandled exception has been thrown:
  49. Exception: Environment not properly prepared. in /var/www/nextcloud/lib/private/Console/Application.php:166
  50. Stack trace:
  51. #0 /var/www/nextcloud/console.php(95): OC\Console\Application->loadCommands(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
  52. #1 /var/www/nextcloud/occ(11): require_once('/var/www/nextcl...')
  53. #2 {main}
  54. ```
  55. </details>
  56.  
  57.  
  58. <details>
  59. <summary>HTTPd logs</summary>
  60.  
  61. ```
  62.  
  63. [Fri Jan 10 14:37:55.054998 2020] [ssl:warn] [pid 789:tid 1995911408] AH01909: localhost:4443:0 server certificate does NOT include an ID which matches the server name
  64. [Fri Jan 10 14:37:56.002732 2020] [mpm_event:notice] [pid 789:tid 1995911408] AH00489: Apache/2.4.25 (Raspbian) OpenSSL/1.0.2l configured -- resuming normal operations
  65. [Fri Jan 10 14:37:56.002939 2020] [core:notice] [pid 789:tid 1995911408] AH00094: Command line: '/usr/sbin/apache2'
  66. [Fri Jan 10 14:37:54.310323 2020] [ssl:warn] [pid 531:tid 1996128496] AH01909: localhost:4443:0 server certificate does NOT include an ID which matches the server name
  67. [Fri Jan 10 14:37:55.055508 2020] [ssl:warn] [pid 798:tid 1996128496] AH01909: localhost:4443:0 server certificate does NOT include an ID which matches the server name
  68. [Fri Jan 10 14:37:56.003970 2020] [mpm_event:notice] [pid 798:tid 1996128496] AH00489: Apache/2.4.25 (Raspbian) OpenSSL/1.0.2l configured -- resuming normal operations
  69. [Fri Jan 10 14:37:56.004140 2020] [core:notice] [pid 798:tid 1996128496] AH00094: Command line: '/usr/sbin/apache2'
  70. [Fri Jan 10 14:36:39.676149 2020] [ssl:warn] [pid 637:tid 1995550960] AH01909: localhost:4443:0 server certificate does NOT include an ID which matches the server name
  71. [Fri Jan 10 14:36:40.051434 2020] [ssl:warn] [pid 939:tid 1995550960] AH01909: localhost:4443:0 server certificate does NOT include an ID which matches the server name
  72. [Fri Jan 10 14:36:41.003771 2020] [mpm_event:notice] [pid 939:tid 1995550960] AH00489: Apache/2.4.25 (Raspbian) OpenSSL/1.0.2l configured -- resuming normal operations
  73. [Fri Jan 10 14:36:41.003941 2020] [core:notice] [pid 939:tid 1995550960] AH00094: Command line: '/usr/sbin/apache2'
  74. [Fri Jan 10 14:50:41.255124 2020] [proxy_fcgi:error] [pid 1007:tid 1707430960] [client 192.168.178.86:51798] AH01067: Failed to read FastCGI header
  75. [Fri Jan 10 14:50:41.255276 2020] [proxy_fcgi:error] [pid 1007:tid 1707430960] (104)Connection reset by peer: [client 192.168.178.86:51798] AH01075: Error dispatching request to :4443:
  76. [Fri Jan 10 14:50:41.557730 2020] [mpm_event:notice] [pid 939:tid 1995550960] AH00491: caught SIGTERM, shutting down
  77. [Fri Jan 10 14:50:59.952779 2020] [ssl:warn] [pid 645:tid 1996206320] AH01909: localhost:4443:0 server certificate does NOT include an ID which matches the server name
  78. [Fri Jan 10 14:51:00.059674 2020] [ssl:warn] [pid 937:tid 1996206320] AH01909: localhost:4443:0 server certificate does NOT include an ID which matches the server name
  79. [Fri Jan 10 14:51:01.004046 2020] [mpm_event:notice] [pid 937:tid 1996206320] AH00489: Apache/2.4.25 (Raspbian) OpenSSL/1.0.2l configured -- resuming normal operations
  80. [Fri Jan 10 14:51:01.004209 2020] [core:notice] [pid 937:tid 1996206320] AH00094: Command line: '/usr/sbin/apache2'
  81. [Fri Jan 10 14:51:01.183191 2020] [proxy:error] [pid 981:tid 1691284528] (2)No such file or directory: AH02454: FCGI: attempt to connect to Unix domain socket /run/php/php7.2-fpm.sock (*) failed
  82. [Fri Jan 10 14:51:01.183321 2020] [proxy_fcgi:error] [pid 981:tid 1691284528] [client 192.168.178.86:51908] AH01079: failed to make connection to backend: httpd-UDS
  83.  
  84. ```
  85. </details>
  86.  
  87.  
  88. <details>
  89. <summary>Database logs</summary>
  90.  
  91. ```
  92.  
  93. 2020-01-10 14:50:43 1970905920 [Note] /usr/sbin/mysqld: Shutdown complete
  94.  
  95. 2020-01-10 14:51:31 1995558704 [Note] InnoDB: Using mutexes to ref count buffer pool pages
  96. 2020-01-10 14:51:31 1995558704 [Note] InnoDB: The InnoDB memory heap is disabled
  97. 2020-01-10 14:51:31 1995558704 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
  98. 2020-01-10 14:51:31 1995558704 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used for memory barrier
  99. 2020-01-10 14:51:31 1995558704 [Note] InnoDB: Compressed tables use zlib 1.2.8
  100. 2020-01-10 14:51:31 1995558704 [Note] InnoDB: Using Linux native AIO
  101. 2020-01-10 14:51:31 1995558704 [Note] InnoDB: Using generic crc32 instructions
  102. 2020-01-10 14:51:31 1995558704 [Note] InnoDB: Initializing buffer pool, size = 370.0M
  103. 2020-01-10 14:51:31 1995558704 [Note] InnoDB: Completed initialization of buffer pool
  104. 2020-01-10 14:51:32 1995558704 [Note] InnoDB: Highest supported file format is Barracuda.
  105. 2020-01-10 14:51:33 1995558704 [Note] InnoDB: 128 rollback segment(s) are active.
  106. 2020-01-10 14:51:33 1995558704 [Note] InnoDB: Waiting for purge to start
  107. 2020-01-10 14:51:33 1995558704 [Note] InnoDB: Percona XtraDB (http://www.percona.com) 5.6.41-84.1 started; log sequence number 317722066
  108. 2020-01-10 14:51:33 1111487296 [Note] InnoDB: Dumping buffer pool(s) not yet started
  109. 2020-01-10 14:51:33 1995558704 [Note] Plugin 'FEEDBACK' is disabled.
  110. 2020-01-10 14:51:33 1995558704 [Note] Server socket created on IP: '127.0.0.1'.
  111. 2020-01-10 14:51:33 1995558704 [Note] /usr/sbin/mysqld: ready for connections.
  112. Version: '10.1.37-MariaDB-0+deb9u1' socket: '/var/run/mysqld/mysqld.sock' port: 3306 Raspbian 9.0
  113.  
  114. ```
  115. </details>
  116.  
  117.  
  118. <details>
  119. <summary>Nextcloud logs</summary>
  120.  
  121. ```
  122.  
  123. {"reqId":"XhiQ7H8AAQEAAAPUCfcAAAcA","level":3,"time":"2020-01-10T14:57:48+00:00","remoteAddr":"87.123.189.179","user":"Schalli","app":"PHP","method":"GET","url":"\/status.php","message":"file_get_contents(\/media\/USBdrive\/ncdata\/appdata_ocmkxn4qrngc\/js\/core\/merged-template-prepend.js.deps): failed to open stream: No such file or directory at \/var\/www\/nextcloud\/lib\/private\/Files\/Storage\/Local.php#216","userAgent":"Mozilla\/5.0 (Windows) mirall\/2.6.2stable-Win64 (build 20191224) (Nextcloud)","version":"15.0.5.3"}
  124. {"reqId":"XhiQ7H8AAQEAAAPUCfcAAAcA","level":3,"time":"2020-01-10T14:57:48+00:00","remoteAddr":"87.123.189.179","user":"Schalli","app":"PHP","method":"GET","url":"\/status.php","message":"stat(): stat failed for \/media\/USBdrive\/ncdata\/appdata_ocmkxn4qrngc\/js\/core\/merged-template-prepend.js.deps at \/var\/www\/nextcloud\/lib\/private\/Files\/Storage\/Local.php#142","userAgent":"Mozilla\/5.0 (Windows) mirall\/2.6.2stable-Win64 (build 20191224) (Nextcloud)","version":"15.0.5.3"}
  125. {"reqId":"XhiQ7H8AAQEAAAPUCfcAAAcA","level":3,"time":"2020-01-10T14:57:48+00:00","remoteAddr":"87.123.189.179","user":"Schalli","app":"PHP","method":"GET","url":"\/status.php","message":"filesize(): stat failed for \/media\/USBdrive\/ncdata\/appdata_ocmkxn4qrngc\/js\/core\/merged-template-prepend.js.deps at \/var\/www\/nextcloud\/lib\/private\/LargeFileHelper.php#171","userAgent":"Mozilla\/5.0 (Windows) mirall\/2.6.2stable-Win64 (build 20191224) (Nextcloud)","version":"15.0.5.3"}
  126. {"reqId":"XhiQ7H8AAQEAAAPUCfcAAAcA","level":3,"time":"2020-01-10T14:57:48+00:00","remoteAddr":"87.123.189.179","user":"Schalli","app":"PHP","method":"GET","url":"\/status.php","message":"file_put_contents(\/media\/USBdrive\/ncdata\/appdata_ocmkxn4qrngc\/js\/core\/merged-template-prepend.js): failed to open stream: No such file or directory at \/var\/www\/nextcloud\/lib\/private\/Files\/Storage\/Local.php#220","userAgent":"Mozilla\/5.0 (Windows) mirall\/2.6.2stable-Win64 (build 20191224) (Nextcloud)","version":"15.0.5.3"}
  127. {"reqId":"XhiQ7H8AAQEAAAPUCfcAAAcA","level":3,"time":"2020-01-10T14:57:48+00:00","remoteAddr":"87.123.189.179","user":"Schalli","app":"remote","method":"GET","url":"\/status.php","message":{"Exception":"OCP\\Files\\GenericFileException","Message":"file_put_contents failed","Code":0,"Trace":[{"file":"\/var\/www\/nextcloud\/lib\/private\/Files\/SimpleFS\/SimpleFile.php","line":104,"function":"putContent","class":"OC\\Files\\Node\\File","type":"->","args":["(function($) {\n\t$.widget('oc.ocdialog', {\n\t\toptions: {\n\t\t\twidth: 'auto',\n\t\t\theight: 'auto',\n\t\t\tcloseButton: true,\n\t\t\tcloseOnEscape: true,\n\t\t\tmodal: false\n\t\t},\n\t\t_create: function() {\n\t\t\tvar self = this;\n\n\t\t\tthis.originalCss = {\n\t\t\t\tdisplay: this.element[0].style.display,\n\t\t\t\twidth: this.element[0].style.width,\n\t\t\t\theight: this.element[0].style.height\n\t\t\t};\n\n\t\t\tthis.originalTitle = this.element.attr('title');\n\t\t\tthis.options.title = this.options.title || this.originalTitle;\n\n\t\t\tthis.$dialog = $('<div class=\"oc-dialog\" \/>')\n\t\t\t\t.attr({\n\t\t\t\t\t\/\/ Setting tabIndex makes the div focusable\n\t\t\t\t\ttabIndex: -1,\n\t\t\t\t\trole: 'dialog'\n\t\t\t\t})\n\t\t\t\t.insertBefore(this.element);\n\t\t\tthis.$dialog.append(this.element.detach());\n\t\t\tthis.element.removeAttr('title').addClass('oc-dialog-content').appendTo(this.$dialog);\n\n\t\t\tthis.$dialog.css({\n\t\t\t\tdisplay: 'inline-block',\n\t\t\t\tposition: 'fixed'\n\t\t\t});\n\n\t\t\t$(document).on('keydown keyup', function(event) {\n\t\t\t\tif (\n\t\t\t\t\tevent.target !== self.$dialog.get(0) &&\n\t\t\t\t\tself.$dialog.find($(event.target)).length === 0\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t\/\/ Escape\n\t\t\t\tif (\n\t\t\t\t\tevent.keyCode === 27 &&\n\t\t\t\t\tevent.type === 'keydown' &&\n\t\t\t\t\tself.options.closeOnEscape\n\t\t\t\t) {\n\t\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t\t\tself.close();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t\/\/ Enter\n\t\t\t\tif(event.keyCode === 13) {\n\t\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t\t\tif(event.type === 'keyup') {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t\/\/ If no button is selected we trigger the primary\n\t\t\t\t\tif (\n\t\t\t\t\t\tself.$buttonrow &&\n\t\t\t\t\t\tself.$buttonrow.find($(event.target)).length === 0\n\t\t\t\t\t) {\n\t\t\t\t\t\tvar $button = self.$buttonrow.find('button.primary');\n\t\t\t\t\t\tif($button && !$button.prop('disabled')) {\n\t\t\t\t\t\t\t$button.trigger('click');\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if(self.$buttonrow) {\n\t\t\t\t\t\t$(event.target).trigger('click');\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tthis._setOptions(this.options);\n\t\t\tthis._createOverlay();\n\t\t},\n\t\t_init: function() {\n\t\t\tthis.$dialog.focus();\n\t\t\tthis._trigger('open');\n\t\t},\n\t\t_setOption: function(key, value) {\n\t\t\tvar self = this;\n\t\t\tswitch(key) {\n\t\t\t\tcase 'title':\n\t\t\t\t\tif(this.$title) {\n\t\t\t\t\t\tthis.$title.text(value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar $title = $('<h2 class=\"oc-dialog-title\">'\n\t\t\t\t\t\t\t+ value\n\t\t\t\t\t\t\t+ '<\/h2>');\n\t\t\t\t\t\tthis.$title = $title.prependTo(this.$dialog);\n\t\t\t\t\t}\n\t\t\t\t\tthis._setSizes();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'buttons':\n\t\t\t\t\tif(this.$buttonrow) {\n\t\t\t\t\t\tthis.$buttonrow.empty();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar $buttonrow = $('<div class=\"oc-dialog-buttonrow\" \/>');\n\t\t\t\t\t\tthis.$buttonrow = $buttonrow.appendTo(this.$dialog);\n\t\t\t\t\t}\n\t\t\t\t\tif (value.length === 1) {\n\t\t\t\t\t\tthis.$buttonrow.addClass('onebutton');\n\t\t\t\t\t} else if (value.length === 2) {\n\t\t\t\t\t\tthis.$buttonrow.addClass('twobuttons');\n\t\t\t\t\t} else if (value.length === 3) {\n\t\t\t\t\t\tthis.$buttonrow.addClass('threebuttons');\n\t\t\t\t\t}\n\t\t\t\t\t$.each(value, function(idx, val) {\n\t\t\t\t\t\tvar $button = $('<button>').text(val.text);\n\t\t\t\t\t\tif (val.classes) {\n\t\t\t\t\t\t\t$button.addClass(val.classes);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif(val.defaultButton) {\n\t\t\t\t\t\t\t$button.addClass('primary');\n\t\t\t\t\t\t\tself.$defaultButton = $button;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tself.$buttonrow.append($button);\n\t\t\t\t\t\t$button.click(function() {\n\t\t\t\t\t\t\tval.click.apply(self.element[0], arguments);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t\tthis.$buttonrow.find('button')\n\t\t\t\t\t\t.on('focus', function(event) {\n\t\t\t\t\t\t\tself.$buttonrow.find('button').removeClass('primary');\n\t\t\t\t\t\t\t$(this).addClass('primary');\n\t\t\t\t\t\t});\n\t\t\t\t\tthis._setSizes();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'style':\n\t\t\t\t\tif (value.buttons !== undefined) {\n\t\t\t\t\t\tthis.$buttonrow.addClass(value.buttons);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'closeButton':\n\t\t\t\t\tif(value) {\n\t\t\t\t\t\tvar $closeButton = $('<a class=\"oc-dialog-close\"><\/a>');\n\t\t\t\t\t\tthis.$dialog.prepend($closeButton);\n\t\t\t\t\t\t$closeButton.on('click', function() {\n\t\t\t\t\t\t\tself.close();\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.$dialog.find('.oc-dialog-close').remove();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'width':\n\t\t\t\t\tthis.$dialog.css('width', value);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'height':\n\t\t\t\t\tthis.$dialog.css('height', value);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'close':\n\t\t\t\t\tthis.closeCB = value;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\/\/this._super(key, value);\n\t\t\t$.Widget.prototype._setOption.apply(this, arguments );\n\t\t},\n\t\t_setOptions: function(options) {\n\t\t\t\/\/this._super(options);\n\t\t\t$.Widget.prototype._setOptions.apply(this, arguments);\n\t\t},\n\t\t_setSizes: function() {\n\t\t\tvar lessHeight = 0;\n\t\t\tif(this.$title) {\n\t\t\t\tlessHeight += this.$title.outerHeight(true);\n\t\t\t}\n\t\t\tif(this.$buttonrow) {\n\t\t\t\tlessHeight += this.$buttonrow.outerHeight(true);\n\t\t\t}\n\t\t\tthis.element.css({\n\t\t\t\t'height': 'calc(100% - ' + lessHeight + 'px)'\n\t\t\t});\n\t\t},\n\t\t_createOverlay: function() {\n\t\t\tif(!this.options.modal) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar self = this;\n\t\t\tthis.overlay = $('<div>')\n\t\t\t\t.addClass('oc-dialog-dim')\n\t\t\t\t.appendTo($('#content'));\n\t\t\tthis.overlay.on('click keydown keyup', function(event) {\n\t\t\t\tif(event.target !== self.$dialog.get(0) && self.$dialog.find($(event.target)).length === 0) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\t_destroyOverlay: function() {\n\t\t\tif (!this.options.modal) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (this.overlay) {\n\t\t\t\tthis.overlay.off('click keydown keyup');\n\t\t\t\tthis.overlay.remove();\n\t\t\t\tthis.overlay = null;\n\t\t\t}\n\t\t},\n\t\twidget: function() {\n\t\t\treturn this.$dialog;\n\t\t},\n\t\tclose: function() {\n\t\t\tthis._destroyOverlay();\n\t\t\tvar self = this;\n\t\t\t\/\/ Ugly hack to catch remaining keyup events.\n\t\t\tsetTimeout(function() {\n\t\t\t\tself._trigger('close', self);\n\t\t\t}, 200);\n\n\t\t\tself.$dialog.remove();\n\t\t\tthis.destroy();\n\t\t},\n\t\tdestroy: function() {\n\t\t\tif(this.$title) {\n\t\t\t\tthis.$title.remove();\n\t\t\t}\n\t\t\tif(this.$buttonrow) {\n\t\t\t\tthis.$buttonrow.remove();\n\t\t\t}\n\n\t\t\tif(this.originalTitle) {\n\t\t\t\tthis.element.attr('title', this.originalTitle);\n\t\t\t}\n\t\t\tthis.element.removeClass('oc-dialog-content')\n\t\t\t\t\t.css(this.originalCss).detach().insertBefore(this.$dialog);\n\t\t\tthis.$dialog.remove();\n\t\t}\n\t});\n}(jQuery));\n\n\n\/**\n * ownCloud\n *\n * @author Bartek Przybylski, Christopher Sch\u00e4pers, Thomas Tanghus\n * @copyright 2012 Bartek Przybylski bartek@alefzero.eu\n *\n * This library is free software; you can redistribute it and\/or\n * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE\n * License as published by the Free Software Foundation; either\n * version 3 of the License, or any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU AFFERO GENERAL PUBLIC LICENSE for more details.\n *\n * You should have received a copy of the GNU Affero General Public\n * License along with this library. If not, see <http:\/\/www.gnu.org\/licenses\/>.\n *\n *\/\n\n\/* global alert *\/\n\n\/**\n * this class to ease the usage of jquery dialogs\n * @lends OC.dialogs\n *\/\nvar OCdialogs = {\n\t\/\/ dialog button types\n\tYES_NO_BUTTONS:\t\t70,\n\tOK_BUTTONS:\t\t71,\n\n\tFILEPICKER_TYPE_CHOOSE: 1,\n\tFILEPICKER_TYPE_MOVE: 2,\n\tFILEPICKER_TYPE_COPY: 3,\n\tFILEPICKER_TYPE_COPY_MOVE: 4,\n\n\t\/\/ used to name each dialog\n\tdialogsCounter: 0,\n\t\/**\n\t* displays alert dialog\n\t* @param text content of dialog\n\t* @param title dialog title\n\t* @param callback which will be triggered when user presses OK\n\t* @param modal make the dialog modal\n\t*\/\n\talert:function(text, title, callback, modal) {\n\t\tthis.message(\n\t\t\ttext,\n\t\t\ttitle,\n\t\t\t'alert',\n\t\t\tOCdialogs.OK_BUTTON,\n\t\t\tcallback,\n\t\t\tmodal\n\t\t);\n\t},\n\t\/**\n\t* displays info dialog\n\t* @param text content of dialog\n\t* @param title dialog title\n\t* @param callback which will be triggered when user presses OK\n\t* @param modal make the dialog modal\n\t*\/\n\tinfo:function(text, title, callback, modal) {\n\t\tthis.message(text, title, 'info', OCdialogs.OK_BUTTON, callback, modal);\n\t},\n\t\/**\n\t* displays confirmation dialog\n\t* @param text content of dialog\n\t* @param title dialog title\n\t* @param callback which will be triggered when user presses YES or NO\n\t* (true or false would be passed to callback respectively)\n\t* @param modal make the dialog modal\n\t*\/\n\tconfirm:function(text, title, callback, modal) {\n\t\treturn this.message(\n\t\t\ttext,\n\t\t\ttitle,\n\t\t\t'notice',\n\t\t\tOCdialogs.YES_NO_BUTTONS,\n\t\t\tcallback,\n\t\t\tmodal\n\t\t);\n\t},\n\t\/**\n\t* displays confirmation dialog\n\t* @param text content of dialog\n\t* @param title dialog title\n\t* @param callback which will be triggered when user presses YES or NO\n\t* (true or false would be passed to callback respectively)\n\t* @param modal make the dialog modal\n\t*\/\n\tconfirmHtml:function(text, title, callback, modal) {\n\t\treturn this.message(\n\t\t\ttext,\n\t\t\ttitle,\n\t\t\t'notice',\n\t\t\tOCdialogs.YES_NO_BUTTONS,\n\t\t\tcallback,\n\t\t\tmodal,\n\t\t\ttrue\n\t\t);\n\t},\n\t\/**\n\t * displays prompt dialog\n\t * @param text content of dialog\n\t * @param title dialog title\n\t * @param callback which will be triggered when user presses YES or NO\n\t * (true or false would be passed to callback respectively)\n\t * @param modal make the dialog modal\n\t * @param name name of the input field\n\t * @param password whether the input should be a password input\n\t *\/\n\tprompt: function (text, title, callback, modal, name, password) {\n\t\treturn $.when(this._getMessageTemplate()).then(function ($tmpl) {\n\t\t\tvar dialogName = 'oc-dialog-' + OCdialogs.dialogsCounter + '-content';\n\t\t\tvar dialogId = '#' + dialogName;\n\t\t\tvar $dlg = $tmpl.octemplate({\n\t\t\t\tdialog_name: dialogName,\n\t\t\t\ttitle : title,\n\t\t\t\tmessage : text,\n\t\t\t\ttype : 'notice'\n\t\t\t});\n\t\t\tvar input = $('<input\/>');\n\t\t\tinput.attr('type', password ? 'password' : 'text').attr('id', dialogName + '-input').attr('placeholder', name);\n\t\t\tvar label = $('<label\/>').attr('for', dialogName + '-input').text(name + ': ');\n\t\t\t$dlg.append(label);\n\t\t\t$dlg.append(input);\n\t\t\tif (modal === undefined) {\n\t\t\t\tmodal = false;\n\t\t\t}\n\t\t\t$('body').append($dlg);\n\n\t\t\t\/\/ wrap callback in _.once():\n\t\t\t\/\/ only call callback once and not twice (button handler and close\n\t\t\t\/\/ event) but call it for the close event, if ESC or the x is hit\n\t\t\tif (callback !== undefined) {\n\t\t\t\tcallback = _.once(callback);\n\t\t\t}\n\n\t\t\tvar buttonlist = [{\n\t\t\t\t\ttext : t('core', 'No'),\n\t\t\t\t\tclick: function () {\n\t\t\t\t\t\tif (callback !== undefined) {\n\t\t\t\t\t\t\tcallback(false, input.val());\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\t}\n\t\t\t\t}, {\n\t\t\t\t\ttext : t('core', 'Yes'),\n\t\t\t\t\tclick : function () {\n\t\t\t\t\t\tif (callback !== undefined) {\n\t\t\t\t\t\t\tcallback(true, input.val());\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\t},\n\t\t\t\t\tdefaultButton: true\n\t\t\t\t}\n\t\t\t];\n\n\t\t\t$(dialogId).ocdialog({\n\t\t\t\tcloseOnEscape: true,\n\t\t\t\tmodal : modal,\n\t\t\t\tbuttons : buttonlist,\n\t\t\t\tclose : function() {\n\t\t\t\t\t\/\/ callback is already fired if Yes\/No is clicked directly\n\t\t\t\t\tif (callback !== undefined) {\n\t\t\t\t\t\tcallback(false, input.val());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\tinput.focus();\n\t\t\tOCdialogs.dialogsCounter++;\n\t\t});\n\t},\n\t\/**\n\t * show a file picker to pick a file from\n\t *\n\t * In order to pick several types of mime types they need to be passed as an\n\t * array of strings.\n\t *\n\t * When no mime type filter is given only files can be selected. In order to\n\t * be able to select both files and folders \"['*', 'httpd\/unix-directory']\"\n\t * should be used instead.\n\t *\n\t * @param title dialog title\n\t * @param callback which will be triggered when user presses Choose\n\t * @param multiselect whether it should be possible to select multiple files\n\t * @param mimetypeFilter mimetype to filter by - directories will always be included\n\t * @param modal make the dialog modal\n\t * @param type Type of file picker : Choose, copy, move, copy and move\n\t*\/\n\tfilepicker:function(title, callback, multiselect, mimetypeFilter, modal, type) {\n\t\tvar self = this;\n\n\t\tthis.filepicker.sortField = 'name';\n\t\tthis.filepicker.sortOrder = 'asc';\n\t\t\/\/ avoid opening the picker twice\n\t\tif (this.filepicker.loading) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (type === undefined) {\n\t\t\ttype = this.FILEPICKER_TYPE_CHOOSE;\n\t\t}\n\n\t\tvar emptyText = t('core', 'No files in here');\n\t\tif (type === this.FILEPICKER_TYPE_COPY || type === this.FILEPICKER_TYPE_MOVE || type === this.FILEPICKER_TYPE_COPY_MOVE) {\n\t\t\temptyText = t('core', 'No more subfolders in here');\n\t\t}\n\n\t\tthis.filepicker.loading = true;\n\t\tthis.filepicker.filesClient = (OCA.Sharing && OCA.Sharing.PublicApp && OCA.Sharing.PublicApp.fileList)? OCA.Sharing.PublicApp.fileList.filesClient: OC.Files.getClient();\n\n\t\t$.when(this._getFilePickerTemplate()).then(function($tmpl) {\n\t\t\tself.filepicker.loading = false;\n\t\t\tvar dialogName = 'oc-dialog-filepicker-content';\n\t\t\tif(self.$filePicker) {\n\t\t\t\tself.$filePicker.ocdialog('close');\n\t\t\t}\n\n\t\t\tif (mimetypeFilter === undefined || mimetypeFilter === null) {\n\t\t\t\tmimetypeFilter = [];\n\t\t\t}\n\t\t\tif (typeof(mimetypeFilter) === \"string\") {\n\t\t\t\tmimetypeFilter = [mimetypeFilter];\n\t\t\t}\n\n\t\t\tself.$filePicker = $tmpl.octemplate({\n\t\t\t\tdialog_name: dialogName,\n\t\t\t\ttitle: title,\n\t\t\t\temptytext: emptyText\n\t\t\t}).data('path', '').data('multiselect', multiselect).data('mimetype', mimetypeFilter);\n\n\t\t\tif (modal === undefined) {\n\t\t\t\tmodal = false;\n\t\t\t}\n\t\t\tif (multiselect === undefined) {\n\t\t\t\tmultiselect = false;\n\t\t\t}\n\n\t\t\t\/\/ No grid for IE!\n\t\t\tif (OC.Util.isIE()) {\n\t\t\t\tself.$filePicker.find('#picker-view-toggle').remove();\n\t\t\t\tself.$filePicker.find('#picker-filestable').removeClass('view-grid');\n\t\t\t}\n\n\t\t\t$('body').append(self.$filePicker);\n\n\t\t\tself.$showGridView = $('input#picker-showgridview');\n\t\t\tself.$showGridView.on('change', _.bind(self._onGridviewChange, self));\n\n\t\t\tif (!OC.Util.isIE()) {\n\t\t\t\tself._getGridSettings();\n\t\t\t}\n\n\t\t\tself.$filePicker.ready(function() {\n\t\t\t\tself.$fileListHeader = self.$filePicker.find('.filelist thead tr');\n\t\t\t\tself.$filelist = self.$filePicker.find('.filelist tbody');\n\t\t\t\tself.$filelistContainer = self.$filePicker.find('.filelist-container');\n\t\t\t\tself.$dirTree = self.$filePicker.find('.dirtree');\n\t\t\t\tself.$dirTree.on('click', 'div:not(:last-child)', self, function (event) {\n\t\t\t\t\tself._handleTreeListSelect(event, type);\n\t\t\t\t});\n\t\t\t\tself.$filelist.on('click', 'tr', function(event) {\n\t\t\t\t\tself._handlePickerClick(event, $(this), type);\n\t\t\t\t});\n\t\t\t\tself.$fileListHeader.on('click', 'a', function(event) {\n\t\t\t\t\tvar dir = self.$filePicker.data('path');\n\t\t\t\t\tself.filepicker.sortField = $(event.currentTarget).data('sort');\n\t\t\t\t\tself.filepicker.sortOrder = self.filepicker.sortOrder === 'asc' ? 'desc' : 'asc';\n\t\t\t\t\tself._fillFilePicker(dir);\n\t\t\t\t});\n\t\t\t\tself._fillFilePicker('');\n\t\t\t});\n\n\t\t\t\/\/ build buttons\n\t\t\tvar functionToCall = function(returnType) {\n\t\t\t\tif (callback !== undefined) {\n\t\t\t\t\tvar datapath;\n\t\t\t\t\tif (multiselect === true) {\n\t\t\t\t\t\tdatapath = [];\n\t\t\t\t\t\tself.$filelist.find('tr.filepicker_element_selected').each(function(index, element) {\n\t\t\t\t\t\t\tdatapath.push(self.$filePicker.data('path') + '\/' + $(element).data('entryname'));\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdatapath = self.$filePicker.data('path');\n\t\t\t\t\t\tvar selectedName = self.$filelist.find('tr.filepicker_element_selected').data('entryname');\n\t\t\t\t\t\tif (selectedName) {\n\t\t\t\t\t\t\tdatapath += '\/' + selectedName;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcallback(datapath, returnType);\n\t\t\t\t\tself.$filePicker.ocdialog('close');\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tvar chooseCallback = function () {\n\t\t\t\tfunctionToCall(OCdialogs.FILEPICKER_TYPE_CHOOSE);\n\t\t\t};\n\n\t\t\tvar copyCallback = function () {\n\t\t\t\tfunctionToCall(OCdialogs.FILEPICKER_TYPE_COPY);\n\t\t\t};\n\n\t\t\tvar moveCallback = function () {\n\t\t\t\tfunctionToCall(OCdialogs.FILEPICKER_TYPE_MOVE);\n\t\t\t};\n\n\t\t\tvar buttonlist = [];\n\t\t\tif (type === OCdialogs.FILEPICKER_TYPE_CHOOSE) {\n\t\t\t\tbuttonlist.push({\n\t\t\t\t\ttext: t('core', 'Choose'),\n\t\t\t\t\tclick: chooseCallback,\n\t\t\t\t\tdefaultButton: true\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tif (type === OCdialogs.FILEPICKER_TYPE_COPY || type === OCdialogs.FILEPICKER_TYPE_COPY_MOVE) {\n\t\t\t\t\tbuttonlist.push({\n\t\t\t\t\t\ttext: t('core', 'Copy'),\n\t\t\t\t\t\tclick: copyCallback,\n\t\t\t\t\t\tdefaultButton: false\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (type === OCdialogs.FILEPICKER_TYPE_MOVE || type === OCdialogs.FILEPICKER_TYPE_COPY_MOVE) {\n\t\t\t\t\tbuttonlist.push({\n\t\t\t\t\t\ttext: t('core', 'Move'),\n\t\t\t\t\t\tclick: moveCallback,\n\t\t\t\t\t\tdefaultButton: true\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tself.$filePicker.ocdialog({\n\t\t\t\tcloseOnEscape: true,\n\t\t\t\t\/\/ max-width of 600\n\t\t\t\twidth: 600,\n\t\t\t\theight: 500,\n\t\t\t\tmodal: modal,\n\t\t\t\tbuttons: buttonlist,\n\t\t\t\tstyle: {\n\t\t\t\t\tbuttons: 'aside',\n\t\t\t\t},\n\t\t\t\tclose: function() {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$(this).ocdialog('destroy').remove();\n\t\t\t\t\t} catch(e) {}\n\t\t\t\t\tself.$filePicker = null;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t\/\/ We can access primary class only from oc-dialog.\n\t\t\t\/\/ Hence this is one of the approach to get the choose button.\n\t\t\tvar getOcDialog = self.$filePicker.closest('.oc-dialog');\n\t\t\tvar buttonEnableDisable = getOcDialog.find('.primary');\n\t\t\tif (self.$filePicker.data('mimetype').indexOf(\"httpd\/unix-directory\") !== -1) {\n\t\t\t\tbuttonEnableDisable.prop(\"disabled\", false);\n\t\t\t} else {\n\t\t\t\tbuttonEnableDisable.prop(\"disabled\", true);\n\t\t\t}\n\t\t})\n\t\t.fail(function(status, error) {\n\t\t\t\/\/ If the method is called while navigating away\n\t\t\t\/\/ from the page, it is probably not needed ;)\n\t\t\tself.filepicker.loading = false;\n\t\t\tif(status !== 0) {\n\t\t\t\talert(t('core', 'Error loading file picker template: {error}', {error: error}));\n\t\t\t}\n\t\t});\n\t},\n\t\/**\n\t * Displays raw dialog\n\t * You better use a wrapper instead ...\n\t*\/\n\tmessage:function(content, title, dialogType, buttons, callback, modal, allowHtml) {\n\t\treturn $.when(this._getMessageTemplate()).then(function($tmpl) {\n\t\t\tvar dialogName = 'oc-dialog-' + OCdialogs.dialogsCounter + '-content';\n\t\t\tvar dialogId = '#' + dialogName;\n\t\t\tvar $dlg = $tmpl.octemplate({\n\t\t\t\tdialog_name: dialogName,\n\t\t\t\ttitle: title,\n\t\t\t\tmessage: content,\n\t\t\t\ttype: dialogType\n\t\t\t}, allowHtml ? {escapeFunction: ''} : {});\n\t\t\tif (modal === undefined) {\n\t\t\t\tmodal = false;\n\t\t\t}\n\t\t\t$('body').append($dlg);\n\t\t\tvar buttonlist = [];\n\t\t\tswitch (buttons) {\n\t\t\tcase OCdialogs.YES_NO_BUTTONS:\n\t\t\t\tbuttonlist = [{\n\t\t\t\t\ttext: t('core', 'No'),\n\t\t\t\t\tclick: function(){\n\t\t\t\t\t\tif (callback !== undefined) {\n\t\t\t\t\t\t\tcallback(false);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\ttext: t('core', 'Yes'),\n\t\t\t\t\tclick: function(){\n\t\t\t\t\t\tif (callback !== undefined) {\n\t\t\t\t\t\t\tcallback(true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\t},\n\t\t\t\t\tdefaultButton: true\n\t\t\t\t}];\n\t\t\t\tbreak;\n\t\t\tcase OCdialogs.OK_BUTTON:\n\t\t\t\tvar functionToCall = function() {\n\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\tif(callback !== undefined) {\n\t\t\t\t\t\tcallback();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tbuttonlist[0] = {\n\t\t\t\t\ttext: t('core', 'OK'),\n\t\t\t\t\tclick: functionToCall,\n\t\t\t\t\tdefaultButton: true\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t$(dialogId).ocdialog({\n\t\t\t\tcloseOnEscape: true,\n\t\t\t\tmodal: modal,\n\t\t\t\tbuttons: buttonlist\n\t\t\t});\n\t\t\tOCdialogs.dialogsCounter++;\n\t\t})\n\t\t.fail(function(status, error) {\n\t\t\t\/\/ If the method is called while navigating away from\n\t\t\t\/\/ the page, we still want to deliver the message.\n\t\t\tif(status === 0) {\n\t\t\t\talert(title + ': ' + content);\n\t\t\t} else {\n\t\t\t\talert(t('core', 'Error loading message template: {error}', {error: error}));\n\t\t\t}\n\t\t});\n\t},\n\t_fileexistsshown: false,\n\t\/**\n\t * Displays file exists dialog\n\t * @param {object} data upload object\n\t * @param {object} original file with name, size and mtime\n\t * @param {object} replacement file with name, size and mtime\n\t * @param {object} controller with onCancel, onSkip, onReplace and onRename methods\n\t * @return {Promise} jquery promise that resolves after the dialog template was loaded\n\t*\/\n\tfileexists:function(data, original, replacement, controller) {\n\t\tvar self = this;\n\t\tvar dialogDeferred = new $.Deferred();\n\n\t\tvar getCroppedPreview = function(file) {\n\t\t\tvar deferred = new $.Deferred();\n\t\t\t\/\/ Only process image files.\n\t\t\tvar type = file.type && file.type.split('\/').shift();\n\t\t\tif (window.FileReader && type === 'image') {\n\t\t\t\tvar reader = new FileReader();\n\t\t\t\treader.onload = function (e) {\n\t\t\t\t\tvar blob = new Blob([e.target.result]);\n\t\t\t\t\twindow.URL = window.URL || window.webkitURL;\n\t\t\t\t\tvar originalUrl = window.URL.createObjectURL(blob);\n\t\t\t\t\tvar image = new Image();\n\t\t\t\t\timage.src = originalUrl;\n\t\t\t\t\timage.onload = function () {\n\t\t\t\t\t\tvar url = crop(image);\n\t\t\t\t\t\tdeferred.resolve(url);\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t\treader.readAsArrayBuffer(file);\n\t\t\t} else {\n\t\t\t\tdeferred.reject();\n\t\t\t}\n\t\t\treturn deferred;\n\t\t};\n\n\t\tvar crop = function(img) {\n\t\t\tvar canvas = document.createElement('canvas'),\n\t\t\t\t\ttargetSize = 96,\n\t\t\t\t\twidth = img.width,\n\t\t\t\t\theight = img.height,\n\t\t\t\t\tx, y, size;\n\n\t\t\t\/\/ Calculate the width and height, constraining the proportions\n\t\t\tif (width > height) {\n\t\t\t\ty = 0;\n\t\t\t\tx = (width - height) \/ 2;\n\t\t\t} else {\n\t\t\t\ty = (height - width) \/ 2;\n\t\t\t\tx = 0;\n\t\t\t}\n\t\t\tsize = Math.min(width, height);\n\n\t\t\t\/\/ Set canvas size to the cropped area\n\t\t\tcanvas.width = size;\n\t\t\tcanvas.height = size;\n\t\t\tvar ctx = canvas.getContext(\"2d\");\n\t\t\tctx.drawImage(img, x, y, size, size, 0, 0, size, size);\n\n\t\t\t\/\/ Resize the canvas to match the destination (right size uses 96px)\n\t\t\tresampleHermite(canvas, size, size, targetSize, targetSize);\n\n\t\t\treturn canvas.toDataURL(\"image\/png\", 0.7);\n\t\t};\n\n\t\t\/**\n\t\t * Fast image resize\/resample using Hermite filter with JavaScript.\n\t\t *\n\t\t * @author: ViliusL\n\t\t *\n\t\t * @param {*} canvas\n\t\t * @param {number} W\n\t\t * @param {number} H\n\t\t * @param {number} W2\n\t\t * @param {number} H2\n\t\t *\/\n\t\tvar resampleHermite = function (canvas, W, H, W2, H2) {\n\t\t\tW2 = Math.round(W2);\n\t\t\tH2 = Math.round(H2);\n\t\t\tvar img = canvas.getContext(\"2d\").getImageData(0, 0, W, H);\n\t\t\tvar img2 = canvas.getContext(\"2d\").getImageData(0, 0, W2, H2);\n\t\t\tvar data = img.data;\n\t\t\tvar data2 = img2.data;\n\t\t\tvar ratio_w = W \/ W2;\n\t\t\tvar ratio_h = H \/ H2;\n\t\t\tvar ratio_w_half = Math.ceil(ratio_w \/ 2);\n\t\t\tvar ratio_h_half = Math.ceil(ratio_h \/ 2);\n\n\t\t\tfor (var j = 0; j < H2; j++) {\n\t\t\t\tfor (var i = 0; i < W2; i++) {\n\t\t\t\t\tvar x2 = (i + j * W2) * 4;\n\t\t\t\t\tvar weight = 0;\n\t\t\t\t\tvar weights = 0;\n\t\t\t\t\tvar weights_alpha = 0;\n\t\t\t\t\tvar gx_r = 0;\n\t\t\t\t\tvar gx_g = 0;\n\t\t\t\t\tvar gx_b = 0;\n\t\t\t\t\tvar gx_a = 0;\n\t\t\t\t\tvar center_y = (j + 0.5) * ratio_h;\n\t\t\t\t\tfor (var yy = Math.floor(j * ratio_h); yy < (j + 1) * ratio_h; yy++) {\n\t\t\t\t\t\tvar dy = Math.abs(center_y - (yy + 0.5)) \/ ratio_h_half;\n\t\t\t\t\t\tvar center_x = (i + 0.5) * ratio_w;\n\t\t\t\t\t\tvar w0 = dy * dy; \/\/pre-calc part of w\n\t\t\t\t\t\tfor (var xx = Math.floor(i * ratio_w); xx < (i + 1) * ratio_w; xx++) {\n\t\t\t\t\t\t\tvar dx = Math.abs(center_x - (xx + 0.5)) \/ ratio_w_half;\n\t\t\t\t\t\t\tvar w = Math.sqrt(w0 + dx * dx);\n\t\t\t\t\t\t\tif (w >= -1 && w <= 1) {\n\t\t\t\t\t\t\t\t\/\/hermite filter\n\t\t\t\t\t\t\t\tweight = 2 * w * w * w - 3 * w * w + 1;\n\t\t\t\t\t\t\t\tif (weight > 0) {\n\t\t\t\t\t\t\t\t\tdx = 4 * (xx + yy * W);\n\t\t\t\t\t\t\t\t\t\/\/alpha\n\t\t\t\t\t\t\t\t\tgx_a += weight * data[dx + 3];\n\t\t\t\t\t\t\t\t\tweights_alpha += weight;\n\t\t\t\t\t\t\t\t\t\/\/colors\n\t\t\t\t\t\t\t\t\tif (data[dx + 3] < 255)\n\t\t\t\t\t\t\t\t\t\tweight = weight * data[dx + 3] \/ 250;\n\t\t\t\t\t\t\t\t\tgx_r += weight * data[dx];\n\t\t\t\t\t\t\t\t\tgx_g += weight * data[dx + 1];\n\t\t\t\t\t\t\t\t\tgx_b += weight * data[dx + 2];\n\t\t\t\t\t\t\t\t\tweights += weight;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdata2[x2] = gx_r \/ weights;\n\t\t\t\t\tdata2[x2 + 1] = gx_g \/ weights;\n\t\t\t\t\tdata2[x2 + 2] = gx_b \/ weights;\n\t\t\t\t\tdata2[x2 + 3] = gx_a \/ weights_alpha;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcanvas.getContext(\"2d\").clearRect(0, 0, Math.max(W, W2), Math.max(H, H2));\n\t\t\tcanvas.width = W2;\n\t\t\tcanvas.height = H2;\n\t\t\tcanvas.getContext(\"2d\").putImageData(img2, 0, 0);\n\t\t};\n\n\t\tvar addConflict = function($conflicts, original, replacement) {\n\n\t\t\tvar $conflict = $conflicts.find('.template').clone().removeClass('template').addClass('conflict');\n\t\t\tvar $originalDiv = $conflict.find('.original');\n\t\t\tvar $replacementDiv = $conflict.find('.replacement');\n\n\t\t\t$conflict.data('data',data);\n\n\t\t\t$conflict.find('.filename').text(original.name);\n\t\t\t$originalDiv.find('.size').text(humanFileSize(original.size));\n\t\t\t$originalDiv.find('.mtime').text(formatDate(original.mtime));\n\t\t\t\/\/ ie sucks\n\t\t\tif (replacement.size && replacement.lastModifiedDate) {\n\t\t\t\t$replacementDiv.find('.size').text(humanFileSize(replacement.size));\n\t\t\t\t$replacementDiv.find('.mtime').text(formatDate(replacement.lastModifiedDate));\n\t\t\t}\n\t\t\tvar path = original.directory + '\/' +original.name;\n\t\t\tvar urlSpec = {\n\t\t\t\tfile:\t\tpath,\n\t\t\t\tx:\t\t96,\n\t\t\t\ty:\t\t96,\n\t\t\t\tc:\t\toriginal.etag,\n\t\t\t\tforceIcon:\t0\n\t\t\t};\n\t\t\tvar previewpath = Files.generatePreviewUrl(urlSpec);\n\t\t\t\/\/ Escaping single quotes\n\t\t\tpreviewpath = previewpath.replace(\/'\/g, \"%27\");\n\t\t\t$originalDiv.find('.icon').css({\"background-image\": \"url('\" + previewpath + \"')\"});\n\t\t\tgetCroppedPreview(replacement).then(\n\t\t\t\tfunction(path){\n\t\t\t\t\t$replacementDiv.find('.icon').css('background-image','url(' + path + ')');\n\t\t\t\t}, function(){\n\t\t\t\t\tpath = OC.MimeType.getIconUrl(replacement.type);\n\t\t\t\t\t$replacementDiv.find('.icon').css('background-image','url(' + path + ')');\n\t\t\t\t}\n\t\t\t);\n\t\t\t\/\/ connect checkboxes with labels\n\t\t\tvar checkboxId = $conflicts.find('.conflict').length;\n\t\t\t$originalDiv.find('input:checkbox').attr('id', 'checkbox_original_'+checkboxId);\n\t\t\t$replacementDiv.find('input:checkbox').attr('id', 'checkbox_replacement_'+checkboxId);\n\n\t\t\t$conflicts.append($conflict);\n\n\t\t\t\/\/set more recent mtime bold\n\t\t\t\/\/ ie sucks\n\t\t\tif (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() > original.mtime) {\n\t\t\t\t$replacementDiv.find('.mtime').css('font-weight', 'bold');\n\t\t\t} else if (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() < original.mtime) {\n\t\t\t\t$originalDiv.find('.mtime').css('font-weight', 'bold');\n\t\t\t} else {\n\t\t\t\t\/\/TODO add to same mtime collection?\n\t\t\t}\n\n\t\t\t\/\/ set bigger size bold\n\t\t\tif (replacement.size && replacement.size > original.size) {\n\t\t\t\t$replacementDiv.find('.size').css('font-weight', 'bold');\n\t\t\t} else if (replacement.size && replacement.size < original.size) {\n\t\t\t\t$originalDiv.find('.size').css('font-weight', 'bold');\n\t\t\t} else {\n\t\t\t\t\/\/TODO add to same size collection?\n\t\t\t}\n\n\t\t\t\/\/TODO show skip action for files with same size and mtime in bottom row\n\n\t\t\t\/\/ always keep readonly files\n\n\t\t\tif (original.status === 'readonly') {\n\t\t\t\t$originalDiv\n\t\t\t\t\t.addClass('readonly')\n\t\t\t\t\t.find('input[type=\"checkbox\"]')\n\t\t\t\t\t\t.prop('checked', true)\n\t\t\t\t\t\t.prop('disabled', true);\n\t\t\t\t$originalDiv.find('.message')\n\t\t\t\t\t.text(t('core','read-only'));\n\t\t\t}\n\t\t};\n\t\t\/\/var selection = controller.getSelection(data.originalFiles);\n\t\t\/\/if (selection.defaultAction) {\n\t\t\/\/\tcontroller[selection.defaultAction](data);\n\t\t\/\/} else {\n\t\tvar dialogName = 'oc-dialog-fileexists-content';\n\t\tvar dialogId = '#' + dialogName;\n\t\tif (this._fileexistsshown) {\n\t\t\t\/\/ add conflict\n\n\t\t\tvar $conflicts = $(dialogId+ ' .conflicts');\n\t\t\taddConflict($conflicts, original, replacement);\n\n\t\t\tvar count = $(dialogId+ ' .conflict').length;\n\t\t\tvar title = n('core',\n\t\t\t\t\t\t\t'{count} file conflict',\n\t\t\t\t\t\t\t'{count} file conflicts',\n\t\t\t\t\t\t\tcount,\n\t\t\t\t\t\t\t{count:count}\n\t\t\t\t\t\t);\n\t\t\t$(dialogId).parent().children('.oc-dialog-title').text(title);\n\n\t\t\t\/\/recalculate dimensions\n\t\t\t$(window).trigger('resize');\n\t\t\tdialogDeferred.resolve();\n\t\t} else {\n\t\t\t\/\/create dialog\n\t\t\tthis._fileexistsshown = true;\n\t\t\t$.when(this._getFileExistsTemplate()).then(function($tmpl) {\n\t\t\t\tvar title = t('core','One file conflict');\n\t\t\t\tvar $dlg = $tmpl.octemplate({\n\t\t\t\t\tdialog_name: dialogName,\n\t\t\t\t\ttitle: title,\n\t\t\t\t\ttype: 'fileexists',\n\n\t\t\t\t\tallnewfiles: t('core','New Files'),\n\t\t\t\t\tallexistingfiles: t('core','Already existing files'),\n\n\t\t\t\t\twhy: t('core','Which files do you want to keep?'),\n\t\t\t\t\twhat: t('core','If you select both versions, the copied file will have a number added to its name.')\n\t\t\t\t});\n\t\t\t\t$('body').append($dlg);\n\n\t\t\t\tif (original && replacement) {\n\t\t\t\t\tvar $conflicts = $dlg.find('.conflicts');\n\t\t\t\t\taddConflict($conflicts, original, replacement);\n\t\t\t\t}\n\n\t\t\t\tvar buttonlist = [{\n\t\t\t\t\t\ttext: t('core', 'Cancel'),\n\t\t\t\t\t\tclasses: 'cancel',\n\t\t\t\t\t\tclick: function(){\n\t\t\t\t\t\t\tif ( typeof controller.onCancel !== 'undefined') {\n\t\t\t\t\t\t\t\tcontroller.onCancel(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttext: t('core', 'Continue'),\n\t\t\t\t\t\tclasses: 'continue',\n\t\t\t\t\t\tclick: function(){\n\t\t\t\t\t\t\tif ( typeof controller.onContinue !== 'undefined') {\n\t\t\t\t\t\t\t\tcontroller.onContinue($(dialogId + ' .conflict'));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\t\t}\n\t\t\t\t\t}];\n\n\t\t\t\t$(dialogId).ocdialog({\n\t\t\t\t\twidth: 500,\n\t\t\t\t\tcloseOnEscape: true,\n\t\t\t\t\tmodal: true,\n\t\t\t\t\tbuttons: buttonlist,\n\t\t\t\t\tcloseButton: null,\n\t\t\t\t\tclose: function() {\n\t\t\t\t\t\t\tself._fileexistsshown = false;\n\t\t\t\t\t\t\t$(this).ocdialog('destroy').remove();\n\t\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t$(dialogId).css('height','auto');\n\n\t\t\t\tvar $primaryButton = $dlg.closest('.oc-dialog').find('button.continue');\n\t\t\t\t$primaryButton.prop('disabled', true);\n\n\t\t\t\tfunction updatePrimaryButton() {\n\t\t\t\t\tvar checkedCount = $dlg.find('.conflicts .checkbox:checked').length;\n\t\t\t\t\t$primaryButton.prop('disabled', checkedCount === 0);\n\t\t\t\t}\n\n\t\t\t\t\/\/add checkbox toggling actions\n\t\t\t\t$(dialogId).find('.allnewfiles').on('click', function() {\n\t\t\t\t\tvar $checkboxes = $(dialogId).find('.conflict .replacement input[type=\"checkbox\"]');\n\t\t\t\t\t$checkboxes.prop('checked', $(this).prop('checked'));\n\t\t\t\t});\n\t\t\t\t$(dialogId).find('.allexistingfiles').on('click', function() {\n\t\t\t\t\tvar $checkboxes = $(dialogId).find('.conflict .original:not(.readonly) input[type=\"checkbox\"]');\n\t\t\t\t\t$checkboxes.prop('checked', $(this).prop('checked'));\n\t\t\t\t});\n\t\t\t\t$(dialogId).find('.conflicts').on('click', '.replacement,.original:not(.readonly)', function() {\n\t\t\t\t\tvar $checkbox = $(this).find('input[type=\"checkbox\"]');\n\t\t\t\t\t$checkbox.prop('checked', !$checkbox.prop('checked'));\n\t\t\t\t});\n\t\t\t\t$(dialogId).find('.conflicts').on('click', '.replacement input[type=\"checkbox\"],.original:not(.readonly) input[type=\"checkbox\"]', function() {\n\t\t\t\t\tvar $checkbox = $(this);\n\t\t\t\t\t$checkbox.prop('checked', !$checkbox.prop('checked'));\n\t\t\t\t});\n\n\t\t\t\t\/\/update counters\n\t\t\t\t$(dialogId).on('click', '.replacement,.allnewfiles', function() {\n\t\t\t\t\tvar count = $(dialogId).find('.conflict .replacement input[type=\"checkbox\"]:checked').length;\n\t\t\t\t\tif (count === $(dialogId+ ' .conflict').length) {\n\t\t\t\t\t\t$(dialogId).find('.allnewfiles').prop('checked', true);\n\t\t\t\t\t\t$(dialogId).find('.allnewfiles + .count').text(t('core','(all selected)'));\n\t\t\t\t\t} else if (count > 0) {\n\t\t\t\t\t\t$(dialogId).find('.allnewfiles').prop('checked', false);\n\t\t\t\t\t\t$(dialogId).find('.allnewfiles + .count').text(t('core','({count} selected)',{count:count}));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$(dialogId).find('.allnewfiles').prop('checked', false);\n\t\t\t\t\t\t$(dialogId).find('.allnewfiles + .count').text('');\n\t\t\t\t\t}\n\t\t\t\t\tupdatePrimaryButton();\n\t\t\t\t});\n\t\t\t\t$(dialogId).on('click', '.original,.allexistingfiles', function(){\n\t\t\t\t\tvar count = $(dialogId).find('.conflict .original input[type=\"checkbox\"]:checked').length;\n\t\t\t\t\tif (count === $(dialogId+ ' .conflict').length) {\n\t\t\t\t\t\t$(dialogId).find('.allexistingfiles').prop('checked', true);\n\t\t\t\t\t\t$(dialogId).find('.allexistingfiles + .count').text(t('core','(all selected)'));\n\t\t\t\t\t} else if (count > 0) {\n\t\t\t\t\t\t$(dialogId).find('.allexistingfiles').prop('checked', false);\n\t\t\t\t\t\t$(dialogId).find('.allexistingfiles + .count')\n\t\t\t\t\t\t\t.text(t('core','({count} selected)',{count:count}));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$(dialogId).find('.allexistingfiles').prop('checked', false);\n\t\t\t\t\t\t$(dialogId).find('.allexistingfiles + .count').text('');\n\t\t\t\t\t}\n\t\t\t\t\tupdatePrimaryButton();\n\t\t\t\t});\n\n\t\t\t\tdialogDeferred.resolve();\n\t\t\t})\n\t\t\t.fail(function() {\n\t\t\t\tdialogDeferred.reject();\n\t\t\t\talert(t('core', 'Error loading file exists template'));\n\t\t\t});\n\t\t}\n\t\t\/\/}\n\t\treturn dialogDeferred.promise();\n\t},\n\t\/\/ get the gridview setting and set the input accordingly\n\t_getGridSettings: function() {\n\t\tvar self = this;\n\t\t$.get(OC.generateUrl('\/apps\/files\/api\/v1\/showgridview'), function(response) {\n\t\t\tself.$showGridView.get(0).checked = response.gridview;\n\t\t\tself.$showGridView.next('#picker-view-toggle')\n\t\t\t\t.removeClass('icon-toggle-filelist icon-toggle-pictures')\n\t\t\t\t.addClass(response.gridview ? 'icon-toggle-filelist' : 'icon-toggle-pictures')\n\t\t\t$('.list-container').toggleClass('view-grid', response.gridview);\n\t\t});\n\t},\n\t_onGridviewChange: function() {\n\t\tvar show = this.$showGridView.is(':checked');\n\t\t\/\/ only save state if user is logged in\n\t\tif (OC.currentUser) {\n\t\t\t$.post(OC.generateUrl('\/apps\/files\/api\/v1\/showgridview'), {\n\t\t\t\tshow: show\n\t\t\t});\n\t\t}\n\t\tthis.$showGridView.next('#picker-view-toggle')\n\t\t\t.removeClass('icon-toggle-filelist icon-toggle-pictures')\n\t\t\t.addClass(show ? 'icon-toggle-filelist' : 'icon-toggle-pictures')\n\t\t$('.list-container').toggleClass('view-grid', show);\n\t},\n\t_getFilePickerTemplate: function() {\n\t\tvar defer = $.Deferred();\n\t\tif(!this.$filePickerTemplate) {\n\t\t\tvar self = this;\n\t\t\t$.get(OC.filePath('core', 'templates', 'filepicker.html'), function(tmpl) {\n\t\t\t\tself.$filePickerTemplate = $(tmpl);\n\t\t\t\tself.$listTmpl = self.$filePickerTemplate.find('.filelist tbody tr:first-child').detach();\n\t\t\t\tdefer.resolve(self.$filePickerTemplate);\n\t\t\t})\n\t\t\t.fail(function(jqXHR, textStatus, errorThrown) {\n\t\t\t\tdefer.reject(jqXHR.status, errorThrown);\n\t\t\t});\n\t\t} else {\n\t\t\tdefer.resolve(this.$filePickerTemplate);\n\t\t}\n\t\treturn defer.promise();\n\t},\n\t_getMessageTemplate: function() {\n\t\tvar defer = $.Deferred();\n\t\tif(!this.$messageTemplate) {\n\t\t\tvar self = this;\n\t\t\t$.get(OC.filePath('core', 'templates', 'message.html'), function(tmpl) {\n\t\t\t\tself.$messageTemplate = $(tmpl);\n\t\t\t\tdefer.resolve(self.$messageTemplate);\n\t\t\t})\n\t\t\t.fail(function(jqXHR, textStatus, errorThrown) {\n\t\t\t\tdefer.reject(jqXHR.status, errorThrown);\n\t\t\t});\n\t\t} else {\n\t\t\tdefer.resolve(this.$messageTemplate);\n\t\t}\n\t\treturn defer.promise();\n\t},\n\t_getFileExistsTemplate: function () {\n\t\tvar defer = $.Deferred();\n\t\tif (!this.$fileexistsTemplate) {\n\t\t\tvar self = this;\n\t\t\t$.get(OC.filePath('files', 'templates', 'fileexists.html'), function (tmpl) {\n\t\t\t\tself.$fileexistsTemplate = $(tmpl);\n\t\t\t\tdefer.resolve(self.$fileexistsTemplate);\n\t\t\t})\n\t\t\t.fail(function () {\n\t\t\t\tdefer.reject();\n\t\t\t});\n\t\t} else {\n\t\t\tdefer.resolve(this.$fileexistsTemplate);\n\t\t}\n\t\treturn defer.promise();\n\t},\n\t_getFileList: function(dir, mimeType) { \/\/this is only used by the spreedme app atm\n\t\tif (typeof(mimeType) === \"string\") {\n\t\t\tmimeType = [mimeType];\n\t\t}\n\n\t\treturn $.getJSON(\n\t\t\tOC.filePath('files', 'ajax', 'list.php'),\n\t\t\t{\n\t\t\t\tdir: dir,\n\t\t\t\tmimetypes: JSON.stringify(mimeType)\n\t\t\t}\n\t\t);\n\t},\n\n\t\/**\n\t * fills the filepicker with files\n\t*\/\n\t_fillFilePicker:function(dir) {\n\t\tvar self = this;\n\t\tthis.$filelist.empty();\n\t\tthis.$filePicker.find('.emptycontent').hide();\n\t\tthis.$filelistContainer.addClass('icon-loading');\n\t\tthis.$filePicker.data('path', dir);\n\t\tvar filter = this.$filePicker.data('mimetype');\n\t\tif (typeof(filter) === \"string\") {\n\t\t\tfilter = [filter];\n\t\t}\n\t\tself.$fileListHeader.find('.sort-indicator').addClass('hidden').removeClass('icon-triangle-n').removeClass('icon-triangle-s');\n\t\tself.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').removeClass('hidden');\n\t\tif (self.filepicker.sortOrder === 'asc') {\n\t\t\tself.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-n');\n\t\t} else {\n\t\t\tself.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-s');\n\t\t}\n\t\tself.filepicker.filesClient.getFolderContents(dir).then(function(status, files) {\n\t\t\tif (filter && filter.length > 0 && filter.indexOf('*') === -1) {\n\t\t\t\tfiles = files.filter(function (file) {\n\t\t\t\t\treturn file.type === 'dir' || filter.indexOf(file.mimetype) !== -1;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tvar Comparators = {\n\t\t\t\tname: function(fileInfo1, fileInfo2) {\n\t\t\t\t\tif (fileInfo1.type === 'dir' && fileInfo2.type !== 'dir') {\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t\tif (fileInfo1.type !== 'dir' && fileInfo2.type === 'dir') {\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\treturn OC.Util.naturalSortCompare(fileInfo1.name, fileInfo2.name);\n\t\t\t\t},\n\t\t\t\tsize: function(fileInfo1, fileInfo2) {\n\t\t\t\t\treturn fileInfo1.size - fileInfo2.size;\n\t\t\t\t},\n\t\t\t\tmtime: function(fileInfo1, fileInfo2) {\n\t\t\t\t\treturn fileInfo1.mtime - fileInfo2.mtime;\n\t\t\t\t}\n\t\t\t};\n\t\t\tvar comparator = Comparators[self.filepicker.sortField] || Comparators.name;\n\t\t\tfiles = files.sort(function(file1, file2) {\n\t\t\t\tvar isFavorite = function(fileInfo) {\n\t\t\t\t\treturn fileInfo.tags && fileInfo.tags.indexOf(OC.TAG_FAVORITE) >= 0;\n\t\t\t\t};\n\n\t\t\t\tif (isFavorite(file1) && !isFavorite(file2)) {\n\t\t\t\t\treturn -1;\n\t\t\t\t} else if (!isFavorite(file1) && isFavorite(file2)) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\n\t\t\t\treturn self.filepicker.sortOrder === 'asc' ? comparator(file1, file2) : -comparator(file1, file2);\n\t\t\t});\n\n\t\t\tself._fillSlug();\n\n\t\t\tif (files.length === 0) {\n\t\t\t\tself.$filePicker.find('.emptycontent').show();\n\t\t\t\tself.$fileListHeader.hide();\n\t\t\t} else {\n\t\t\t\tself.$filePicker.find('.emptycontent').hide();\n\t\t\t\tself.$fileListHeader.show();\n\t\t\t}\n\n\t\t\t$.each(files, function(idx, entry) {\n\t\t\t\tentry.icon = OC.MimeType.getIconUrl(entry.mimetype);\n\t\t\t\tvar simpleSize, sizeColor;\n\t\t\t\tif (typeof(entry.size) !== 'undefined' && entry.size >= 0) {\n\t\t\t\t\tsimpleSize = humanFileSize(parseInt(entry.size, 10), true);\n\t\t\t\t\tsizeColor = Math.round(160 - Math.pow((entry.size \/ (1024 * 1024)), 2));\n\t\t\t\t} else {\n\t\t\t\t\tsimpleSize = t('files', 'Pending');\n\t\t\t\t\tsizeColor = 80;\n\t\t\t\t}\n\t\t\t\tvar $row = self.$listTmpl.octemplate({\n\t\t\t\t\ttype: entry.type,\n\t\t\t\t\tdir: dir,\n\t\t\t\t\tfilename: entry.name,\n\t\t\t\t\tdate: OC.Util.relativeModifiedDate(entry.mtime),\n\t\t\t\t\tsize: simpleSize,\n\t\t\t\t\tsizeColor: sizeColor,\n\t\t\t\t\ticon: entry.icon\n\t\t\t\t});\n\t\t\t\tif (entry.type === 'file') {\n\t\t\t\t\tvar urlSpec = {\n\t\t\t\t\t\tfile: dir + '\/' + entry.name,\n\t\t\t\t\t\tx: 100,\n\t\t\t\t\t\ty: 100\n\t\t\t\t\t};\n\t\t\t\t\tvar img = new Image();\n\t\t\t\t\tvar previewUrl = OC.generateUrl('\/core\/preview.png?') + $.param(urlSpec);\n\t\t\t\t\timg.onload = function() {\n\t\t\t\t\t\tif (img.width > 5) {\n\t\t\t\t\t\t\t$row.find('td.filename').attr('style', 'background-image:url(' + previewUrl + ')');\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\timg.src = previewUrl;\n\t\t\t\t}\n\t\t\t\tself.$filelist.append($row);\n\t\t\t});\n\n\t\t\tself.$filelistContainer.removeClass('icon-loading');\n\t\t});\n\t},\n\t\/**\n\t * fills the tree list with directories\n\t*\/\n\t_fillSlug: function() {\n\t\tthis.$dirTree.empty();\n\t\tvar self = this;\n\t\tvar dir;\n\t\tvar path = this.$filePicker.data('path');\n\t\tvar $template = $('<div data-dir=\"{dir}\"><a>{name}<\/a><\/div>').addClass('crumb');\n\t\tif(path) {\n\t\t\tvar paths = path.split('\/');\n\t\t\t$.each(paths, function(index, dir) {\n\t\t\t\tdir = paths.pop();\n\t\t\t\tif(dir === '') {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tself.$dirTree.prepend($template.octemplate({\n\t\t\t\t\tdir: paths.join('\/') + '\/' + dir,\n\t\t\t\t\tname: dir\n\t\t\t\t}));\n\t\t\t});\n\t\t}\n\t\t$template.octemplate({\n\t\t\tdir: '',\n\t\t\tname: '' \/\/ Ugly but works ;)\n\t\t}, {escapeFunction: null}).prependTo(this.$dirTree);\n\t},\n\t\/**\n\t * handle selection made in the tree list\n\t*\/\n\t_handleTreeListSelect:function(event, type) {\n\t\tvar self = event.data;\n\t\tvar dir = $(event.target).closest('.crumb').data('dir');\n\t\tself._fillFilePicker(dir);\n\t\tvar getOcDialog = (event.target).closest('.oc-dialog');\n\t\tvar buttonEnableDisable = $('.primary', getOcDialog);\n\t\tthis._changeButtonsText(type, dir.split(\/[\/]+\/).pop());\n\t\tif (this.$filePicker.data('mimetype').indexOf(\"httpd\/unix-directory\") !== -1) {\n\t\t\tbuttonEnableDisable.prop(\"disabled\", false);\n\t\t} else {\n\t\t\tbuttonEnableDisable.prop(\"disabled\", true);\n\t\t}\n\t},\n\t\/**\n\t * handle clicks made in the filepicker\n\t*\/\n\t_handlePickerClick:function(event, $element, type) {\n\t\tvar getOcDialog = this.$filePicker.closest('.oc-dialog');\n\t\tvar buttonEnableDisable = getOcDialog.find('.primary');\n\t\tif ($element.data('type') === 'file') {\n\t\t\tif (this.$filePicker.data('multiselect') !== true || !event.ctrlKey) {\n\t\t\t\tthis.$filelist.find('.filepicker_element_selected').removeClass('filepicker_element_selected');\n\t\t\t}\n\t\t\t$element.toggleClass('filepicker_element_selected');\n\t\t\tbuttonEnableDisable.prop(\"disabled\", false);\n\t\t} else if ( $element.data('type') === 'dir' ) {\n\t\t\tthis._fillFilePicker(this.$filePicker.data('path') + '\/' + $element.data('entryname'));\n\t\t\tthis._changeButtonsText(type, $element.data('entryname'));\n\t\t\tif (this.$filePicker.data('mimetype').indexOf(\"httpd\/unix-directory\") !== -1) {\n\t\t\t\tbuttonEnableDisable.prop(\"disabled\", false);\n\t\t\t} else {\n\t\t\t\tbuttonEnableDisable.prop(\"disabled\", true);\n\t\t\t}\n\t\t}\n\t},\n\n\t\/**\n\t * Handle\n\t * @param type of action\n\t * @param dir on which to change buttons text\n\t * @private\n\t *\/\n\t_changeButtonsText: function(type, dir) {\n\t\tvar copyText = dir === '' ? t('core', 'Copy') : t('core', 'Copy to {folder}', {folder: dir});\n\t\tvar moveText = dir === '' ? t('core', 'Move') : t('core', 'Move to {folder}', {folder: dir});\n\t\tvar buttons = $('.oc-dialog-buttonrow button');\n\t\tswitch (type) {\n\t\t\tcase this.FILEPICKER_TYPE_CHOOSE:\n\t\t\t\tbreak;\n\t\t\tcase this.FILEPICKER_TYPE_COPY:\n\t\t\t\tbuttons.text(copyText);\n\t\t\t\tbreak;\n\t\t\tcase this.FILEPICKER_TYPE_MOVE:\n\t\t\t\tbuttons.text(moveText);\n\t\t\t\tbreak;\n\t\t\tcase this.FILEPICKER_TYPE_COPY_MOVE:\n\t\t\t\tbuttons.eq(0).text(copyText);\n\t\t\t\tbuttons.eq(1).text(moveText);\n\t\t\t\tbreak;\n\t\t}\n\t}\n};\n\n\n\/* global oc_isadmin *\/\n\nvar oc_debug;\nvar oc_webroot;\n\nvar oc_current_user = document.getElementsByTagName('head')[0].getAttribute('data-user');\nvar oc_requesttoken = document.getElementsByTagName('head')[0].getAttribute('data-requesttoken');\n\nwindow.oc_config = window.oc_config || {};\n\nif (typeof oc_webroot === \"undefined\") {\n\toc_webroot = location.pathname;\n\tvar pos = oc_webroot.indexOf('\/index.php\/');\n\tif (pos !== -1) {\n\t\toc_webroot = oc_webroot.substr(0, pos);\n\t}\n\telse {\n\t\toc_webroot = oc_webroot.substr(0, oc_webroot.lastIndexOf('\/'));\n\t}\n}\nif (typeof console === \"undefined\" || typeof console.log === \"undefined\") {\n\tif (!window.console) {\n\t\twindow.console = {};\n\t}\n\tvar noOp = function() { };\n\tvar methods = ['log', 'debug', 'warn', 'info', 'error', 'assert', 'time', 'timeEnd'];\n\tfor (var i = 0; i < methods.length; i++) {\n\t\tconsole[methods[i]] = noOp;\n\t}\n}\n\n\/**\n* Sanitizes a HTML string by replacing all potential dangerous characters with HTML entities\n* @param {string} s String to sanitize\n* @return {string} Sanitized string\n*\/\nfunction escapeHTML(s) {\n\treturn s.toString().split('&').join('&amp;').split('<').join('&lt;').split('>').join('&gt;').split('\"').join('&quot;').split('\\'').join('&#039;');\n}\n\n\/** @namespace OCP *\/\nvar OCP = {},\n\t\/**\n\t * @namespace OC\n\t *\/\n\tOC = {\n\tPERMISSION_NONE:0,\n\tPERMISSION_CREATE:4,\n\tPERMISSION_READ:1,\n\tPERMISSION_UPDATE:2,\n\tPERMISSION_DELETE:8,\n\tPERMISSION_SHARE:16,\n\tPERMISSION_ALL:31,\n\tTAG_FAVORITE: '_$!<Favorite>!$_',\n\t\/* jshint camelcase: false *\/\n\t\/**\n\t * Relative path to Nextcloud root.\n\t * For example: \"\/nextcloud\"\n\t *\n\t * @type string\n\t *\n\t * @deprecated since 8.2, use OC.getRootPath() instead\n\t * @see OC#getRootPath\n\t *\/\n\twebroot:oc_webroot,\n\n\t\/**\n\t * Capabilities\n\t *\n\t * @type array\n\t *\/\n\t_capabilities: window.oc_capabilities || null,\n\n\tappswebroots:(typeof oc_appswebroots !== 'undefined') ? oc_appswebroots:false,\n\t\/**\n\t * Currently logged in user or null if none\n\t *\n\t * @type String\n\t * @deprecated use {@link OC.getCurrentUser} instead\n\t *\/\n\tcurrentUser:(typeof oc_current_user!=='undefined')?oc_current_user:false,\n\tconfig: window.oc_config,\n\tappConfig: window.oc_appconfig || {},\n\ttheme: window.oc_defaults || {},\n\tcoreApps:['', 'admin','log','core\/search','settings','core','3rdparty'],\n\trequestToken: oc_requesttoken,\n\tmenuSpeed: 50,\n\n\t\/**\n\t * Get an absolute url to a file in an app\n\t * @param {string} app the id of the app the file belongs to\n\t * @param {string} file the file path relative to the app folder\n\t * @return {string} Absolute URL to a file\n\t *\/\n\tlinkTo:function(app,file){\n\t\treturn OC.filePath(app,'',file);\n\t},\n\n\t\/**\n\t * Creates a relative url for remote use\n\t * @param {string} service id\n\t * @return {string} the url\n\t *\/\n\tlinkToRemoteBase:function(service) {\n\t\treturn OC.getRootPath() + '\/remote.php\/' + service;\n\t},\n\n\t\/**\n\t * @brief Creates an absolute url for remote use\n\t * @param {string} service id\n\t * @return {string} the url\n\t *\/\n\tlinkToRemote:function(service) {\n\t\treturn window.location.protocol + '\/\/' + window.location.host + OC.linkToRemoteBase(service);\n\t},\n\n\t\/**\n\t * Gets the base path for the given OCS API service.\n\t * @param {string} service name\n\t * @param {int} version OCS API version\n\t * @return {string} OCS API base path\n\t *\/\n\tlinkToOCS: function(service, version) {\n\t\tversion = (version !== 2) ? 1 : 2;\n\t\treturn window.location.protocol + '\/\/' + window.location.host + OC.getRootPath() + '\/ocs\/v' + version + '.php\/' + service + '\/';\n\t},\n\n\t\/**\n\t * Generates the absolute url for the given relative url, which can contain parameters.\n\t * Parameters will be URL encoded automatically.\n\t * @param {string} url\n\t * @param [params] params\n\t * @param [options] options\n\t * @param {bool} [options.escape=true] enable\/disable auto escape of placeholders (by default enabled)\n\t * @return {string} Absolute URL for the given relative URL\n\t *\/\n\tgenerateUrl: function(url, params, options) {\n\t\tvar defaultOptions = {\n\t\t\t\tescape: true\n\t\t\t},\n\t\t\tallOptions = options || {};\n\t\t_.defaults(allOptions, defaultOptions);\n\n\t\tvar _build = function (text, vars) {\n\t\t\tvars = vars || [];\n\t\t\treturn text.replace(\/{([^{}]*)}\/g,\n\t\t\t\tfunction (a, b) {\n\t\t\t\t\tvar r = (vars[b]);\n\t\t\t\t\tif(allOptions.escape) {\n\t\t\t\t\t\treturn (typeof r === 'string' || typeof r === 'number') ? encodeURIComponent(r) : encodeURIComponent(a);\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn (typeof r === 'string' || typeof r === 'number') ? r : a;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\t\t};\n\t\tif (url.charAt(0) !== '\/') {\n\t\t\turl = '\/' + url;\n\n\t\t}\n\n\t\tif(oc_config.modRewriteWorking == true) {\n\t\t\treturn OC.getRootPath() + _build(url, params);\n\t\t}\n\n\t\treturn OC.getRootPath() + '\/index.php' + _build(url, params);\n\t},\n\n\t\/**\n\t * Get the absolute url for a file in an app\n\t * @param {string} app the id of the app\n\t * @param {string} type the type of the file to link to (e.g. css,img,ajax.template)\n\t * @param {string} file the filename\n\t * @return {string} Absolute URL for a file in an app\n\t *\/\n\tfilePath:function(app,type,file){\n\t\tvar isCore=OC.coreApps.indexOf(app)!==-1,\n\t\t\tlink=OC.getRootPath();\n\t\tif(file.substring(file.length-3) === 'php' && !isCore){\n\t\t\tlink+='\/index.php\/apps\/' + app;\n\t\t\tif (file != 'index.php') {\n\t\t\t\tlink+='\/';\n\t\t\t\tif(type){\n\t\t\t\t\tlink+=encodeURI(type + '\/');\n\t\t\t\t}\n\t\t\t\tlink+= file;\n\t\t\t}\n\t\t}else if(file.substring(file.length-3) !== 'php' && !isCore){\n\t\t\tlink=OC.appswebroots[app];\n\t\t\tif(type){\n\t\t\t\tlink+= '\/'+type+'\/';\n\t\t\t}\n\t\t\tif(link.substring(link.length-1) !== '\/'){\n\t\t\t\tlink+='\/';\n\t\t\t}\n\t\t\tlink+=file;\n\t\t}else{\n\t\t\tif ((app == 'settings' || app == 'core' || app == 'search') && type == 'ajax') {\n\t\t\t\tlink+='\/index.php\/';\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlink+='\/';\n\t\t\t}\n\t\t\tif(!isCore){\n\t\t\t\tlink+='apps\/';\n\t\t\t}\n\t\t\tif (app !== '') {\n\t\t\t\tapp+='\/';\n\t\t\t\tlink+=app;\n\t\t\t}\n\t\t\tif(type){\n\t\t\t\tlink+=type+'\/';\n\t\t\t}\n\t\t\tlink+=file;\n\t\t}\n\t\treturn link;\n\t},\n\n\t\/**\n\t * Check if a user file is allowed to be handled.\n\t * @param {string} file to check\n\t *\/\n\tfileIsBlacklisted: function(file) {\n\t\treturn !!(file.match(oc_config.blacklist_files_regex));\n\t},\n\n\t\/**\n\t * Redirect to the target URL, can also be used for downloads.\n\t * @param {string} targetURL URL to redirect to\n\t *\/\n\tredirect: function(targetURL) {\n\t\twindow.location = targetURL;\n\t},\n\n\t\/**\n\t * Reloads the current page\n\t *\/\n\treload: function() {\n\t\twindow.location.reload();\n\t},\n\n\t\/**\n\t * Protocol that is used to access this Nextcloud instance\n\t * @return {string} Used protocol\n\t *\/\n\tgetProtocol: function() {\n\t\treturn window.location.protocol.split(':')[0];\n\t},\n\n\t\/**\n\t * Returns the host used to access this Nextcloud instance\n\t * Host is sometimes the same as the hostname but now always.\n\t *\n\t * Examples:\n\t * http:\/\/example.com => example.com\n\t * https:\/\/example.com => example.com\n\t * http:\/\/example.com:8080 => example.com:8080\n\t *\n\t * @return {string} host\n\t *\n\t * @since 8.2\n\t *\/\n\tgetHost: function() {\n\t\treturn window.location.host;\n\t},\n\n\t\/**\n\t * Returns the hostname used to access this Nextcloud instance\n\t * The hostname is always stripped of the port\n\t *\n\t * @return {string} hostname\n\t * @since 9.0\n\t *\/\n\tgetHostName: function() {\n\t\treturn window.location.hostname;\n\t},\n\n\t\/**\n\t * Returns the port number used to access this Nextcloud instance\n\t *\n\t * @return {int} port number\n\t *\n\t * @since 8.2\n\t *\/\n\tgetPort: function() {\n\t\treturn window.location.port;\n\t},\n\n\t\/**\n\t * Returns the web root path where this Nextcloud instance\n\t * is accessible, with a leading slash.\n\t * For example \"\/nextcloud\".\n\t *\n\t * @return {string} web root path\n\t *\n\t * @since 8.2\n\t *\/\n\tgetRootPath: function() {\n\t\treturn OC.webroot;\n\t},\n\n\n\t\/**\n\t * Returns the capabilities\n\t *\n\t * @return {array} capabilities\n\t *\n\t * @since 14.0\n\t *\/\n\tgetCapabilities: function() {\n\t\treturn OC._capabilities;\n\t},\n\n\t\/**\n\t * Returns the currently logged in user or null if there is no logged in\n\t * user (public page mode)\n\t *\n\t * @return {OC.CurrentUser} user spec\n\t * @since 9.0.0\n\t *\/\n\tgetCurrentUser: function() {\n\t\tif (_.isUndefined(this._currentUserDisplayName)) {\n\t\t\tthis._currentUserDisplayName = document.getElementsByTagName('head')[0].getAttribute('data-user-displayname');\n\t\t}\n\t\treturn {\n\t\t\tuid: this.currentUser,\n\t\t\tdisplayName: this._currentUserDisplayName\n\t\t};\n\t},\n\n\t\/**\n\t * get the absolute path to an image file\n\t * if no extension is given for the image, it will automatically decide\n\t * between .png and .svg based on what the browser supports\n\t * @param {string} app the app id to which the image belongs\n\t * @param {string} file the name of the image file\n\t * @return {string}\n\t *\/\n\timagePath:function(app,file){\n\t\tif(file.indexOf('.')==-1){\/\/if no extension is given, use svg\n\t\t\tfile+='.svg';\n\t\t}\n\t\treturn OC.filePath(app,'img',file);\n\t},\n\n\t\/**\n\t * URI-Encodes a file path but keep the path slashes.\n\t *\n\t * @param path path\n\t * @return encoded path\n\t *\/\n\tencodePath: function(path) {\n\t\tif (!path) {\n\t\t\treturn path;\n\t\t}\n\t\tvar parts = path.split('\/');\n\t\tvar result = [];\n\t\tfor (var i = 0; i < parts.length; i++) {\n\t\t\tresult.push(encodeURIComponent(parts[i]));\n\t\t}\n\t\treturn result.join('\/');\n\t},\n\n\t\/**\n\t * Load a script for the server and load it. If the script is already loaded,\n\t * the event handler will be called directly\n\t * @param {string} app the app id to which the script belongs\n\t * @param {string} script the filename of the script\n\t * @param ready event handler to be called when the script is loaded\n\t *\/\n\taddScript:function(app,script,ready){\n\t\tvar deferred, path=OC.filePath(app,'js',script+'.js');\n\t\tif(!OC.addScript.loaded[path]) {\n\t\t\tdeferred = $.Deferred();\n\t\t\t$.getScript(path, function() {\n\t\t\t\tdeferred.resolve();\n\t\t\t});\n\t\t\tOC.addScript.loaded[path] = deferred;\n\t\t} else {\n\t\t\tif (ready) {\n\t\t\t\tready();\n\t\t\t}\n\t\t}\n\t\treturn OC.addScript.loaded[path];\n\t},\n\t\/**\n\t * Loads a CSS file\n\t * @param {string} app the app id to which the css style belongs\n\t * @param {string} style the filename of the css file\n\t *\/\n\taddStyle:function(app,style){\n\t\tvar path=OC.filePath(app,'css',style+'.css');\n\t\tif(OC.addStyle.loaded.indexOf(path)===-1){\n\t\t\tOC.addStyle.loaded.push(path);\n\t\t\tif (document.createStyleSheet) {\n\t\t\t\tdocument.createStyleSheet(path);\n\t\t\t} else {\n\t\t\t\tstyle=$('<link rel=\"stylesheet\" type=\"text\/css\" href=\"'+path+'\"\/>');\n\t\t\t\t$('head').append(style);\n\t\t\t}\n\t\t}\n\t},\n\n\t\/**\n\t * Loads translations for the given app asynchronously.\n\t *\n\t * @param {String} app app name\n\t * @param {Function} callback callback to call after loading\n\t * @return {Promise}\n\t *\/\n\taddTranslations: function(app, callback) {\n\t\treturn OC.L10N.load(app, callback);\n\t},\n\n\t\/**\n\t * Returns the base name of the given path.\n\t * For example for \"\/abc\/somefile.txt\" it will return \"somefile.txt\"\n\t *\n\t * @param {String} path\n\t * @return {String} base name\n\t *\/\n\tbasename: function(path) {\n\t\treturn path.replace(\/\\\\\/g,'\/').replace( \/.*\\\/\/, '' );\n\t},\n\n\t\/**\n\t * Returns the dir name of the given path.\n\t * For example for \"\/abc\/somefile.txt\" it will return \"\/abc\"\n\t *\n\t * @param {String} path\n\t * @return {String} dir name\n\t *\/\n\tdirname: function(path) {\n\t\treturn path.replace(\/\\\\\/g,'\/').replace(\/\\\/[^\\\/]*$\/, '');\n\t},\n\n\t\/**\n\t * Returns whether the given paths are the same, without\n\t * leading, trailing or doubled slashes and also removing\n\t * the dot sections.\n\t *\n\t * @param {String} path1 first path\n\t * @param {String} path2 second path\n\t * @return {bool} true if the paths are the same\n\t *\n\t * @since 9.0\n\t *\/\n\tisSamePath: function(path1, path2) {\n\t\tvar filterDot = function(p) {\n\t\t\treturn p !== '.';\n\t\t};\n\t\tvar pathSections1 = _.filter((path1 || '').split('\/'), filterDot);\n\t\tvar pathSections2 = _.filter((path2 || '').split('\/'), filterDot);\n\t\tpath1 = OC.joinPaths.apply(OC, pathSections1);\n\t\tpath2 = OC.joinPaths.apply(OC, pathSections2);\n\t\treturn path1 === path2;\n\t},\n\n\t\/**\n\t * Join path sections\n\t *\n\t * @param {...String} path sections\n\t *\n\t * @return {String} joined path, any leading or trailing slash\n\t * will be kept\n\t *\n\t * @since 8.2\n\t *\/\n\tjoinPaths: function() {\n\t\tif (arguments.length < 1) {\n\t\t\treturn '';\n\t\t}\n\t\tvar path = '';\n\t\t\/\/ convert to array\n\t\tvar args = Array.prototype.slice.call(arguments);\n\t\t\/\/ discard empty arguments\n\t\targs = _.filter(args, function(arg) {\n\t\t\treturn arg.length > 0;\n\t\t});\n\t\tif (args.length < 1) {\n\t\t\treturn '';\n\t\t}\n\n\t\tvar lastArg = args[args.length - 1];\n\t\tvar leadingSlash = args[0].charAt(0) === '\/';\n\t\tvar trailingSlash = lastArg.charAt(lastArg.length - 1) === '\/';\n\t\tvar sections = [];\n\t\tvar i;\n\t\tfor (i = 0; i < args.length; i++) {\n\t\t\tsections = sections.concat(args[i].split('\/'));\n\t\t}\n\t\tvar first = !leadingSlash;\n\t\tfor (i = 0; i < sections.length; i++) {\n\t\t\tif (sections[i] !== '') {\n\t\t\t\tif (first) {\n\t\t\t\t\tfirst = false;\n\t\t\t\t} else {\n\t\t\t\t\tpath += '\/';\n\t\t\t\t}\n\t\t\t\tpath += sections[i];\n\t\t\t}\n\t\t}\n\n\t\tif (trailingSlash) {\n\t\t\t\/\/ add it back\n\t\t\tpath += '\/';\n\t\t}\n\t\treturn path;\n\t},\n\n\t\/**\n\t * Do a search query and display the results\n\t * @param {string} query the search query\n\t *\/\n\tsearch: function (query) {\n\t\tOC.Search.search(query, null, 0, 30);\n\t},\n\t\/**\n\t * Dialog helper for jquery dialogs.\n\t *\n\t * @namespace OC.dialogs\n\t *\/\n\tdialogs:OCdialogs,\n\t\/**\n\t * Parses a URL query string into a JS map\n\t * @param {string} queryString query string in the format param1=1234&param2=abcde&param3=xyz\n\t * @return {Object.<string, string>} map containing key\/values matching the URL parameters\n\t *\/\n\tparseQueryString:function(queryString){\n\t\tvar parts,\n\t\t\tpos,\n\t\t\tcomponents,\n\t\t\tresult = {},\n\t\t\tkey,\n\t\t\tvalue;\n\t\tif (!queryString){\n\t\t\treturn null;\n\t\t}\n\t\tpos = queryString.indexOf('?');\n\t\tif (pos >= 0){\n\t\t\tqueryString = queryString.substr(pos + 1);\n\t\t}\n\t\tparts = queryString.replace(\/\\+\/g, '%20').split('&');\n\t\tfor (var i = 0; i < parts.length; i++){\n\t\t\t\/\/ split on first equal sign\n\t\t\tvar part = parts[i];\n\t\t\tpos = part.indexOf('=');\n\t\t\tif (pos >= 0) {\n\t\t\t\tcomponents = [\n\t\t\t\t\tpart.substr(0, pos),\n\t\t\t\t\tpart.substr(pos + 1)\n\t\t\t\t];\n\t\t\t}\n\t\t\telse {\n\t\t\t\t\/\/ key only\n\t\t\t\tcomponents = [part];\n\t\t\t}\n\t\t\tif (!components.length){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tkey = decodeURIComponent(components[0]);\n\t\t\tif (!key){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\/\/ if equal sign was there, return string\n\t\t\tif (components.length > 1) {\n\t\t\t\tresult[key] = decodeURIComponent(components[1]);\n\t\t\t}\n\t\t\t\/\/ no equal sign => null value\n\t\t\telse {\n\t\t\t\tresult[key] = null;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t},\n\n\t\/**\n\t * Builds a URL query from a JS map.\n\t * @param {Object.<string, string>} params map containing key\/values matching the URL parameters\n\t * @return {string} String containing a URL query (without question) mark\n\t *\/\n\tbuildQueryString: function(params) {\n\t\tif (!params) {\n\t\t\treturn '';\n\t\t}\n\t\treturn $.map(params, function(value, key) {\n\t\t\tvar s = encodeURIComponent(key);\n\t\t\tif (value !== null && typeof(value) !== 'undefined') {\n\t\t\t\ts += '=' + encodeURIComponent(value);\n\t\t\t}\n\t\t\treturn s;\n\t\t}).join('&');\n\t},\n\n\t\/**\n\t * Opens a popup with the setting for an app.\n\t * @param {string} appid The ID of the app e.g. 'calendar', 'contacts' or 'files'.\n\t * @param {boolean|string}\u00a0loadJS If true 'js\/settings.js' is loaded. If it's a string\n\t * it will attempt to load a script by that name in the 'js' directory.\n\t * @param {boolean} [cache] If true the javascript file won't be forced refreshed. Defaults to true.\n\t * @param {string} [scriptName] The name of the PHP file to load. Defaults to 'settings.php' in\n\t * the root of the app directory hierarchy.\n\t *\/\n\tappSettings:function(args) {\n\t\tif(typeof args === 'undefined' || typeof args.appid === 'undefined') {\n\t\t\tthrow { name: 'MissingParameter', message: 'The parameter appid is missing' };\n\t\t}\n\t\tvar props = {scriptName:'settings.php', cache:true};\n\t\t$.extend(props, args);\n\t\tvar settings = $('#appsettings');\n\t\tif(settings.length === 0) {\n\t\t\tthrow { name: 'MissingDOMElement', message: 'There has be be an element with id \"appsettings\" for the popup to show.' };\n\t\t}\n\t\tvar popup = $('#appsettings_popup');\n\t\tif(popup.length === 0) {\n\t\t\t$('body').prepend('<div class=\"popup hidden\" id=\"appsettings_popup\"><\/div>');\n\t\t\tpopup = $('#appsettings_popup');\n\t\t\tpopup.addClass(settings.hasClass('topright') ? 'topright' : 'bottomleft');\n\t\t}\n\t\tif(popup.is(':visible')) {\n\t\t\tpopup.hide().remove();\n\t\t} else {\n\t\t\tvar arrowclass = settings.hasClass('topright') ? 'up' : 'left';\n\t\t\tvar jqxhr = $.get(OC.filePath(props.appid, '', props.scriptName), function(data) {\n\t\t\t\tpopup.html(data).ready(function() {\n\t\t\t\t\tpopup.prepend('<span class=\"arrow '+arrowclass+'\"><\/span><h2>'+t('core', 'Settings')+'<\/h2><a class=\"close\"><\/a>').show();\n\t\t\t\t\tpopup.find('.close').bind('click', function() {\n\t\t\t\t\t\tpopup.remove();\n\t\t\t\t\t});\n\t\t\t\t\tif(typeof props.loadJS !== 'undefined') {\n\t\t\t\t\t\tvar scriptname;\n\t\t\t\t\t\tif(props.loadJS === true) {\n\t\t\t\t\t\t\tscriptname = 'settings.js';\n\t\t\t\t\t\t} else if(typeof props.loadJS === 'string') {\n\t\t\t\t\t\t\tscriptname = props.loadJS;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow { name: 'InvalidParameter', message: 'The \"loadJS\" parameter must be either boolean or a string.' };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif(props.cache) {\n\t\t\t\t\t\t\t$.ajaxSetup({cache: true});\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$.getScript(OC.filePath(props.appid, 'js', scriptname))\n\t\t\t\t\t\t.fail(function(jqxhr, settings, e) {\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}).show();\n\t\t\t}, 'html');\n\t\t}\n\t},\n\n\t\/**\n\t * For menu toggling\n\t * @todo Write documentation\n\t *\n\t * @param {jQuery} $toggle\n\t * @param {jQuery} $menuEl\n\t * @param {function|undefined} toggle callback invoked everytime the menu is opened\n\t * @param {boolean} headerMenu is this a top right header menu?\n\t * @returns {undefined}\n\t *\/\n\tregisterMenu: function($toggle, $menuEl, toggle, headerMenu) {\n\t\tvar self = this;\n\t\t$menuEl.addClass('menu');\n\n\t\t\/\/ On link, the enter key trigger a click event\n\t\t\/\/ Only use the click to avoid two fired events\n\t\t$toggle.on($toggle.prop('tagName') === 'A'\n\t\t\t? 'click.menu'\n\t\t\t: 'click.menu keyup.menu', function(event) {\n\t\t\t\/\/ prevent the link event (append anchor to URL)\n\t\t\tevent.preventDefault();\n\n\t\t\t\/\/ allow enter key as a trigger\n\t\t\tif (event.key && event.key !== \"Enter\") {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ($menuEl.is(OC._currentMenu)) {\n\t\t\t\tself.hideMenus();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\/\/ another menu was open?\n\t\t\telse if (OC._currentMenu) {\n\t\t\t\t\/\/ close it\n\t\t\t\tself.hideMenus();\n\t\t\t}\n\n\t\t\tif (headerMenu === true) {\n\t\t\t\t$menuEl.parent().addClass('openedMenu');\n\t\t\t}\n\n\t\t\t\/\/ Set menu to expanded\n\t\t\t$toggle.attr('aria-expanded', true);\n\n\t\t\t$menuEl.slideToggle(OC.menuSpeed, toggle);\n\t\t\tOC._currentMenu = $menuEl;\n\t\t\tOC._currentMenuToggle = $toggle;\n\t\t});\n\t},\n\n\t\/**\n\t * @todo Write documentation\n\t *\/\n\tunregisterMenu: function($toggle, $menuEl) {\n\t\t\/\/ close menu if opened\n\t\tif ($menuEl.is(OC._currentMenu)) {\n\t\t\tthis.hideMenus();\n\t\t}\n\t\t$toggle.off('click.menu').removeClass('menutoggle');\n\t\t$menuEl.removeClass('menu');\n\t},\n\n\t\/**\n\t * Hides any open menus\n\t *\n\t * @param {Function} complete callback when the hiding animation is done\n\t *\/\n\thideMenus: function(complete) {\n\t\tif (OC._currentMenu) {\n\t\t\tvar lastMenu = OC._currentMenu;\n\t\t\tOC._currentMenu.trigger(new $.Event('beforeHide'));\n\t\t\tOC._currentMenu.slideUp(OC.menuSpeed, function() {\n\t\t\t\tlastMenu.trigger(new $.Event('afterHide'));\n\t\t\t\tif (complete) {\n\t\t\t\t\tcomplete.apply(this, arguments);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t\/\/ Set menu to closed\n\t\t$('.menutoggle').attr('aria-expanded', false);\n\n\t\t$('.openedMenu').removeClass('openedMenu');\n\t\tOC._currentMenu = null;\n\t\tOC._currentMenuToggle = null;\n\t},\n\n\t\/**\n\t * Shows a given element as menu\n\t *\n\t * @param {Object} [$toggle=null] menu toggle\n\t * @param {Object} $menuEl menu element\n\t * @param {Function} complete callback when the showing animation is done\n\t *\/\n\tshowMenu: function($toggle, $menuEl, complete) {\n\t\tif ($menuEl.is(OC._currentMenu)) {\n\t\t\treturn;\n\t\t}\n\t\tthis.hideMenus();\n\t\tOC._currentMenu = $menuEl;\n\t\tOC._currentMenuToggle = $toggle;\n\t\t$menuEl.trigger(new $.Event('beforeShow'));\n\t\t$menuEl.show();\n\t\t$menuEl.trigger(new $.Event('afterShow'));\n\t\t\/\/ no animation\n\t\tif (_.isFunction(complete)) {\n\t\t\tcomplete();\n\t\t}\n\t},\n\n\t\/**\n\t * Wrapper for matchMedia\n\t *\n\t * This is makes it possible for unit tests to\n\t * stub matchMedia (which doesn't work in PhantomJS)\n\t * @private\n\t *\/\n\t_matchMedia: function(media) {\n\t\tif (window.matchMedia) {\n\t\t\treturn window.matchMedia(media);\n\t\t}\n\t\treturn false;\n\t},\n\n\t\/**\n\t * Returns the user's locale as a BCP 47 compliant language tag\n\t *\n\t * @return {String} locale string\n\t *\/\n\tgetCanonicalLocale: function() {\n\t\tvar locale = this.getLocale();\n\t\treturn typeof locale === 'string' ? locale.replace(\/_\/g, '-') : locale;\n\t},\n\n\t\/**\n\t * Returns the user's locale\n\t *\n\t * @return {String} locale string\n\t *\/\n\tgetLocale: function() {\n\t\treturn $('html').data('locale');\n\t},\n\n\t\/**\n\t * Returns the user's language\n\t *\n\t * @returns {String} language string\n\t *\/\n\tgetLanguage: function () {\n\t\treturn $('html').prop('lang');\n\t},\n\n\t\/**\n\t * Returns whether the current user is an administrator\n\t *\n\t * @return {bool} true if the user is an admin, false otherwise\n\t * @since 9.0.0\n\t *\/\n\tisUserAdmin: function() {\n\t\treturn oc_isadmin;\n\t},\n\n\t\/**\n\t * Warn users that the connection to the server was lost temporarily\n\t *\n\t * This function is throttled to prevent stacked notfications.\n\t * After 7sec the first notification is gone, then we can show another one\n\t * if necessary.\n\t *\/\n\t_ajaxConnectionLostHandler: _.throttle(function() {\n\t\tOC.Notification.showTemporary(t('core', 'Connection to server lost'));\n\t}, 7 * 1000, {trailing: false}),\n\n\t\/**\n\t * Process ajax error, redirects to main page\n\t * if an error\/auth error status was returned.\n\t *\/\n\t_processAjaxError: function(xhr) {\n\t\tvar self = this;\n\t\t\/\/ purposefully aborted request ?\n\t\t\/\/ this._userIsNavigatingAway needed to distinguish ajax calls cancelled by navigating away\n\t\t\/\/ from calls cancelled by failed cross-domain ajax due to SSO redirect\n\t\tif (xhr.status === 0 && (xhr.statusText === 'abort' || xhr.statusText === 'timeout' || self._reloadCalled)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (_.contains([302, 303, 307, 401], xhr.status) && OC.currentUser) {\n\t\t\t\/\/ sometimes \"beforeunload\" happens later, so need to defer the reload a bit\n\t\t\tsetTimeout(function() {\n\t\t\t\tif (!self._userIsNavigatingAway && !self._reloadCalled) {\n\t\t\t\t\tvar timer = 0;\n\t\t\t\t\tvar seconds = 5;\n\t\t\t\t\tvar interval = setInterval( function() {\n\t\t\t\t\t\tOC.Notification.showUpdate(n('core', 'Problem loading page, reloading in %n second', 'Problem loading page, reloading in %n seconds', seconds - timer));\n\t\t\t\t\t\tif (timer >= seconds) {\n\t\t\t\t\t\t\tclearInterval(interval);\n\t\t\t\t\t\t\tOC.reload();\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttimer++;\n\t\t\t\t\t\t}, 1000 \/\/ 1 second interval\n\t\t\t\t\t);\n\n\t\t\t\t\t\/\/ only call reload once\n\t\t\t\t\tself._reloadCalled = true;\n\t\t\t\t}\n\t\t\t}, 100);\n\t\t} else if(xhr.status === 0) {\n\t\t\t\/\/ Connection lost (e.g. WiFi disconnected or server is down)\n\t\t\tsetTimeout(function() {\n\t\t\t\tif (!self._userIsNavigatingAway && !self._reloadCalled) {\n\t\t\t\t\tself._ajaxConnectionLostHandler();\n\t\t\t\t}\n\t\t\t}, 100);\n\t\t}\n\t},\n\n\t\/**\n\t * Registers XmlHttpRequest object for global error processing.\n\t *\n\t * This means that if this XHR object returns 401 or session timeout errors,\n\t * the current page will automatically be reloaded.\n\t *\n\t * @param {XMLHttpRequest} xhr\n\t *\/\n\tregisterXHRForErrorProcessing: function(xhr) {\n\t\tvar loadCallback = function() {\n\t\t\tif (xhr.readyState !== 4) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t\/\/ fire jquery global ajax error handler\n\t\t\t$(document).trigger(new $.Event('ajaxError'), xhr);\n\t\t};\n\n\t\tvar errorCallback = function() {\n\t\t\t\/\/ fire jquery global ajax error handler\n\t\t\t$(document).trigger(new $.Event('ajaxError'), xhr);\n\t\t};\n\n\t\tif (xhr.addEventListener) {\n\t\t\txhr.addEventListener('load', loadCallback);\n\t\t\txhr.addEventListener('error', errorCallback);\n\t\t}\n\n\t}\n};\n\n\/**\n * Current user attributes\n *\n * @typedef {Object} OC.CurrentUser\n *\n * @property {String} uid user id\n * @property {String} displayName display name\n *\/\n\n\/**\n * @namespace OC.Plugins\n *\/\nOC.Plugins = {\n\t\/**\n\t * @type Array.<OC.Plugin>\n\t *\/\n\t_plugins: {},\n\n\t\/**\n\t * Register plugin\n\t *\n\t * @param {String} targetName app name \/ class name to hook into\n\t * @param {OC.Plugin} plugin\n\t *\/\n\tregister: function(targetName, plugin) {\n\t\tvar plugins = this._plugins[targetName];\n\t\tif (!plugins) {\n\t\t\tplugins = this._plugins[targetName] = [];\n\t\t}\n\t\tplugins.push(plugin);\n\t},\n\n\t\/**\n\t * Returns all plugin registered to the given target\n\t * name \/ app name \/ class name.\n\t *\n\t * @param {String} targetName app name \/ class name to hook into\n\t * @return {Array.<OC.Plugin>} array of plugins\n\t *\/\n\tgetPlugins: function(targetName) {\n\t\treturn this._plugins[targetName] || [];\n\t},\n\n\t\/**\n\t * Call attach() on all plugins registered to the given target name.\n\t *\n\t * @param {String} targetName app name \/ class name\n\t * @param {Object} object to be extended\n\t * @param {Object} [options] options\n\t *\/\n\tattach: function(targetName, targetObject, options) {\n\t\tvar plugins = this.getPlugins(targetName);\n\t\tfor (var i = 0; i < plugins.length; i++) {\n\t\t\tif (plugins[i].attach) {\n\t\t\t\tplugins[i].attach(targetObject, options);\n\t\t\t}\n\t\t}\n\t},\n\n\t\/**\n\t * Call detach() on all plugins registered to the given target name.\n\t *\n\t * @param {String} targetName app name \/ class name\n\t * @param {Object} object to be extended\n\t * @param {Object} [options] options\n\t *\/\n\tdetach: function(targetName, targetObject, options) {\n\t\tvar plugins = this.getPlugins(targetName);\n\t\tfor (var i = 0; i < plugins.length; i++) {\n\t\t\tif (plugins[i].detach) {\n\t\t\t\tplugins[i].detach(targetObject, options);\n\t\t\t}\n\t\t}\n\t},\n\n\t\/**\n\t * Plugin\n\t *\n\t * @todo make this a real class in the future\n\t * @typedef {Object} OC.Plugin\n\t *\n\t * @property {String} name plugin name\n\t * @property {Function} attach function that will be called when the\n\t * plugin is attached\n\t * @property {Function} [detach] function that will be called when the\n\t * plugin is detached\n\t *\/\n\n};\n\n\/**\n * @namespace OC.search\n *\/\nOC.search.customResults = {};\n\/**\n * @deprecated use get\/setFormatter() instead\n *\/\nOC.search.resultTypes = {};\n\nOC.addStyle.loaded=[];\nOC.addScript.loaded=[];\n\n\/**\n * A little class to manage a status field for a \"saving\" process.\n * It can be used to display a starting message (e.g. \"Saving...\") and then\n * replace it with a green success message or a red error message.\n *\n * @namespace OC.msg\n *\/\nOC.msg = {\n\t\/**\n\t * Displayes a \"Saving...\" message in the given message placeholder\n\t *\n\t * @param {Object} selector\tPlaceholder to display the message in\n\t *\/\n\tstartSaving: function(selector) {\n\t\tthis.startAction(selector, t('core', 'Saving...'));\n\t},\n\n\t\/**\n\t * Displayes a custom message in the given message placeholder\n\t *\n\t * @param {Object} selector\tPlaceholder to display the message in\n\t * @param {string} message\tPlain text message to display (no HTML allowed)\n\t *\/\n\tstartAction: function(selector, message) {\n\t\t$(selector).text(message)\n\t\t\t.removeClass('success')\n\t\t\t.removeClass('error')\n\t\t\t.stop(true, true)\n\t\t\t.show();\n\t},\n\n\t\/**\n\t * Displayes an success\/error message in the given selector\n\t *\n\t * @param {Object} selector\tPlaceholder to display the message in\n\t * @param {Object} response\tResponse of the server\n\t * @param {Object} response.data\tData of the servers response\n\t * @param {string} response.data.message\tPlain text message to display (no HTML allowed)\n\t * @param {string} response.status\tis being used to decide whether the message\n\t * is displayed as an error\/success\n\t *\/\n\tfinishedSaving: function(selector, response) {\n\t\tthis.finishedAction(selector, response);\n\t},\n\n\t\/**\n\t * Displayes an success\/error message in the given selector\n\t *\n\t * @param {Object} selector\tPlaceholder to display the message in\n\t * @param {Object} response\tResponse of the server\n\t * @param {Object} response.data Data of the servers response\n\t * @param {string} response.data.message Plain text message to display (no HTML allowed)\n\t * @param {string} response.status is being used to decide whether the message\n\t * is displayed as an error\/success\n\t *\/\n\tfinishedAction: function(selector, response) {\n\t\tif (response.status === \"success\") {\n\t\t\tthis.finishedSuccess(selector, response.data.message);\n\t\t} else {\n\t\t\tthis.finishedError(selector, response.data.message);\n\t\t}\n\t},\n\n\t\/**\n\t * Displayes an success message in the given selector\n\t *\n\t * @param {Object} selector Placeholder to display the message in\n\t * @param {string} message Plain text success message to display (no HTML allowed)\n\t *\/\n\tfinishedSuccess: function(selector, message) {\n\t\t$(selector).text(message)\n\t\t\t.addClass('success')\n\t\t\t.removeClass('error')\n\t\t\t.stop(true, true)\n\t\t\t.delay(3000)\n\t\t\t.fadeOut(900)\n\t\t\t.show();\n\t},\n\n\t\/**\n\t * Displayes an error message in the given selector\n\t *\n\t * @param {Object} selector Placeholder to display the message in\n\t * @param {string} message Plain text error message to display (no HTML allowed)\n\t *\/\n\tfinishedError: function(selector, message) {\n\t\t$(selector).text(message)\n\t\t\t.addClass('error')\n\t\t\t.removeClass('success')\n\t\t\t.show();\n\t}\n};\n\n\/**\n * @todo Write documentation\n * @namespace\n *\/\nOC.Notification={\n\tqueuedNotifications: [],\n\tgetDefaultNotificationFunction: null,\n\n\t\/**\n\t * @type Array<int>\n\t * @description array of notification timers\n\t *\/\n\tnotificationTimers: [],\n\n\t\/**\n\t * @param callback\n\t * @todo Write documentation\n\t *\/\n\tsetDefault: function(callback) {\n\t\tOC.Notification.getDefaultNotificationFunction = callback;\n\t},\n\n\t\/**\n\t * Hides a notification.\n\t *\n\t * If a row is given, only hide that one.\n\t * If no row is given, hide all notifications.\n\t *\n\t * @param {jQuery} [$row] notification row\n\t * @param {Function} [callback] callback\n\t *\/\n\thide: function($row, callback) {\n\t\tvar self = this;\n\t\tvar $notification = $('#notification');\n\n\t\tif (_.isFunction($row)) {\n\t\t\t\/\/ first arg is the callback\n\t\t\tcallback = $row;\n\t\t\t$row = undefined;\n\t\t}\n\n\t\tif (!$row) {\n\t\t\tconsole.warn('Missing argument $row in OC.Notification.hide() call, caller needs to be adjusted to only dismiss its own notification');\n\t\t\t\/\/ assume that the row to be hidden is the first one\n\t\t\t$row = $notification.find('.row:first');\n\t\t}\n\n\t\tif ($row && $notification.find('.row').length > 1) {\n\t\t\t\/\/ remove the row directly\n\t\t\t$row.remove();\n\t\t\tif (callback) {\n\t\t\t\tcallback.call();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t_.defer(function() {\n\t\t\t\/\/ fade out is supposed to only fade when there is a single row\n\t\t\t\/\/ however, some code might call hide() and show() directly after,\n\t\t\t\/\/ which results in more than one element\n\t\t\t\/\/ in this case, simply delete that one element that was supposed to\n\t\t\t\/\/ fade out\n\t\t\t\/\/\n\t\t\t\/\/ FIXME: remove once all callers are adjusted to only hide their own notifications\n\t\t\tif ($notification.find('.row').length > 1) {\n\t\t\t\t$row.remove();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t\/\/ else, fade out whatever was present\n\t\t\t$notification.fadeOut('400', function(){\n\t\t\t\tif (self.isHidden()) {\n\t\t\t\t\tif (self.getDefaultNotificationFunction) {\n\t\t\t\t\t\tself.getDefaultNotificationFunction.call();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (callback) {\n\t\t\t\t\tcallback.call();\n\t\t\t\t}\n\t\t\t\t$notification.empty();\n\t\t\t});\n\t\t});\n\t},\n\n\t\/**\n\t * Shows a notification as HTML without being sanitized before.\n\t * If you pass unsanitized user input this may lead to a XSS vulnerability.\n\t * Consider using show() instead of showHTML()\n\t *\n\t * @param {string} html Message to display\n\t * @param {Object} [options] options\n\t * @param {string} [options.type] notification type\n\t * @param {int} [options.timeout=0] timeout value, defaults to 0 (permanent)\n\t * @return {jQuery} jQuery element for notification row\n\t *\/\n\tshowHtml: function(html, options) {\n\t\toptions = options || {};\n\t\t_.defaults(options, {\n\t\t\ttimeout: 0\n\t\t});\n\n\t\tvar self = this;\n\t\tvar $notification = $('#notification');\n\t\tif (this.isHidden()) {\n\t\t\t$notification.fadeIn().css('display','inline-block');\n\t\t}\n\t\tvar $row = $('<div class=\"row\"><\/div>');\n\t\tif (options.type) {\n\t\t\t$row.addClass('type-' + options.type);\n\t\t}\n\t\tif (options.type === 'error') {\n\t\t\t\/\/ add a close button\n\t\t\tvar $closeButton = $('<a class=\"action close icon-close\" href=\"#\"><\/a>');\n\t\t\t$closeButton.attr('alt', t('core', 'Dismiss'));\n\t\t\t$row.append($closeButton);\n\t\t\t$closeButton.one('click', function() {\n\t\t\t\tself.hide($row);\n\t\t\t\treturn false;\n\t\t\t});\n\t\t\t$row.addClass('closeable');\n\t\t}\n\n\t\t$row.prepend(html);\n\t\t$notification.append($row);\n\n\t\tif(options.timeout > 0) {\n\t\t\t\/\/ register timeout to vanish notification\n\t\t\tthis.notificationTimers.push(setTimeout(function() {\n\t\t\t\tself.hide($row);\n\t\t\t}, (options.timeout * 1000)));\n\t\t}\n\n\t\treturn $row;\n\t},\n\n\t\/**\n\t * Shows a sanitized notification\n\t *\n\t * @param {string} text Message to display\n\t * @param {Object} [options] options\n\t * @param {string} [options.type] notification type\n\t * @param {int} [options.timeout=0] timeout value, defaults to 0 (permanent)\n\t * @return {jQuery} jQuery element for notification row\n\t *\/\n\tshow: function(text, options) {\n\t\treturn this.showHtml($('<div\/>').text(text).html(), options);\n\t},\n\n\t\/**\n\t * Updates (replaces) a sanitized notification.\n\t *\n\t * @param {string} text Message to display\n\t * @return {jQuery} JQuery element for notificaiton row\n\t *\/\n\tshowUpdate: function(text) {\n\t\tvar $notification = $('#notification');\n\t\t\/\/ sanitise\n\t\tvar $html = $('<div\/>').text(text).html();\n\n\t\t\/\/ new notification\n\t\tif (text && $notification.find('.row').length == 0) {\n\t\t\treturn this.showHtml($html);\n\t\t}\n\n\t\tvar $row = $('<div class=\"row\"><\/div>').prepend($html);\n\n\t\t\/\/ just update html in notification\n\t\t$notification.html($row);\n\n\t\treturn $row;\n\t},\n\n\t\/**\n\t * Shows a notification that disappears after x seconds, default is\n\t * 7 seconds\n\t *\n\t * @param {string} text Message to show\n\t * @param {array} [options] options array\n\t * @param {int} [options.timeout=7] timeout in seconds, if this is 0 it will show the message permanently\n\t * @param {boolean} [options.isHTML=false] an indicator for HTML notifications (true) or text (false)\n\t * @param {string} [options.type] notification type\n\t *\/\n\tshowTemporary: function(text, options) {\n\t\tvar defaults = {\n\t\t\tisHTML: false,\n\t\t\ttimeout: 7\n\t\t};\n\t\toptions = options || {};\n\t\t\/\/ merge defaults with passed in options\n\t\t_.defaults(options, defaults);\n\n\t\tvar $row;\n\t\tif(options.isHTML) {\n\t\t\t$row = this.showHtml(text, options);\n\t\t} else {\n\t\t\t$row = this.show(text, options);\n\t\t}\n\t\treturn $row;\n\t},\n\n\t\/**\n\t * Returns whether a notification is hidden.\n\t * @return {boolean}\n\t *\/\n\tisHidden: function() {\n\t\treturn !$(\"#notification\").find('.row').length;\n\t}\n};\n\n\/**\n * Initializes core\n *\/\nfunction initCore() {\n\t\/**\n\t * Disable automatic evaluation of responses for $.ajax() functions (and its\n\t * higher-level alternatives like $.get() and $.post()).\n\t *\n\t * If a response to a $.ajax() request returns a content type of \"application\/javascript\"\n\t * JQuery would previously execute the response body. This is a pretty unexpected\n\t * behaviour and can result in a bypass of our Content-Security-Policy as well as\n\t * multiple unexpected XSS vectors.\n\t *\/\n\t$.ajaxSetup({\n\t\tcontents: {\n\t\t\tscript: false\n\t\t}\n\t});\n\n\t\/**\n\t * Disable execution of eval in jQuery. We do require an allowed eval CSP\n\t * configuration at the moment for handlebars et al. But for jQuery there is\n\t * not much of a reason to execute JavaScript directly via eval.\n\t *\n\t * This thus mitigates some unexpected XSS vectors.\n\t *\/\n\tjQuery.globalEval = function(){};\n\n\t\/**\n\t * Set users locale to moment.js as soon as possible\n\t *\/\n\tmoment.locale(OC.getLocale());\n\n\tvar userAgent = window.navigator.userAgent;\n\tvar msie = userAgent.indexOf('MSIE ');\n\tvar trident = userAgent.indexOf('Trident\/');\n\tvar edge = userAgent.indexOf('Edge\/');\n\n\tif (msie > 0 || trident > 0) {\n\t\t\/\/ (IE 10 or older) || IE 11\n\t\t$('html').addClass('ie');\n\t} else if (edge > 0) {\n\t\t\/\/ for edge\n\t\t$('html').addClass('edge');\n\t}\n\n\t\/\/ css variables fallback for IE\n\tif (msie > 0 || trident > 0) {\n\t\tcssVars();\n\t}\n\n\t$(window).on('unload.main', function() {\n\t\tOC._unloadCalled = true;\n\t});\n\t$(window).on('beforeunload.main', function() {\n\t\t\/\/ super-trick thanks to http:\/\/stackoverflow.com\/a\/4651049\n\t\t\/\/ in case another handler displays a confirmation dialog (ex: navigating away\n\t\t\/\/ during an upload), there are two possible outcomes: user clicked \"ok\" or\n\t\t\/\/ \"cancel\"\n\n\t\t\/\/ first timeout handler is called after unload dialog is closed\n\t\tsetTimeout(function() {\n\t\t\tOC._userIsNavigatingAway = true;\n\n\t\t\t\/\/ second timeout event is only called if user cancelled (Chrome),\n\t\t\t\/\/ but in other browsers it might still be triggered, so need to\n\t\t\t\/\/ set a higher delay...\n\t\t\tsetTimeout(function() {\n\t\t\t\tif (!OC._unloadCalled) {\n\t\t\t\t\tOC._userIsNavigatingAway = false;\n\t\t\t\t}\n\t\t\t}, 10000);\n\t\t},1);\n\t});\n\t$(document).on('ajaxError.main', function( event, request, settings ) {\n\t\tif (settings && settings.allowAuthErrors) {\n\t\t\treturn;\n\t\t}\n\t\tOC._processAjaxError(request);\n\t});\n\n\t\/**\n\t * Calls the server periodically to ensure that session and CSRF\n\t * token doesn't expire\n\t *\/\n\tfunction initSessionHeartBeat() {\n\t\t\/\/ interval in seconds\n\t\tvar interval = NaN;\n\t\tif (oc_config.session_lifetime) {\n\t\t\tinterval = Math.floor(oc_config.session_lifetime \/ 2);\n\t\t}\n\t\tinterval = isNaN(interval)? 900: interval;\n\n\t\t\/\/ minimum one minute\n\t\tinterval = Math.max(60, interval);\n\t\t\/\/ max interval in seconds set to 24 hours\n\t\tinterval = Math.min(24 * 3600, interval);\n\n\t\tvar url = OC.generateUrl('\/csrftoken');\n\t\tsetInterval(function() {\n\t\t\t$.ajax(url).then(function(resp) {\n\t\t\t\toc_requesttoken = resp.token;\n\t\t\t\tOC.requestToken = resp.token;\n\t\t\t}).fail(function(e) {\n\t\t\t\tconsole.error('session heartbeat failed', e);\n\t\t\t});\n\t\t}, interval * 1000);\n\t}\n\n\t\/\/ session heartbeat (defaults to enabled)\n\tif (typeof(oc_config.session_keepalive) === 'undefined' ||\n\t\t!!oc_config.session_keepalive) {\n\n\t\tinitSessionHeartBeat();\n\t}\n\n\tOC.registerMenu($('#expand'), $('#expanddiv'), false, true);\n\n\t\/\/ toggle for menus\n\t\/\/$(document).on('mouseup.closemenus keyup', function(event) {\n\t$(document).on('mouseup.closemenus', function(event) {\n\n\t\t\/\/ allow enter as a trigger\n\t\t\/\/ if (event.key && event.key !== \"Enter\") {\n\t\t\/\/ \treturn;\n\t\t\/\/ }\n\n\t\tvar $el = $(event.target);\n\t\tif ($el.closest('.menu').length || $el.closest('.menutoggle').length) {\n\t\t\t\/\/ don't close when clicking on the menu directly or a menu toggle\n\t\t\treturn false;\n\t\t}\n\n\t\tOC.hideMenus();\n\t});\n\n\t\/**\n\t * Set up the main menu toggle to react to media query changes.\n\t * If the screen is small enough, the main menu becomes a toggle.\n\t * If the screen is bigger, the main menu is not a toggle any more.\n\t *\/\n\tfunction setupMainMenu() {\n\n\t\t\/\/ init the more-apps menu\n\t\tOC.registerMenu($('#more-apps > a'), $('#navigation'));\n\n\t\t\/\/ toggle the navigation\n\t\tvar $toggle = $('#header .header-appname-container');\n\t\tvar $navigation = $('#navigation');\n\t\tvar $appmenu = $('#appmenu');\n\n\t\t\/\/ init the menu\n\t\tOC.registerMenu($toggle, $navigation);\n\t\t$toggle.data('oldhref', $toggle.attr('href'));\n\t\t$toggle.attr('href', '#');\n\t\t$navigation.hide();\n\n\t\t\/\/ show loading feedback\n\t\t$navigation.delegate('a', 'click', function(event) {\n\t\t\tvar $app = $(event.target);\n\t\t\tif(!$app.is('a')) {\n\t\t\t\t$app = $app.closest('a');\n\t\t\t}\n\t\t\tif(event.which === 1 && !event.ctrlKey && !event.metaKey) {\n\t\t\t\t$app.addClass('app-loading');\n\t\t\t} else {\n\t\t\t\t\/\/ Close navigation when opening app in\n\t\t\t\t\/\/ a new tab\n\t\t\t\tOC.hideMenus(function(){return false;});\n\t\t\t}\n\t\t});\n\n\t\t$navigation.delegate('a', 'mouseup', function(event) {\n\t\t\tif(event.which === 2) {\n\t\t\t\t\/\/ Close navigation when opening app in\n\t\t\t\t\/\/ a new tab via middle click\n\t\t\t\tOC.hideMenus(function(){return false;});\n\t\t\t}\n\t\t});\n\n\t\t$appmenu.delegate('a', 'click', function(event) {\n\t\t\tvar $app = $(event.target);\n\t\t\tif(!$app.is('a')) {\n\t\t\t\t$app = $app.closest('a');\n\t\t\t}\n\t\t\tif(event.which === 1 && !event.ctrlKey && !event.metaKey) {\n\t\t\t\t$app.addClass('app-loading');\n\t\t\t} else {\n\t\t\t\t\/\/ Close navigation when opening app in\n\t\t\t\t\/\/ a new tab\n\t\t\t\tOC.hideMenus(function(){return false;});\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction setupUserMenu() {\n\t\tvar $menu = $('#header #settings');\n\n\t\t\/\/ show loading feedback\n\t\t$menu.delegate('a', 'click', function(event) {\n\t\t\tvar $page = $(event.target);\n\t\t\tif (!$page.is('a')) {\n\t\t\t\t$page = $page.closest('a');\n\t\t\t}\n\t\t\tif(event.which === 1 && !event.ctrlKey && !event.metaKey) {\n\t\t\t\t$page.find('img').remove();\n\t\t\t\t$page.find('div').remove(); \/\/ prevent odd double-clicks\n\t\t\t\t$page.prepend($('<div\/>').addClass('icon-loading-small'));\n\t\t\t} else {\n\t\t\t\t\/\/ Close navigation when opening menu entry in\n\t\t\t\t\/\/ a new tab\n\t\t\t\tOC.hideMenus(function(){return false;});\n\t\t\t}\n\t\t});\n\n\t\t$menu.delegate('a', 'mouseup', function(event) {\n\t\t\tif(event.which === 2) {\n\t\t\t\t\/\/ Close navigation when opening app in\n\t\t\t\t\/\/ a new tab via middle click\n\t\t\t\tOC.hideMenus(function(){return false;});\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction setupContactsMenu() {\n\t\tnew OC.ContactsMenu({\n\t\t\tel: $('#contactsmenu .menu'),\n\t\t\ttrigger: $('#contactsmenu .menutoggle')\n\t\t});\n\t}\n\n\tsetupMainMenu();\n\tsetupUserMenu();\n\tsetupContactsMenu();\n\n\t\/\/ move triangle of apps dropdown to align with app name triangle\n\t\/\/ 2 is the additional offset between the triangles\n\tif($('#navigation').length) {\n\t\t$('#header #nextcloud + .menutoggle').on('click', function(){\n\t\t\t$('#menu-css-helper').remove();\n\t\t\tvar caretPosition = $('.header-appname + .icon-caret').offset().left - 2;\n\t\t\tif(caretPosition > 255) {\n\t\t\t\t\/\/ if the app name is longer than the menu, just put the triangle in the middle\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\t$('head').append('<style id=\"menu-css-helper\">#navigation:after { left: '+ caretPosition +'px; }<\/style>');\n\t\t\t}\n\t\t});\n\t\t$('#header #appmenu .menutoggle').on('click', function() {\n\t\t\t$('#appmenu').toggleClass('menu-open');\n\t\t\tif($('#appmenu').is(':visible')) {\n\t\t\t\t$('#menu-css-helper').remove();\n\t\t\t}\n\t\t});\n\t}\n\n\tvar resizeMenu = function() {\n\t\tvar appList = $('#appmenu li');\n\t\tvar rightHeaderWidth = $('.header-right').outerWidth();\n\t\tvar headerWidth = $('header').outerWidth();\n\t\tvar usePercentualAppMenuLimit = 0.33;\n\t\tvar minAppsDesktop = 8;\n\t\tvar availableWidth = headerWidth - $('#nextcloud').outerWidth() - (rightHeaderWidth > 210 ? rightHeaderWidth : 210)\n\t\tvar isMobile = $(window).width() < 768;\n\t\tif (!isMobile) {\n\t\t\tavailableWidth = availableWidth * usePercentualAppMenuLimit;\n\t\t}\n\t\tvar appCount = Math.floor((availableWidth \/ $(appList).width()));\n\t\tif (isMobile && appCount > minAppsDesktop) {\n\t\t\tappCount = minAppsDesktop;\n\t\t}\n\t\tif (!isMobile && appCount < minAppsDesktop) {\n\t\t\tappCount = minAppsDesktop;\n\t\t}\n\n\t\t\/\/ show at least 2 apps in the popover\n\t\tif(appList.length-1-appCount >= 1) {\n\t\t\tappCount--;\n\t\t}\n\n\t\t$('#more-apps a').removeClass('active');\n\t\tvar lastShownApp;\n\t\tfor (var k = 0; k < appList.length-1; k++) {\n\t\t\tvar name = $(appList[k]).data('id');\n\t\t\tif(k < appCount) {\n\t\t\t\t$(appList[k]).removeClass('hidden');\n\t\t\t\t$('#apps li[data-id=' + name + ']').addClass('in-header');\n\t\t\t\tlastShownApp = appList[k];\n\t\t\t} else {\n\t\t\t\t$(appList[k]).addClass('hidden');\n\t\t\t\t$('#apps li[data-id=' + name + ']').removeClass('in-header');\n\t\t\t\t\/\/ move active app to last position if it is active\n\t\t\t\tif(appCount > 0 && $(appList[k]).children('a').hasClass('active')) {\n\t\t\t\t\t$(lastShownApp).addClass('hidden');\n\t\t\t\t\t$('#apps li[data-id=' + $(lastShownApp).data('id') + ']').removeClass('in-header');\n\t\t\t\t\t$(appList[k]).removeClass('hidden');\n\t\t\t\t\t$('#apps li[data-id=' + name + ']').addClass('in-header');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t\/\/ show\/hide more apps icon\n\t\tif($('#apps li:not(.in-header)').length === 0) {\n\t\t\t$('#more-apps').hide();\n\t\t\t$('#navigation').hide();\n\t\t} else {\n\t\t\t$('#more-apps').show();\n\t\t}\n\t};\n\t$(window).resize(resizeMenu);\n\tsetTimeout(resizeMenu, 0);\n\n\t\/\/ just add snapper for logged in users\n\tif($('#app-navigation').length && !$('html').hasClass('lte9')) {\n\n\t\t\/\/ App sidebar on mobile\n\t\tvar snapper = new Snap({\n\t\t\telement: document.getElementById('app-content'),\n\t\t\tdisable: 'right',\n\t\t\tmaxPosition: 300, \/\/ $navigation-width\n\t\t\tminDragDistance: 100\n\t\t});\n\n\t\t$('#app-content').prepend('<div id=\"app-navigation-toggle\" class=\"icon-menu\" style=\"display:none;\" tabindex=\"0\"><\/div>');\n\n\t\tvar toggleSnapperOnButton = function(){\n\t\t\tif(snapper.state().state == 'left'){\n\t\t\t\tsnapper.close();\n\t\t\t} else {\n\t\t\t\tsnapper.open('left');\n\t\t\t}\n\t\t};\n\n\t\t$('#app-navigation-toggle').click(function(){\n\t\t\ttoggleSnapperOnButton();\n\t\t});\n\n\t\t$('#app-navigation-toggle').keypress(function(e) {\n\t\t\tif(e.which == 13) {\n\t\t\t\ttoggleSnapperOnButton();\n\t\t\t}\n\t\t});\n\n\t\t\/\/ close sidebar when switching navigation entry\n\t\tvar $appNavigation = $('#app-navigation');\n\t\t$appNavigation.delegate('a, :button', 'click', function(event) {\n\t\t\tvar $target = $(event.target);\n\t\t\t\/\/ don't hide navigation when changing settings or adding things\n\t\t\tif($target.is('.app-navigation-noclose') ||\n\t\t\t\t$target.closest('.app-navigation-noclose').length) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif($target.is('.app-navigation-entry-utils-menu-button') ||\n\t\t\t\t$target.closest('.app-navigation-entry-utils-menu-button').length) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif($target.is('.add-new') ||\n\t\t\t\t$target.closest('.add-new').length) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif($target.is('#app-settings') ||\n\t\t\t\t$target.closest('#app-settings').length) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsnapper.close();\n\t\t});\n\n\t\tvar navigationBarSlideGestureEnabled = false;\n\t\tvar navigationBarSlideGestureAllowed = true;\n\t\tvar navigationBarSlideGestureEnablePending = false;\n\n\t\tOC.allowNavigationBarSlideGesture = function() {\n\t\t\tnavigationBarSlideGestureAllowed = true;\n\n\t\t\tif (navigationBarSlideGestureEnablePending) {\n\t\t\t\tsnapper.enable();\n\n\t\t\t\tnavigationBarSlideGestureEnabled = true;\n\t\t\t\tnavigationBarSlideGestureEnablePending = false;\n\t\t\t}\n\t\t};\n\n\t\tOC.disallowNavigationBarSlideGesture = function() {\n\t\t\tnavigationBarSlideGestureAllowed = false;\n\n\t\t\tif (navigationBarSlideGestureEnabled) {\n\t\t\t\tvar endCurrentDrag = true;\n\t\t\t\tsnapper.disable(endCurrentDrag);\n\n\t\t\t\tnavigationBarSlideGestureEnabled = false;\n\t\t\t\tnavigationBarSlideGestureEnablePending = true;\n\t\t\t}\n\t\t};\n\n\t\tvar toggleSnapperOnSize = function() {\n\t\t\tif($(window).width() > 768) {\n\t\t\t\tsnapper.close();\n\t\t\t\tsnapper.disable();\n\n\t\t\t\tnavigationBarSlideGestureEnabled = false;\n\t\t\t\tnavigationBarSlideGestureEnablePending = false;\n\t\t\t} else if (navigationBarSlideGestureAllowed) {\n\t\t\t\tsnapper.enable();\n\n\t\t\t\tnavigationBarSlideGestureEnabled = true;\n\t\t\t\tnavigationBarSlideGestureEnablePending = false;\n\t\t\t} else {\n\t\t\t\tnavigationBarSlideGestureEnablePending = true;\n\t\t\t}\n\t\t};\n\n\t\t$(window).resize(_.debounce(toggleSnapperOnSize, 250));\n\n\t\t\/\/ initial call\n\t\ttoggleSnapperOnSize();\n\n\t}\n\n\t\/\/ Update live timestamps every 30 seconds\n\tsetInterval(function() {\n\t\t$('.live-relative-timestamp').each(function() {\n\t\t\t$(this).text(OC.Util.relativeModifiedDate(parseInt($(this).attr('data-timestamp'), 10)));\n\t\t});\n\t}, 30 * 1000);\n\n\tOC.PasswordConfirmation.init();\n}\n\nOC.PasswordConfirmation = {\n\tcallback: null,\n\tpageLoadTime: null,\n\tinit: function() {\n\t\t$('.password-confirm-required').on('click', _.bind(this.requirePasswordConfirmation, this));\n\t\tthis.pageLoadTime = moment.now();\n\t},\n\n\trequiresPasswordConfirmation: function() {\n\t\tvar serverTimeDiff = this.pageLoadTime - (nc_pageLoad * 1000);\n\t\tvar timeSinceLogin = moment.now() - (serverTimeDiff + (nc_lastLogin * 1000));\n\n\t\t\/\/ if timeSinceLogin > 30 minutes and user backend allows password confirmation\n\t\treturn (backendAllowsPasswordConfirmation && timeSinceLogin > 30 * 60 * 1000);\n\t},\n\n\t\/**\n\t * @param {function} callback\n\t *\/\n\trequirePasswordConfirmation: function(callback, options, rejectCallback) {\n\t\toptions = typeof options !== 'undefined' ? options : {};\n\t\tvar defaults = {\n\t\t\ttitle: t('core','Authentication required'),\n\t\t\ttext: t(\n\t\t\t\t'core',\n\t\t\t\t'This action requires you to confirm your password'\n\t\t\t),\n\t\t\tconfirm: t('core', 'Confirm'),\n\t\t\tlabel: t('core','Password'),\n\t\t\terror: '',\n\t\t};\n\n\t\tvar config = _.extend(defaults, options);\n\n\t\tvar self = this;\n\n\t\tif (this.requiresPasswordConfirmation()) {\n\t\t\tOC.dialogs.prompt(\n\t\t\t\tconfig.text,\n\t\t\t\tconfig.title,\n\t\t\t\tfunction (result, password) {\n\t\t\t\t\tif (result && password !== '') {\n\t\t\t\t\t\tself._confirmPassword(password, config);\n\t\t\t\t\t} else if (_.isFunction(rejectCallback)) {\n\t\t\t\t\t\trejectCallback()\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\ttrue,\n\t\t\t\tconfig.label,\n\t\t\t\ttrue\n\t\t\t).then(function() {\n\t\t\t\tvar $dialog = $('.oc-dialog:visible');\n\t\t\t\t$dialog.find('.ui-icon').remove();\n\t\t\t\t$dialog.addClass('password-confirmation');\n\t\t\t\tif (config.error !== '') {\n\t\t\t\t\tvar $error = $('<p><\/p>').addClass('msg warning').text(config.error);\n\t\t\t\t}\n\t\t\t\t$dialog.find('.oc-dialog-content').append($error);\n\t\t\t\t$dialog.find('.oc-dialog-buttonrow').addClass('aside');\n\n\t\t\t\tvar $buttons = $dialog.find('button');\n\t\t\t\t$buttons.eq(0).hide();\n\t\t\t\t$buttons.eq(1).text(config.confirm);\n\t\t\t});\n\t\t}\n\n\t\tthis.callback = callback;\n\t},\n\n\t_confirmPassword: function(password, config) {\n\t\tvar self = this;\n\n\t\t$.ajax({\n\t\t\turl: OC.generateUrl('\/login\/confirm'),\n\t\t\tdata: {\n\t\t\t\tpassword: password\n\t\t\t},\n\t\t\ttype: 'POST',\n\t\t\tsuccess: function(response) {\n\t\t\t\tnc_lastLogin = response.lastLogin;\n\n\t\t\t\tif (_.isFunction(self.callback)) {\n\t\t\t\t\tself.callback();\n\t\t\t\t}\n\t\t\t},\n\t\t\terror: function() {\n\t\t\t\tconfig.error = t('core', 'Failed to authenticate, try again');\n\t\t\t\tOC.PasswordConfirmation.requirePasswordConfirmation(self.callback, config);\n\t\t\t}\n\t\t});\n\t}\n};\n\n$(document).ready(initCore);\n\n\/**\n * Filter Jquery selector by attribute value\n *\/\n$.fn.filterAttr = function(attr_name, attr_value) {\n\treturn this.filter(function() { return $(this).attr(attr_name) === attr_value; });\n};\n\n\/**\n * Returns a human readable file size\n * @param {number} size Size in bytes\n * @param {boolean} skipSmallSizes return '< 1 kB' for small files\n * @return {string}\n *\/\nfunction humanFileSize(size, skipSmallSizes) {\n\tvar humanList = ['B', 'KB', 'MB', 'GB', 'TB'];\n\t\/\/ Calculate Log with base 1024: size = 1024 ** order\n\tvar order = size > 0 ? Math.floor(Math.log(size) \/ Math.log(1024)) : 0;\n\t\/\/ Stay in range of the byte sizes that are defined\n\torder = Math.min(humanList.length - 1, order);\n\tvar readableFormat = humanList[order];\n\tvar relativeSize = (size \/ Math.pow(1024, order)).toFixed(1);\n\tif(skipSmallSizes === true && order === 0) {\n\t\tif(relativeSize !== \"0.0\"){\n\t\t\treturn '< 1 KB';\n\t\t} else {\n\t\t\treturn '0 KB';\n\t\t}\n\t}\n\tif(order < 2){\n\t\trelativeSize = parseFloat(relativeSize).toFixed(0);\n\t}\n\telse if(relativeSize.substr(relativeSize.length-2,2)==='.0'){\n\t\trelativeSize=relativeSize.substr(0,relativeSize.length-2);\n\t}\n\telse{\n\t\trelativeSize = parseFloat(relativeSize).toLocaleString(OC.getCanonicalLocale());\n\t}\n\treturn relativeSize + ' ' + readableFormat;\n}\n\n\/**\n * Format an UNIX timestamp to a human understandable format\n * @param {number} timestamp UNIX timestamp\n * @return {string} Human readable format\n *\/\nfunction formatDate(timestamp){\n\treturn OC.Util.formatDate(timestamp);\n}\n\n\/\/\n\/**\n * Get the value of a URL parameter\n * @link http:\/\/stackoverflow.com\/questions\/1403888\/get-url-parameter-with-jquery\n * @param {string} name URL parameter\n * @return {string}\n *\/\nfunction getURLParameter(name) {\n\treturn decodeURIComponent(\n\t\t(new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(\n\t\t\tlocation.search)||[,''])[1].replace(\/\\+\/g, '%20')\n\t\t)||'';\n}\n\n\/**\n * Takes an absolute timestamp and return a string with a human-friendly relative date\n * @param {number} timestamp A Unix timestamp\n *\/\nfunction relative_modified_date(timestamp) {\n\t\/*\n\t Were multiplying by 1000 to bring the timestamp back to its original value\n\t per https:\/\/github.com\/owncloud\/core\/pull\/10647#discussion_r16790315\n\t *\/\n\treturn OC.Util.relativeModifiedDate(timestamp * 1000);\n}\n\n\/**\n * Utility functions\n * @namespace\n *\/\nOC.Util = {\n\t\/\/ TODO: remove original functions from global namespace\n\thumanFileSize: humanFileSize,\n\n\t\/**\n\t * Returns a file size in bytes from a humanly readable string\n\t * Makes 2kB to 2048.\n\t * Inspired by computerFileSize in helper.php\n\t * @param {string} string file size in human readable format\n\t * @return {number} or null if string could not be parsed\n\t *\n\t *\n\t *\/\n\tcomputerFileSize: function (string) {\n\t\tif (typeof string !== 'string') {\n\t\t\treturn null;\n\t\t}\n\n\t\tvar s = string.toLowerCase().trim();\n\t\tvar bytes = null;\n\n\t\tvar bytesArray = {\n\t\t\t'b' : 1,\n\t\t\t'k' : 1024,\n\t\t\t'kb': 1024,\n\t\t\t'mb': 1024 * 1024,\n\t\t\t'm' : 1024 * 1024,\n\t\t\t'gb': 1024 * 1024 * 1024,\n\t\t\t'g' : 1024 * 1024 * 1024,\n\t\t\t'tb': 1024 * 1024 * 1024 * 1024,\n\t\t\t't' : 1024 * 1024 * 1024 * 1024,\n\t\t\t'pb': 1024 * 1024 * 1024 * 1024 * 1024,\n\t\t\t'p' : 1024 * 1024 * 1024 * 1024 * 1024\n\t\t};\n\n\t\tvar matches = s.match(\/^[\\s+]?([0-9]*)(\\.([0-9]+))?( +)?([kmgtp]?b?)$\/i);\n\t\tif (matches !== null) {\n\t\t\tbytes = parseFloat(s);\n\t\t\tif (!isFinite(bytes)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t\tif (matches[5]) {\n\t\t\tbytes = bytes * bytesArray[matches[5]];\n\t\t}\n\n\t\tbytes = Math.round(bytes);\n\t\treturn bytes;\n\t},\n\n\t\/**\n\t * @param timestamp\n\t * @param format\n\t * @returns {string} timestamp formatted as requested\n\t *\/\n\tformatDate: function (timestamp, format) {\n\t\tformat = format || \"LLL\";\n\t\treturn moment(timestamp).format(format);\n\t},\n\n\t\/**\n\t * @param timestamp\n\t * @returns {string} human readable difference from now\n\t *\/\n\trelativeModifiedDate: function (timestamp) {\n\t\tvar diff = moment().diff(moment(timestamp));\n\t\tif (diff >= 0 && diff < 45000 ) {\n\t\t\treturn t('core', 'seconds ago');\n\t\t}\n\t\treturn moment(timestamp).fromNow();\n\t},\n\n\t\/**\n\t * Returns whether this is IE\n\t *\n\t * @return {bool} true if this is IE, false otherwise\n\t *\/\n\tisIE: function() {\n\t\treturn $('html').hasClass('ie');\n\t},\n\n\t\/**\n\t * Returns the width of a generic browser scrollbar\n\t *\n\t * @return {int} width of scrollbar\n\t *\/\n\tgetScrollBarWidth: function() {\n\t\tif (this._scrollBarWidth) {\n\t\t\treturn this._scrollBarWidth;\n\t\t}\n\n\t\tvar inner = document.createElement('p');\n\t\tinner.style.width = \"100%\";\n\t\tinner.style.height = \"200px\";\n\n\t\tvar outer = document.createElement('div');\n\t\touter.style.position = \"absolute\";\n\t\touter.style.top = \"0px\";\n\t\touter.style.left = \"0px\";\n\t\touter.style.visibility = \"hidden\";\n\t\touter.style.width = \"200px\";\n\t\touter.style.height = \"150px\";\n\t\touter.style.overflow = \"hidden\";\n\t\touter.appendChild (inner);\n\n\t\tdocument.body.appendChild (outer);\n\t\tvar w1 = inner.offsetWidth;\n\t\touter.style.overflow = 'scroll';\n\t\tvar w2 = inner.offsetWidth;\n\t\tif(w1 === w2) {\n\t\t\tw2 = outer.clientWidth;\n\t\t}\n\n\t\tdocument.body.removeChild (outer);\n\n\t\tthis._scrollBarWidth = (w1 - w2);\n\n\t\treturn this._scrollBarWidth;\n\t},\n\n\t\/**\n\t * Remove the time component from a given date\n\t *\n\t * @param {Date} date date\n\t * @return {Date} date with stripped time\n\t *\/\n\tstripTime: function(date) {\n\t\t\/\/ FIXME: likely to break when crossing DST\n\t\t\/\/ would be better to use a library like momentJS\n\t\treturn new Date(date.getFullYear(), date.getMonth(), date.getDate());\n\t},\n\n\t_chunkify: function(t) {\n\t\t\/\/ Adapted from http:\/\/my.opera.com\/GreyWyvern\/blog\/show.dml\/1671288\n\t\tvar tz = [], x = 0, y = -1, n = 0, code, c;\n\n\t\twhile (x < t.length) {\n\t\t\tc = t.charAt(x);\n\t\t\t\/\/ only include the dot in strings\n\t\t\tvar m = ((!n && c === '.') || (c >= '0' && c <= '9'));\n\t\t\tif (m !== n) {\n\t\t\t\t\/\/ next chunk\n\t\t\t\ty++;\n\t\t\t\ttz[y] = '';\n\t\t\t\tn = m;\n\t\t\t}\n\t\t\ttz[y] += c;\n\t\t\tx++;\n\t\t}\n\t\treturn tz;\n\t},\n\t\/**\n\t * Compare two strings to provide a natural sort\n\t * @param a first string to compare\n\t * @param b second string to compare\n\t * @return -1 if b comes before a, 1 if a comes before b\n\t * or 0 if the strings are identical\n\t *\/\n\tnaturalSortCompare: function(a, b) {\n\t\tvar x;\n\t\tvar aa = OC.Util._chunkify(a);\n\t\tvar bb = OC.Util._chunkify(b);\n\n\t\tfor (x = 0; aa[x] && bb[x]; x++) {\n\t\t\tif (aa[x] !== bb[x]) {\n\t\t\t\tvar aNum = Number(aa[x]), bNum = Number(bb[x]);\n\t\t\t\t\/\/ note: == is correct here\n\t\t\t\tif (aNum == aa[x] && bNum == bb[x]) {\n\t\t\t\t\treturn aNum - bNum;\n\t\t\t\t} else {\n\t\t\t\t\t\/\/ Note: This locale setting isn't supported by all browsers but for the ones\n\t\t\t\t\t\/\/ that do there will be more consistency between client-server sorting\n\t\t\t\t\treturn aa[x].localeCompare(bb[x], OC.getLanguage());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn aa.length - bb.length;\n\t},\n\t\/**\n\t * Calls the callback in a given interval until it returns true\n\t * @param {function} callback\n\t * @param {integer} interval in milliseconds\n\t *\/\n\twaitFor: function(callback, interval) {\n\t\tvar internalCallback = function() {\n\t\t\tif(callback() !== true) {\n\t\t\t\tsetTimeout(internalCallback, interval);\n\t\t\t}\n\t\t};\n\n\t\tinternalCallback();\n\t},\n\t\/**\n\t * Checks if a cookie with the given name is present and is set to the provided value.\n\t * @param {string} name name of the cookie\n\t * @param {string} value value of the cookie\n\t * @return {boolean} true if the cookie with the given name has the given value\n\t *\/\n\tisCookieSetToValue: function(name, value) {\n\t\tvar cookies = document.cookie.split(';');\n\t\tfor (var i=0; i < cookies.length; i++) {\n\t\t\tvar cookie = cookies[i].split('=');\n\t\t\tif (cookie[0].trim() === name && cookie[1].trim() === value) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n};\n\n\/**\n * Utility class for the history API,\n * includes fallback to using the URL hash when\n * the browser doesn't support the history API.\n *\n * @namespace\n *\/\nOC.Util.History = {\n\t_handlers: [],\n\n\t\/**\n\t * Push the current URL parameters to the history stack\n\t * and change the visible URL.\n\t * Note: this includes a workaround for IE8\/IE9 that uses\n\t * the hash part instead of the search part.\n\t *\n\t * @param {Object|string} params to append to the URL, can be either a string\n\t * or a map\n\t * @param {string} [url] URL to be used, otherwise the current URL will be used,\n\t * using the params as query string\n\t * @param {boolean} [replace=false] whether to replace instead of pushing\n\t *\/\n\t_pushState: function(params, url, replace) {\n\t\tvar strParams;\n\t\tif (typeof(params) === 'string') {\n\t\t\tstrParams = params;\n\t\t}\n\t\telse {\n\t\t\tstrParams = OC.buildQueryString(params);\n\t\t}\n\t\tif (window.history.pushState) {\n\t\t\turl = url || location.pathname + '?' + strParams;\n\t\t\t\/\/ Workaround for bug with SVG and window.history.pushState on Firefox < 51\n\t\t\t\/\/ https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=652991\n\t\t\tvar isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;\n\t\t\tif (isFirefox && parseInt(navigator.userAgent.split('\/').pop()) < 51) {\n\t\t\t\tvar patterns = document.querySelectorAll('[fill^=\"url(#\"], [stroke^=\"url(#\"], [filter^=\"url(#invert\"]');\n\t\t\t\tfor (var i = 0, ii = patterns.length, pattern; i < ii; i++) {\n\t\t\t\t\tpattern = patterns[i];\n\t\t\t\t\tpattern.style.fill = pattern.style.fill;\n\t\t\t\t\tpattern.style.stroke = pattern.style.stroke;\n\t\t\t\t\tpattern.removeAttribute(\"filter\");\n\t\t\t\t\tpattern.setAttribute(\"filter\", \"url(#invert)\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (replace) {\n\t\t\t\twindow.history.replaceState(params, '', url);\n\t\t\t} else {\n\t\t\t\twindow.history.pushState(params, '', url);\n\t\t\t}\n\t\t}\n\t\t\/\/ use URL hash for IE8\n\t\telse {\n\t\t\twindow.location.hash = '?' + strParams;\n\t\t\t\/\/ inhibit next onhashchange that just added itself\n\t\t\t\/\/ to the event queue\n\t\t\tthis._cancelPop = true;\n\t\t}\n\t},\n\n\t\/**\n\t * Push the current URL parameters to the history stack\n\t * and change the visible URL.\n\t * Note: this includes a workaround for IE8\/IE9 that uses\n\t * the hash part instead of the search part.\n\t *\n\t * @param {Object|string} params to append to the URL, can be either a string\n\t * or a map\n\t * @param {string} [url] URL to be used, otherwise the current URL will be used,\n\t * using the params as query string\n\t *\/\n\tpushState: function(params, url) {\n\t\treturn this._pushState(params, url, false);\n\t},\n\n\t\/**\n\t * Push the current URL parameters to the history stack\n\t * and change the visible URL.\n\t * Note: this includes a workaround for IE8\/IE9 that uses\n\t * the hash part instead of the search part.\n\t *\n\t * @param {Object|string} params to append to the URL, can be either a string\n\t * or a map\n\t * @param {string} [url] URL to be used, otherwise the current URL will be used,\n\t * using the params as query string\n\t *\/\n\treplaceState: function(params, url) {\n\t\treturn this._pushState(params, url, true);\n\t},\n\n\t\/**\n\t * Add a popstate handler\n\t *\n\t * @param handler function\n\t *\/\n\taddOnPopStateHandler: function(handler) {\n\t\tthis._handlers.push(handler);\n\t},\n\n\t\/**\n\t * Parse a query string from the hash part of the URL.\n\t * (workaround for IE8 \/ IE9)\n\t *\/\n\t_parseHashQuery: function() {\n\t\tvar hash = window.location.hash,\n\t\t\tpos = hash.indexOf('?');\n\t\tif (pos >= 0) {\n\t\t\treturn hash.substr(pos + 1);\n\t\t}\n\t\tif (hash.length) {\n\t\t\t\/\/ remove hash sign\n\t\t\treturn hash.substr(1);\n\t\t}\n\t\treturn '';\n\t},\n\n\t_decodeQuery: function(query) {\n\t\treturn query.replace(\/\\+\/g, ' ');\n\t},\n\n\t\/**\n\t * Parse the query\/search part of the URL.\n\t * Also try and parse it from the URL hash (for IE8)\n\t *\n\t * @return map of parameters\n\t *\/\n\tparseUrlQuery: function() {\n\t\tvar query = this._parseHashQuery(),\n\t\t\tparams;\n\t\t\/\/ try and parse from URL hash first\n\t\tif (query) {\n\t\t\tparams = OC.parseQueryString(this._decodeQuery(query));\n\t\t}\n\t\t\/\/ else read from query attributes\n\t\tparams = _.extend(params || {}, OC.parseQueryString(this._decodeQuery(location.search)));\n\t\treturn params || {};\n\t},\n\n\t_onPopState: function(e) {\n\t\tif (this._cancelPop) {\n\t\t\tthis._cancelPop = false;\n\t\t\treturn;\n\t\t}\n\t\tvar params;\n\t\tif (!this._handlers.length) {\n\t\t\treturn;\n\t\t}\n\t\tparams = (e && e.state);\n\t\tif (_.isString(params)) {\n\t\t\tparams = OC.parseQueryString(params);\n\t\t} else if (!params) {\n\t\t\tparams = this.parseUrlQuery() || {};\n\t\t}\n\t\tfor (var i = 0; i < this._handlers.length; i++) {\n\t\t\tthis._handlers[i](params);\n\t\t}\n\t}\n};\n\n\/\/ fallback to hashchange when no history support\nif (window.history.pushState) {\n\twindow.onpopstate = _.bind(OC.Util.History._onPopState, OC.Util.History);\n}\nelse {\n\t$(window).on('hashchange', _.bind(OC.Util.History._onPopState, OC.Util.History));\n}\n\n\/**\n * Get a variable by name\n * @param {string} name\n * @return {*}\n *\/\nOC.get=function(name) {\n\tvar namespaces = name.split(\".\");\n\tvar tail = namespaces.pop();\n\tvar context=window;\n\n\tfor(var i = 0; i < namespaces.length; i++) {\n\t\tcontext = context[namespaces[i]];\n\t\tif(!context){\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn context[tail];\n};\n\n\/**\n * Set a variable by name\n * @param {string} name\n * @param {*} value\n *\/\nOC.set=function(name, value) {\n\tvar namespaces = name.split(\".\");\n\tvar tail = namespaces.pop();\n\tvar context=window;\n\n\tfor(var i = 0; i < namespaces.length; i++) {\n\t\tif(!context[namespaces[i]]){\n\t\t\tcontext[namespaces[i]]={};\n\t\t}\n\t\tcontext = context[namespaces[i]];\n\t}\n\tcontext[tail]=value;\n};\n\n\/\/ fix device width on windows phone\n(function() {\n\tif (\"-ms-user-select\" in document.documentElement.style && navigator.userAgent.match(\/IEMobile\\\/10\\.0\/)) {\n\t\tvar msViewportStyle = document.createElement(\"style\");\n\t\tmsViewportStyle.appendChild(\n\t\t\tdocument.createTextNode(\"@-ms-viewport{width:auto!important}\")\n\t\t);\n\t\tdocument.getElementsByTagName(\"head\")[0].appendChild(msViewportStyle);\n\t}\n})();\n\n\/**\n * Namespace for apps\n * @namespace OCA\n *\/\nwindow.OCA = {};\n\n\/**\n * select a range in an input field\n * @link http:\/\/stackoverflow.com\/questions\/499126\/jquery-set-cursor-position-in-text-area\n * @param {type} start\n * @param {type} end\n *\/\njQuery.fn.selectRange = function(start, end) {\n\treturn this.each(function() {\n\t\tif (this.setSelectionRange) {\n\t\t\tthis.focus();\n\t\t\tthis.setSelectionRange(start, end);\n\t\t} else if (this.createTextRange) {\n\t\t\tvar range = this.createTextRange();\n\t\t\trange.collapse(true);\n\t\t\trange.moveEnd('character', end);\n\t\t\trange.moveStart('character', start);\n\t\t\trange.select();\n\t\t}\n\t});\n};\n\n\/**\n * check if an element exists.\n * allows you to write if ($('#myid').exists()) to increase readability\n * @link http:\/\/stackoverflow.com\/questions\/31044\/is-there-an-exists-function-for-jquery\n *\/\njQuery.fn.exists = function(){\n\treturn this.length > 0;\n};\n\n\/**\n * jQuery tipsy shim for the bootstrap tooltip\n *\/\njQuery.fn.tipsy = function(argument) {\n\tconsole.warn('Deprecation warning: tipsy is deprecated. Use tooltip instead.');\n\tif(typeof argument === 'object' && argument !== null) {\n\n\t\t\/\/ tipsy defaults\n\t\tvar options = {\n\t\t\tplacement: 'bottom',\n\t\t\tdelay: { 'show': 0, 'hide': 0},\n\t\t\ttrigger: 'hover',\n\t\t\thtml: false,\n\t\t\tcontainer: 'body'\n\t\t};\n\t\tif(argument.gravity) {\n\t\t\tswitch(argument.gravity) {\n\t\t\t\tcase 'n':\n\t\t\t\tcase 'nw':\n\t\t\t\tcase 'ne':\n\t\t\t\t\toptions.placement='bottom';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 's':\n\t\t\t\tcase 'sw':\n\t\t\t\tcase 'se':\n\t\t\t\t\toptions.placement='top';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'w':\n\t\t\t\t\toptions.placement='right';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'e':\n\t\t\t\t\toptions.placement='left';\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(argument.trigger) {\n\t\t\toptions.trigger = argument.trigger;\n\t\t}\n\t\tif(argument.delayIn) {\n\t\t\toptions.delay.show = argument.delayIn;\n\t\t}\n\t\tif(argument.delayOut) {\n\t\t\toptions.delay.hide = argument.delayOut;\n\t\t}\n\t\tif(argument.html) {\n\t\t\toptions.html = true;\n\t\t}\n\t\tif(argument.fallback) {\n\t\t\toptions.title = argument.fallback;\n\t\t}\n\t\t\/\/ destroy old tooltip in case the title has changed\n\t\tjQuery.fn.tooltip.call(this, 'destroy');\n\t\tjQuery.fn.tooltip.call(this, options);\n\t} else {\n\t\tthis.tooltip(argument);\n\t\tjQuery.fn.tooltip.call(this, argument);\n\t}\n\treturn this;\n};\n\n\n\/**\n * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>\n *\n * This file is licensed under the Affero General Public License version 3\n * or later.\n *\n * See the COPYING-README file.\n *\n *\/\n\n\/**\n * L10N namespace with localization functions.\n *\n * @namespace\n *\/\nOC.L10N = {\n\t\/**\n\t * String bundles with app name as key.\n\t * @type {Object.<String,String>}\n\t *\/\n\t_bundles: {},\n\n\t\/**\n\t * Plural functions, key is app name and value is function.\n\t * @type {Object.<String,Function>}\n\t *\/\n\t_pluralFunctions: {},\n\n\t\/**\n\t * Load an app's translation bundle if not loaded already.\n\t *\n\t * @param {String} appName name of the app\n\t * @param {Function} callback callback to be called when\n\t * the translations are loaded\n\t * @return {Promise} promise\n\t *\/\n\tload: function(appName, callback) {\n\t\t\/\/ already available ?\n\t\tif (this._bundles[appName] || OC.getLocale() === 'en') {\n\t\t\tvar deferred = $.Deferred();\n\t\t\tvar promise = deferred.promise();\n\t\t\tpromise.then(callback);\n\t\t\tdeferred.resolve();\n\t\t\treturn promise;\n\t\t}\n\n\t\tvar self = this;\n\t\tvar url = OC.filePath(appName, 'l10n', OC.getLocale() + '.json');\n\n\t\t\/\/ load JSON translation bundle per AJAX\n\t\treturn $.get(url)\n\t\t\t.then(\n\t\t\t\tfunction(result) {\n\t\t\t\t\tif (result.translations) {\n\t\t\t\t\t\tself.register(appName, result.translations, result.pluralForm);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t.then(callback);\n\t},\n\n\t\/**\n\t * Register an app's translation bundle.\n\t *\n\t * @param {String} appName name of the app\n\t * @param {Object<String,String>} bundle\n\t * @param {Function|String} [pluralForm] optional plural function or plural string\n\t *\/\n\tregister: function(appName, bundle, pluralForm) {\n\t\tvar self = this;\n\t\tif (_.isUndefined(this._bundles[appName])) {\n\t\t\tthis._bundles[appName] = bundle || {};\n\n\t\t\t\/\/ generate plural function based on form\n\t\t\tthis._pluralFunctions[appName] = this._getPlural;\n\t\t} else {\n\t\t\t\/\/ Theme overwriting the default language\n\t\t\t_.extend(self._bundles[appName], bundle);\n\t\t}\n\t},\n\n\t\/**\n\t * Translate a string\n\t * @param {string} app the id of the app for which to translate the string\n\t * @param {string} text the string to translate\n\t * @param [vars] map of placeholder key to value\n\t * @param {number} [count] number to replace %n with\n\t * @param {array} [options] options array\n\t * @param {bool} [options.escape=true] enable\/disable auto escape of placeholders (by default enabled)\n\t * @return {string}\n\t *\/\n\ttranslate: function(app, text, vars, count, options) {\n\t\tvar defaultOptions = {\n\t\t\t\tescape: true\n\t\t\t},\n\t\t\tallOptions = options || {};\n\t\t_.defaults(allOptions, defaultOptions);\n\n\t\t\/\/ TODO: cache this function to avoid inline recreation\n\t\t\/\/ of the same function over and over again in case\n\t\t\/\/ translate() is used in a loop\n\t\tvar _build = function (text, vars, count) {\n\t\t\treturn text.replace(\/%n\/g, count).replace(\/{([^{}]*)}\/g,\n\t\t\t\tfunction (a, b) {\n\t\t\t\t\tvar r = vars[b];\n\t\t\t\t\tif(typeof r === 'string' || typeof r === 'number') {\n\t\t\t\t\t\tif(allOptions.escape) {\n\t\t\t\t\t\t\treturn DOMPurify.sanitize(escapeHTML(r));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn DOMPurify.sanitize(r);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn DOMPurify.sanitize(a);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\t\t};\n\t\tvar translation = text;\n\t\tvar bundle = this._bundles[app] || {};\n\t\tvar value = bundle[text];\n\t\tif( typeof(value) !== 'undefined' ){\n\t\t\ttranslation = value;\n\t\t}\n\n\t\tif(typeof vars === 'object' || count !== undefined ) {\n\t\t\treturn DOMPurify.sanitize(_build(translation, vars, count));\n\t\t} else {\n\t\t\treturn DOMPurify.sanitize(translation);\n\t\t}\n\t},\n\n\t\/**\n\t * Translate a plural string\n\t * @param {string} app the id of the app for which to translate the string\n\t * @param {string} textSingular the string to translate for exactly one object\n\t * @param {string} textPlural the string to translate for n objects\n\t * @param {number} count number to determine whether to use singular or plural\n\t * @param [vars] map of placeholder key to value\n\t * @param {array} [options] options array\n\t * @param {bool} [options.escape=true] enable\/disable auto escape of placeholders (by default enabled)\n\t * @return {string} Translated string\n\t *\/\n\ttranslatePlural: function(app, textSingular, textPlural, count, vars, options) {\n\t\tvar identifier = '_' + textSingular + '_::_' + textPlural + '_';\n\t\tvar bundle = this._bundles[app] || {};\n\t\tvar value = bundle[identifier];\n\t\tif( typeof(value) !== 'undefined' ){\n\t\t\tvar translation = value;\n\t\t\tif ($.isArray(translation)) {\n\t\t\t\tvar plural = this._pluralFunctions[app](count);\n\t\t\t\treturn this.translate(app, translation[plural], vars, count, options);\n\t\t\t}\n\t\t}\n\n\t\tif(count === 1) {\n\t\t\treturn this.translate(app, textSingular, vars, count, options);\n\t\t}\n\t\telse{\n\t\t\treturn this.translate(app, textPlural, vars, count, options);\n\t\t}\n\t},\n\n\t\/**\n\t * The plural function taken from symfony\n\t *\n\t * @param {number} number\n\t * @returns {number}\n\t * @private\n\t *\/\n\t_getPlural: function(number) {\n\t\tvar locale = OC.getLocale();\n\t\tif ('pt_BR' === locale) {\n\t\t\t\/\/ temporary set a locale for brazilian\n\t\t\tlocale = 'xbr';\n\t\t}\n\n\t\tif (typeof locale === 'undefined') {\n\t\t\treturn (1 == number) ? 0 : 1;\n\t\t}\n\n\t\tif (locale.length > 3) {\n\t\t\tlocale = locale.substring(0, locale.lastIndexOf('_'));\n\t\t}\n\n\t\t\/*\n\t\t * The plural rules are derived from code of the Zend Framework (2010-09-25),\n\t\t * which is subject to the new BSD license (http:\/\/framework.zend.com\/license\/new-bsd).\n\t\t * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http:\/\/www.zend.com)\n\t\t *\/\n\t\tswitch (locale) {\n\t\t\tcase 'az':\n\t\t\tcase 'bo':\n\t\t\tcase 'dz':\n\t\t\tcase 'id':\n\t\t\tcase 'ja':\n\t\t\tcase 'jv':\n\t\t\tcase 'ka':\n\t\t\tcase 'km':\n\t\t\tcase 'kn':\n\t\t\tcase 'ko':\n\t\t\tcase 'ms':\n\t\t\tcase 'th':\n\t\t\tcase 'tr':\n\t\t\tcase 'vi':\n\t\t\tcase 'zh':\n\t\t\t\treturn 0;\n\n\t\t\tcase 'af':\n\t\t\tcase 'bn':\n\t\t\tcase 'bg':\n\t\t\tcase 'ca':\n\t\t\tcase 'da':\n\t\t\tcase 'de':\n\t\t\tcase 'el':\n\t\t\tcase 'en':\n\t\t\tcase 'eo':\n\t\t\tcase 'es':\n\t\t\tcase 'et':\n\t\t\tcase 'eu':\n\t\t\tcase 'fa':\n\t\t\tcase 'fi':\n\t\t\tcase 'fo':\n\t\t\tcase 'fur':\n\t\t\tcase 'fy':\n\t\t\tcase 'gl':\n\t\t\tcase 'gu':\n\t\t\tcase 'ha':\n\t\t\tcase 'he':\n\t\t\tcase 'hu':\n\t\t\tcase 'is':\n\t\t\tcase 'it':\n\t\t\tcase 'ku':\n\t\t\tcase 'lb':\n\t\t\tcase 'ml':\n\t\t\tcase 'mn':\n\t\t\tcase 'mr':\n\t\t\tcase 'nah':\n\t\t\tcase 'nb':\n\t\t\tcase 'ne':\n\t\t\tcase 'nl':\n\t\t\tcase 'nn':\n\t\t\tcase 'no':\n\t\t\tcase 'oc':\n\t\t\tcase 'om':\n\t\t\tcase 'or':\n\t\t\tcase 'pa':\n\t\t\tcase 'pap':\n\t\t\tcase 'ps':\n\t\t\tcase 'pt':\n\t\t\tcase 'so':\n\t\t\tcase 'sq':\n\t\t\tcase 'sv':\n\t\t\tcase 'sw':\n\t\t\tcase 'ta':\n\t\t\tcase 'te':\n\t\t\tcase 'tk':\n\t\t\tcase 'ur':\n\t\t\tcase 'zu':\n\t\t\t\treturn (1 == number) ? 0 : 1;\n\n\t\t\tcase 'am':\n\t\t\tcase 'bh':\n\t\t\tcase 'fil':\n\t\t\tcase 'fr':\n\t\t\tcase 'gun':\n\t\t\tcase 'hi':\n\t\t\tcase 'hy':\n\t\t\tcase 'ln':\n\t\t\tcase 'mg':\n\t\t\tcase 'nso':\n\t\t\tcase 'xbr':\n\t\t\tcase 'ti':\n\t\t\tcase 'wa':\n\t\t\t\treturn ((0 == number) || (1 == number)) ? 0 : 1;\n\n\t\t\tcase 'be':\n\t\t\tcase 'bs':\n\t\t\tcase 'hr':\n\t\t\tcase 'ru':\n\t\t\tcase 'sh':\n\t\t\tcase 'sr':\n\t\t\tcase 'uk':\n\t\t\t\treturn ((1 == number % 10) && (11 != number % 100)) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2);\n\n\t\t\tcase 'cs':\n\t\t\tcase 'sk':\n\t\t\t\treturn (1 == number) ? 0 : (((number >= 2) && (number <= 4)) ? 1 : 2);\n\n\t\t\tcase 'ga':\n\t\t\t\treturn (1 == number) ? 0 : ((2 == number) ? 1 : 2);\n\n\t\t\tcase 'lt':\n\t\t\t\treturn ((1 == number % 10) && (11 != number % 100)) ? 0 : (((number % 10 >= 2) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2);\n\n\t\t\tcase 'sl':\n\t\t\t\treturn (1 == number % 100) ? 0 : ((2 == number % 100) ? 1 : (((3 == number % 100) || (4 == number % 100)) ? 2 : 3));\n\n\t\t\tcase 'mk':\n\t\t\t\treturn (1 == number % 10) ? 0 : 1;\n\n\t\t\tcase 'mt':\n\t\t\t\treturn (1 == number) ? 0 : (((0 == number) || ((number % 100 > 1) && (number % 100 < 11))) ? 1 : (((number % 100 > 10) && (number % 100 < 20)) ? 2 : 3));\n\n\t\t\tcase 'lv':\n\t\t\t\treturn (0 == number) ? 0 : (((1 == number % 10) && (11 != number % 100)) ? 1 : 2);\n\n\t\t\tcase 'pl':\n\t\t\t\treturn (1 == number) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 12) || (number % 100 > 14))) ? 1 : 2);\n\n\t\t\tcase 'cy':\n\t\t\t\treturn (1 == number) ? 0 : ((2 == number) ? 1 : (((8 == number) || (11 == number)) ? 2 : 3));\n\n\t\t\tcase 'ro':\n\t\t\t\treturn (1 == number) ? 0 : (((0 == number) || ((number % 100 > 0) && (number % 100 < 20))) ? 1 : 2);\n\n\t\t\tcase 'ar':\n\t\t\t\treturn (0 == number) ? 0 : ((1 == number) ? 1 : ((2 == number) ? 2 : (((number % 100 >= 3) && (number % 100 <= 10)) ? 3 : (((number % 100 >= 11) && (number % 100 <= 99)) ? 4 : 5))));\n\n\t\t\tdefault:\n\t\t\t\treturn 0;\n\t\t}\n\t}\n};\n\n\/**\n * translate a string\n * @param {string} app the id of the app for which to translate the string\n * @param {string} text the string to translate\n * @param [vars] map of placeholder key to value\n * @param {number} [count] number to replace %n with\n * @return {string}\n *\/\nwindow.t = _.bind(OC.L10N.translate, OC.L10N);\n\n\/**\n * translate a string\n * @param {string} app the id of the app for which to translate the string\n * @param {string} text_singular the string to translate for exactly one object\n * @param {string} text_plural the string to translate for n objects\n * @param {number} count number to determine whether to use singular or plural\n * @param [vars] map of placeholder key to value\n * @return {string} Translated string\n *\/\nwindow.n = _.bind(OC.L10N.translatePlural, OC.L10N);\n\nHandlebars.registerHelper('t', function(app, text) {\n\treturn OC.L10N.translate(app, text);\n});\n\n\n\n\/**\n * jQuery plugin for micro templates\n *\n * Strings are automatically escaped, but that can be disabled by setting\n * escapeFunction to null.\n *\n * Usage examples:\n *\n *\tvar htmlStr = '<p>Bake, uncovered, until the {greasystuff} is melted and the {pasta} is heated through, about {min} minutes.<\/p>'\n *\t$(htmlStr).octemplate({greasystuff: 'cheese', pasta: 'macaroni', min: 10});\n *\n * \tvar htmlStr = '<p>Welcome back {user}<\/p>';\n *\t$(htmlStr).octemplate({user: 'John Q. Public'}, {escapeFunction: null});\n *\n * Be aware that the target string must be wrapped in an HTML element for the\n * plugin to work. The following won't work:\n * \n * var textStr = 'Welcome back {user}';\n * $(textStr).octemplate({user: 'John Q. Public'});\n *\n * For anything larger than one-liners, you can use a simple $.get() ajax\n * request to get the template, or you can embed them it the page using the\n * text\/template type:\n *\n * <script id=\"contactListItemTemplate\" type=\"text\/template\">\n *\t<tr class=\"contact\" data-id=\"{id}\">\n *\t\t<td class=\"name\">\n *\t\t\t<input type=\"checkbox\" name=\"id\" value=\"{id}\" \/><span class=\"nametext\">{name}<\/span>\n *\t\t<\/td>\n *\t\t<td class=\"email\">\n *\t\t\t<a href=\"mailto:{email}\">{email}<\/a>\n *\t\t<\/td>\n *\t\t<td class=\"phone\">{phone}<\/td>\n *\t<\/tr>\n * <\/script>\n *\n * var $tmpl = $('#contactListItemTemplate');\n * var contacts = \/\/ fetched in some ajax call\n *\n * $.each(contacts, function(idx, contact) {\n * \t\t$contactList.append(\n * \t\t\t$tmpl.octemplate({\n * \t\t\t\tid: contact.getId(),\n * \t\t\t\tname: contact.getDisplayName(),\n * \t\t\t\temail: contact.getPreferredEmail(),\n * \t\t\t\tphone: contact.getPreferredPhone(),\n * \t\t\t});\n * \t\t);\n * });\n *\/\n(function( $ ) {\n\t\/**\n\t* Object Template\n\t* Inspired by micro templating done by e.g. underscore.js\n\t*\/\n\tvar Template = {\n\t\tinit: function(vars, options, elem) {\n\t\t\t\/\/ Mix in the passed in options with the default options\n\t\t\tthis.vars = vars;\n\t\t\tthis.options = $.extend({},this.options,options);\n\n\t\t\tthis.elem = elem;\n\t\t\tvar self = this;\n\n\t\t\tif(typeof this.options.escapeFunction === 'function') {\n\t\t\t\tvar keys = Object.keys(this.vars);\n\t\t\t\tfor (var key = 0; key < keys.length; key++) {\n\t\t\t\t\tif(typeof this.vars[keys[key]] === 'string') {\n\t\t\t\t\t\tthis.vars[keys[key]] = self.options.escapeFunction(this.vars[keys[key]]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar _html = this._build(this.vars);\n\t\t\treturn $(_html);\n\t\t},\n\t\t\/\/ From stackoverflow.com\/questions\/1408289\/best-way-to-do-variable-interpolation-in-javascript\n\t\t_build: function(o){\n\t\t\tvar data = this.elem.attr('type') === 'text\/template' ? this.elem.html() : this.elem.get(0).outerHTML;\n\t\t\ttry {\n\t\t\t\treturn data.replace(\/{([^{}]*)}\/g,\n\t\t\t\t\tfunction (a, b) {\n\t\t\t\t\t\tvar r = o[b];\n\t\t\t\t\t\treturn typeof r === 'string' || typeof r === 'number' ? r : a;\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t} catch(e) {\n\t\t\t\tconsole.error(e, 'data:', data);\n\t\t\t}\n\t\t},\n\t\toptions: {\n\t\t\tescapeFunction: escapeHTML\n\t\t}\n\t};\n\n\t$.fn.octemplate = function(vars, options) {\n\t\tvars = vars || {};\n\t\tif(this.length) {\n\t\t\tvar _template = Object.create(Template);\n\t\t\treturn _template.init(vars, options, this);\n\t\t}\n\t};\n\n})( jQuery );\n\n\n\n\/**\n * ownCloud\n *\n * @author Robin Appelman\n * @copyright 2012 Robin Appelman icewind1991@gmail.com\n *\n * This library is free software; you can redistribute it and\/or\n * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE\n * License as published by the Free Software Foundation; either\n * version 3 of the License, or any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU AFFERO GENERAL PUBLIC LICENSE for more details.\n *\n * You should have received a copy of the GNU Affero General Public\n * License along with this library. If not, see <http:\/\/www.gnu.org\/licenses\/>.\n *\n *\/\n\n\/**\n * Wrapper for server side events\n * (http:\/\/en.wikipedia.org\/wiki\/Server-sent_events)\n * includes a fallback for older browsers and IE\n *\n * use server side events with caution, too many open requests can hang the\n * server\n *\/\n\n\/* global EventSource *\/\n\n\/**\n * Create a new event source\n * @param {string} src\n * @param {object} [data] to be send as GET\n *\n * @constructs OC.EventSource\n *\/\nOC.EventSource=function(src,data){\n\tvar dataStr='';\n\tvar name;\n\tvar joinChar;\n\tthis.typelessListeners=[];\n\tthis.closed = false;\n\tthis.listeners={};\n\tif(data){\n\t\tfor(name in data){\n\t\t\tdataStr+=name+'='+encodeURIComponent(data[name])+'&';\n\t\t}\n\t}\n\tdataStr+='requesttoken='+encodeURIComponent(oc_requesttoken);\n\tif(!this.useFallBack && typeof EventSource !== 'undefined'){\n\t\tjoinChar = '&';\n\t\tif(src.indexOf('?') === -1) {\n\t\t\tjoinChar = '?';\n\t\t}\n\t\tthis.source= new EventSource(src+joinChar+dataStr);\n\t\tthis.source.onmessage=function(e){\n\t\t\tfor(var i=0;i<this.typelessListeners.length;i++){\n\t\t\t\tthis.typelessListeners[i](JSON.parse(e.data));\n\t\t\t}\n\t\t}.bind(this);\n\t}else{\n\t\tvar iframeId='oc_eventsource_iframe_'+OC.EventSource.iframeCount;\n\t\tOC.EventSource.fallBackSources[OC.EventSource.iframeCount]=this;\n\t\tthis.iframe=$('<iframe\/>');\n\t\tthis.iframe.attr('id',iframeId);\n\t\tthis.iframe.hide();\n\n\t\tjoinChar = '&';\n\t\tif(src.indexOf('?') === -1) {\n\t\t\tjoinChar = '?';\n\t\t}\n\t\tthis.iframe.attr('src',src+joinChar+'fallback=true&fallback_id='+OC.EventSource.iframeCount+'&'+dataStr);\n\t\t$('body').append(this.iframe);\n\t\tthis.useFallBack=true;\n\t\tOC.EventSource.iframeCount++;\n\t}\n\t\/\/add close listener\n\tthis.listen('__internal__',function(data){\n\t\tif(data === 'close'){\n\t\t\tthis.close();\n\t\t}\n\t}.bind(this));\n};\nOC.EventSource.fallBackSources=[];\nOC.EventSource.iframeCount=0;\/\/number of fallback iframes\nOC.EventSource.fallBackCallBack=function(id,type,data){\n\tOC.EventSource.fallBackSources[id].fallBackCallBack(type,data);\n};\nOC.EventSource.prototype={\n\ttypelessListeners:[],\n\tiframe:null,\n\tlisteners:{},\/\/only for fallback\n\tuseFallBack:false,\n\t\/**\n\t * Fallback callback for browsers that don't have the\n\t * native EventSource object.\n\t *\n\t * Calls the registered listeners.\n\t *\n\t * @private\n\t * @param {String} type event type\n\t * @param {Object} data received data\n\t *\/\n\tfallBackCallBack:function(type,data){\n\t\tvar i;\n\t\t\/\/ ignore messages that might appear after closing\n\t\tif (this.closed) {\n\t\t\treturn;\n\t\t}\n\t\tif(type){\n\t\t\tif (typeof this.listeners.done !== 'undefined') {\n\t\t\t\tfor(i=0;i<this.listeners[type].length;i++){\n\t\t\t\t\tthis.listeners[type][i](data);\n\t\t\t\t}\n\t\t\t}\n\t\t}else{\n\t\t\tfor(i=0;i<this.typelessListeners.length;i++){\n\t\t\t\tthis.typelessListeners[i](data);\n\t\t\t}\n\t\t}\n\t},\n\tlastLength:0,\/\/for fallback\n\t\/**\n\t * Listen to a given type of events.\n\t *\n\t * @param {String} type event type\n\t * @param {Function} callback event callback\n\t *\/\n\tlisten:function(type,callback){\n\t\tif(callback && callback.call){\n\n\t\t\tif(type){\n\t\t\t\tif(this.useFallBack){\n\t\t\t\t\tif(!this.listeners[type]){\n\t\t\t\t\t\tthis.listeners[type]=[];\n\t\t\t\t\t}\n\t\t\t\t\tthis.listeners[type].push(callback);\n\t\t\t\t}else{\n\t\t\t\t\tthis.source.addEventListener(type,function(e){\n\t\t\t\t\t\tif (typeof e.data !== 'undefined') {\n\t\t\t\t\t\t\tcallback(JSON.parse(e.data));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcallback('');\n\t\t\t\t\t\t}\n\t\t\t\t\t},false);\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tthis.typelessListeners.push(callback);\n\t\t\t}\n\t\t}\n\t},\n\t\/**\n\t * Closes this event source.\n\t *\/\n\tclose:function(){\n\t\tthis.closed = true;\n\t\tif (typeof this.source !== 'undefined') {\n\t\t\tthis.source.close();\n\t\t}\n\t}\n};\n\n\n\/**\n * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>\n *\n * @license GNU AGPL version 3 or any later version\n *\n * This program is free software: you can redistribute it and\/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http:\/\/www.gnu.org\/licenses\/>.\n *\n *\/\n\n\/**\n * @namespace\n * @since 11.0.0\n *\/\nOCP.AppConfig = {\n\t\/**\n\t * @param {string} method\n\t * @param {string} endpoint\n\t * @param {Object} [options]\n\t * @param {Object} [options.data]\n\t * @param {function} [options.success]\n\t * @param {function} [options.error]\n\t * @internal\n\t *\/\n\t_call: function(method, endpoint, options) {\n\t\tif ((method === 'post' || method === 'delete') && OC.PasswordConfirmation.requiresPasswordConfirmation()) {\n\t\t\tOC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this._call, this, method, endpoint, options));\n\t\t\treturn;\n\t\t}\n\n\t\toptions = options || {};\n\t\t$.ajax({\n\t\t\ttype: method.toUpperCase(),\n\t\t\turl: OC.linkToOCS('apps\/provisioning_api\/api\/v1', 2) + 'config\/apps' + endpoint,\n\t\t\tdata: options.data || {},\n\t\t\tsuccess: options.success,\n\t\t\terror: options.error\n\t\t});\n\t},\n\n\t\/**\n\t * @param {Object} [options]\n\t * @param {function} [options.success]\n\t * @since 11.0.0\n\t *\/\n\tgetApps: function(options) {\n\t\tthis._call('get', '', options);\n\t},\n\n\t\/**\n\t * @param {string} app\n\t * @param {Object} [options]\n\t * @param {function} [options.success]\n\t * @param {function} [options.error]\n\t * @since 11.0.0\n\t *\/\n\tgetKeys: function(app, options) {\n\t\tthis._call('get', '\/' + app, options);\n\t},\n\n\t\/**\n\t * @param {string} app\n\t * @param {string} key\n\t * @param {string|function} defaultValue\n\t * @param {Object} [options]\n\t * @param {function} [options.success]\n\t * @param {function} [options.error]\n\t * @since 11.0.0\n\t *\/\n\tgetValue: function(app, key, defaultValue, options) {\n\t\toptions = options || {};\n\t\toptions.data = {\n\t\t\tdefaultValue: defaultValue\n\t\t};\n\n\t\tthis._call('get', '\/' + app + '\/' + key, options);\n\t},\n\n\t\/**\n\t * @param {string} app\n\t * @param {string} key\n\t * @param {string} value\n\t * @param {Object} [options]\n\t * @param {function} [options.success]\n\t * @param {function} [options.error]\n\t * @since 11.0.0\n\t *\/\n\tsetValue: function(app, key, value, options) {\n\t\toptions = options || {};\n\t\toptions.data = {\n\t\t\tvalue: value\n\t\t};\n\n\t\tthis._call('post', '\/' + app + '\/' + key, options);\n\t},\n\n\t\/**\n\t * @param {string} app\n\t * @param {string} key\n\t * @param {Object} [options]\n\t * @param {function} [options.success]\n\t * @param {function} [options.error]\n\t * @since 11.0.0\n\t *\/\n\tdeleteKey: function(app, key, options) {\n\t\tthis._call('delete', '\/' + app + '\/' + key, options);\n\t}\n};\n\n\n\/**\n * @copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>\n *\n * @author Arthur Schiwon <blizzz@arthur-schiwon.de>\n *\n * This file is licensed under the Affero General Public License version 3 or\n * later. See the COPYING file.\n *\/\n\n(function(OCP) {\n\t\"use strict\";\n\n\tOCP.Comments = {\n\n\t\t\/*\n\t\t * Detects links:\n\t\t * Either the http(s) protocol is given or two strings, basically limited to ascii with the last\n\t\t * \tword being at least one digit long,\n\t\t * followed by at least another character\n\t\t *\n\t\t * The downside: anything not ascii is excluded. Not sure how common it is in areas using different\n\t\t * alphabets\u2026 the upside: fake domains with similar looking characters won't be formatted as links\n\t\t *\/\n\t\turlRegex: \/(\\s|^)(https?:\\\/\\\/)?((?:[-A-Z0-9+_]+\\.)+[-A-Z]+(?:\\\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\\s|$)\/ig,\n\n\t\tplainToRich: function(content) {\n\t\t\tcontent = this.formatLinksRich(content);\n\t\t\treturn content;\n\t\t},\n\n\t\trichToPlain: function(content) {\n\t\t\tcontent = this.formatLinksPlain(content);\n\t\t\treturn content;\n\t\t},\n\n\t\tformatLinksRich: function(content) {\n\t\t\treturn content.replace(this.urlRegex, function(_, leadingSpace, protocol, url, trailingSpace) {\n\t\t\t\tvar linkText = url;\n\t\t\t\tif(!protocol) {\n\t\t\t\t\tprotocol = 'https:\/\/';\n\t\t\t\t} else if (protocol === 'http:\/\/'){\n\t\t\t\t\tlinkText = protocol + url;\n\t\t\t\t}\n\n\t\t\t\treturn leadingSpace + '<a class=\"external\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"' + protocol + url + '\">' + linkText + '<\/a>' + trailingSpace;\n\t\t\t});\n\t\t},\n\n\t\tformatLinksPlain: function(content) {\n\t\t\tvar $content = $('<div><\/div>').html(content);\n\t\t\t$content.find('a').each(function () {\n\t\t\t\tvar $this = $(this);\n\t\t\t\t$this.html($this.attr('href'));\n\t\t\t});\n\t\t\treturn $content.html();\n\t\t}\n\n\t};\n})(OCP);\n\n\n\/**\n * @copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>\n *\n * @author Arthur Schiwon <blizzz@arthur-schiwon.de>\n *\n * This file is licensed under the Affero General Public License version 3 or\n * later. See the COPYING file.\n *\/\n\n(function(OCP) {\n\t\"use strict\";\n\n\tOCP.WhatsNew = {\n\n\t\tquery: function(options) {\n\t\t\toptions = options || {};\n\t\t\tvar dismissOptions = options.dismiss || {};\n\t\t\t$.ajax({\n\t\t\t\ttype: 'GET',\n\t\t\t\turl: options.url || OC.linkToOCS('core', 2) + 'whatsnew?format=json',\n\t\t\t\tsuccess: options.success || function(data, statusText, xhr) {\n\t\t\t\t\tOCP.WhatsNew._onQuerySuccess(data, statusText, xhr, dismissOptions);\n\t\t\t\t},\n\t\t\t\terror: options.error || this._onQueryError\n\t\t\t});\n\t\t},\n\n\t\tdismiss: function(version, options) {\n\t\t\toptions = options || {};\n\t\t\t$.ajax({\n\t\t\t\ttype: 'POST',\n\t\t\t\turl: options.url || OC.linkToOCS('core', 2) + 'whatsnew',\n\t\t\t\tdata: {version: encodeURIComponent(version)},\n\t\t\t\tsuccess: options.success || this._onDismissSuccess,\n\t\t\t\terror: options.error || this._onDismissError\n\t\t\t});\n\t\t\t\/\/ remove element immediately\n\t\t\t$('.whatsNewPopover').remove();\n\t\t},\n\n\t\t_onQuerySuccess: function(data, statusText, xhr, dismissOptions) {\n\t\t\tconsole.debug('querying Whats New data was successful: ' + statusText);\n\t\t\tconsole.debug(data);\n\n\t\t\tif(xhr.status !== 200) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar item, menuItem, text, icon;\n\n\t\t\tvar div = document.createElement('div');\n\t\t\tdiv.classList.add('popovermenu', 'open', 'whatsNewPopover', 'menu-left');\n\n\t\t\tvar list = document.createElement('ul');\n\n\t\t\t\/\/ header\n\t\t\titem = document.createElement('li');\n\t\t\tmenuItem = document.createElement('span');\n\t\t\tmenuItem.className = \"menuitem\";\n\n\t\t\ttext = document.createElement('span');\n\t\t\ttext.innerText = t('core', 'New in') + ' ' + data['ocs']['data']['product'];\n\t\t\ttext.className = 'caption';\n\t\t\tmenuItem.appendChild(text);\n\n\t\t\ticon = document.createElement('span');\n\t\t\ticon.className = 'icon-close';\n\t\t\ticon.onclick = function () {\n\t\t\t\tOCP.WhatsNew.dismiss(data['ocs']['data']['version'], dismissOptions);\n\t\t\t};\n\t\t\tmenuItem.appendChild(icon);\n\n\t\t\titem.appendChild(menuItem);\n\t\t\tlist.appendChild(item);\n\n\t\t\t\/\/ Highlights\n\t\t\tfor (var i in data['ocs']['data']['whatsNew']['regular']) {\n\t\t\t\tvar whatsNewTextItem = data['ocs']['data']['whatsNew']['regular'][i];\n\t\t\t\titem = document.createElement('li');\n\n\t\t\t\tmenuItem = document.createElement('span');\n\t\t\t\tmenuItem.className = \"menuitem\";\n\n\t\t\t\ticon = document.createElement('span');\n\t\t\t\ticon.className = 'icon-checkmark';\n\t\t\t\tmenuItem.appendChild(icon);\n\n\t\t\t\ttext = document.createElement('p');\n\t\t\t\ttext.innerHTML = _.escape(whatsNewTextItem);\n\t\t\t\tmenuItem.appendChild(text);\n\n\t\t\t\titem.appendChild(menuItem);\n\t\t\t\tlist.appendChild(item);\n\t\t\t}\n\n\t\t\t\/\/ Changelog URL\n\t\t\tif(!_.isUndefined(data['ocs']['data']['changelogURL'])) {\n\t\t\t\titem = document.createElement('li');\n\n\t\t\t\tmenuItem = document.createElement('a');\n\t\t\t\tmenuItem.href = data['ocs']['data']['changelogURL'];\n\t\t\t\tmenuItem.rel = 'noreferrer noopener';\n\t\t\t\tmenuItem.target = '_blank';\n\n\t\t\t\ticon = document.createElement('span');\n\t\t\t\ticon.className = 'icon-link';\n\t\t\t\tmenuItem.appendChild(icon);\n\n\t\t\t\ttext = document.createElement('span');\n\t\t\t\ttext.innerText = t('core', 'View changelog');\n\t\t\t\tmenuItem.appendChild(text);\n\n\t\t\t\titem.appendChild(menuItem);\n\t\t\t\tlist.appendChild(item);\n\t\t\t}\n\n\t\t\tdiv.appendChild(list);\n\t\t\tdocument.body.appendChild(div);\n\t\t},\n\n\t\t_onQueryError: function (x, t, e) {\n\t\t\tconsole.debug('querying Whats New Data resulted in an error: ' + t + e);\n\t\t\tconsole.debug(x);\n\t\t},\n\n\t\t_onDismissSuccess: function(data) {\n\t\t\t\/\/noop\n\t\t},\n\n\t\t_onDismissError: function (data) {\n\t\t\tconsole.debug('dismissing Whats New data resulted in an error: ' + data);\n\t\t}\n\t};\n})(OCP);\n\n\n\/**\n * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>\n *\n * @license GNU AGPL version 3 or any later version\n *\n * This program is free software: you can redistribute it and\/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http:\/\/www.gnu.org\/licenses\/>.\n *\n *\/\n\n\/**\n * @namespace\n * @deprecated Use OCP.AppConfig instead\n *\/\nOC.AppConfig={\n\t\/**\n\t * @deprecated Use OCP.AppConfig.getValue() instead\n\t *\/\n\tgetValue:function(app,key,defaultValue,callback){\n\t\tOCP.AppConfig.getValue(app, key, defaultValue, {\n\t\t\tsuccess: callback\n\t\t});\n\t},\n\n\t\/**\n\t * @deprecated Use OCP.AppConfig.setValue() instead\n\t *\/\n\tsetValue:function(app,key,value){\n\t\tOCP.AppConfig.setValue(app, key, value);\n\t},\n\n\t\/**\n\t * @deprecated Use OCP.AppConfig.getApps() instead\n\t *\/\n\tgetApps:function(callback){\n\t\tOCP.AppConfig.getApps({\n\t\t\tsuccess: callback\n\t\t});\n\t},\n\n\t\/**\n\t * @deprecated Use OCP.AppConfig.getKeys() instead\n\t *\/\n\tgetKeys:function(app,callback){\n\t\tOCP.AppConfig.getKeys(app, {\n\t\t\tsuccess: callback\n\t\t});\n\t},\n\n\t\/**\n\t * @deprecated Use OCP.AppConfig.deleteKey() instead\n\t *\/\n\tdeleteKey:function(app,key){\n\t\tOCP.AppConfig.deleteKey(app, key);\n\t}\n};\n\n\n$(document).on('ajaxSend',function(elm, xhr, settings) {\n\tif(settings.crossDomain === false) {\n\t\txhr.setRequestHeader('requesttoken', oc_requesttoken);\n\t\txhr.setRequestHeader('OCS-APIREQUEST', 'true');\n\t}\n});\n\n\n\/**\n * ownCloud - core\n *\n * This file is licensed under the Affero General Public License version 3 or\n * later. See the COPYING file.\n *\n * @author Bernhard Posselt <dev@bernhard-posselt.com>\n * @copyright Bernhard Posselt 2014\n *\/\n\n(function (document, $, exports) {\n\n\t'use strict';\n\n\tvar dynamicSlideToggleEnabled = false;\n\n\texports.Apps = {\n\t\tenableDynamicSlideToggle: function () {\n\t\t\tdynamicSlideToggleEnabled = true;\n\t\t}\n\t};\n\n\t\/**\n\t * Shows the #app-sidebar and add .with-app-sidebar to subsequent siblings\n\t *\n\t * @param {Object} [$el] sidebar element to show, defaults to $('#app-sidebar')\n\t *\/\n\texports.Apps.showAppSidebar = function($el) {\n\t\tvar $appSidebar = $el || $('#app-sidebar');\n\t\t$appSidebar.removeClass('disappear')\n\t\t\t.show('slide', { direction: 'right' }, 300);\n\t\t$('#app-content').trigger(new $.Event('appresized'));\n\t};\n\n\t\/**\n\t * Shows the #app-sidebar and removes .with-app-sidebar from subsequent\n\t * siblings\n\t *\n\t * @param {Object} [$el] sidebar element to hide, defaults to $('#app-sidebar')\n\t *\/\n\texports.Apps.hideAppSidebar = function($el) {\n\t\tvar $appSidebar = $el || $('#app-sidebar');\n\t\t$appSidebar.hide('slide', { direction: 'right' }, 300,\n\t\t\tfunction() {\n\t\t\t\t$appSidebar.addClass('disappear');\n\t\t\t});\n\t\t$('#app-content').trigger(new $.Event('appresized'));\n\t};\n\n\t\/**\n\t * Provides a way to slide down a target area through a button and slide it\n\t * up if the user clicks somewhere else. Used for the news app settings and\n\t * add new field.\n\t *\n\t * Usage:\n\t * <button data-apps-slide-toggle=\".slide-area\">slide<\/button>\n\t * <div class=\".slide-area\" class=\"hidden\">I'm sliding up<\/div>\n\t *\/\n\tvar registerAppsSlideToggle = function () {\n\t\tvar buttons = $('[data-apps-slide-toggle]');\n\n\t\tif (buttons.length === 0) {\n\t\t\t$('#app-navigation').addClass('without-app-settings');\n\t\t}\n\n\t\t$(document).click(function (event) {\n\n\t\t\tif (dynamicSlideToggleEnabled) {\n\t\t\t\tbuttons = $('[data-apps-slide-toggle]');\n\t\t\t}\n\n\t\t\tbuttons.each(function (index, button) {\n\n\t\t\t\tvar areaSelector = $(button).data('apps-slide-toggle');\n\t\t\t\tvar area = $(areaSelector);\n\n\t\t\t\tfunction hideArea() {\n\t\t\t\t\tarea.slideUp(OC.menuSpeed*4, function() {\n\t\t\t\t\t\tarea.trigger(new $.Event('hide'));\n\t\t\t\t\t});\n\t\t\t\t\tarea.removeClass('opened');\n\t\t\t\t\t$(button).removeClass('opened');\n\t\t\t\t}\n\t\t\t\tfunction showArea() {\n\t\t\t\t\tarea.slideDown(OC.menuSpeed*4, function() {\n\t\t\t\t\t\tarea.trigger(new $.Event('show'));\n\t\t\t\t\t});\n\t\t\t\t\tarea.addClass('opened');\n\t\t\t\t\t$(button).addClass('opened');\n\t\t\t\t\tvar input = $(areaSelector + ' [autofocus]');\n\t\t\t\t\tif (input.length === 1) {\n\t\t\t\t\t\tinput.focus();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t\/\/ do nothing if the area is animated\n\t\t\t\tif (!area.is(':animated')) {\n\n\t\t\t\t\t\/\/ button toggles the area\n\t\t\t\t\tif ($(button).is($(event.target).closest('[data-apps-slide-toggle]'))) {\n\t\t\t\t\t\tif (area.is(':visible')) {\n\t\t\t\t\t\t\thideArea();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tshowArea();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\/\/ all other areas that have not been clicked but are open\n\t\t\t\t\t\/\/ should be slid up\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar closest = $(event.target).closest(areaSelector);\n\t\t\t\t\t\tif (area.is(':visible') && closest[0] !== area[0]) {\n\t\t\t\t\t\t\thideArea();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t});\n\t};\n\n\n\t$(document).ready(function () {\n\t\tregisterAppsSlideToggle();\n\t});\n\n}(document, jQuery, OC));\n\n\n\/**\n * @author Roeland Jago Douma <roeland@famdouma.nl>\n *\n * @copyright Copyright (c) 2015, ownCloud, Inc.\n * @license AGPL-3.0\n *\n * This code is free software: you can redistribute it and\/or modify\n * it under the terms of the GNU Affero General Public License, version 3,\n * as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License, version 3,\n * along with this program. If not, see <http:\/\/www.gnu.org\/licenses\/>\n *\n *\/\n\n\/**\n * Namespace to hold functions related to convert mimetype to icons\n *\n * @namespace\n *\/\nOC.MimeType = {\n\n\t\/**\n\t * Cache that maps mimeTypes to icon urls\n\t *\/\n\t_mimeTypeIcons: {},\n\n\t\/**\n\t * Return the file icon we want to use for the given mimeType.\n\t * The file needs to be present in the supplied file list\n\t *\n\t * @param {string} mimeType The mimeType we want an icon for\n\t * @param {array} files The available icons in this theme\n\t * @return {string} The icon to use or null if there is no match\n\t *\/\n\t_getFile: function(mimeType, files) {\n\t\tvar icon = mimeType.replace(new RegExp('\/', 'g'), '-');\n\n\t\t\/\/ Generate path\n\t\tif (mimeType === 'dir' && $.inArray('folder', files) !== -1) {\n\t\t\treturn 'folder';\n\t\t} else if (mimeType === 'dir-encrypted' && $.inArray('folder-encrypted', files) !== -1) {\n\t\t\treturn 'folder-encrypted';\n\t\t} else if (mimeType === 'dir-shared' && $.inArray('folder-shared', files) !== -1) {\n\t\t\treturn 'folder-shared';\n\t\t} else if (mimeType === 'dir-public' && $.inArray('folder-public', files) !== -1) {\n\t\t\treturn 'folder-public';\n\t\t} else if (mimeType === 'dir-external' && $.inArray('folder-external', files) !== -1) {\n\t\t\treturn 'folder-external';\n\t\t} else if ($.inArray(icon, files) !== -1) {\n\t\t\treturn icon;\n\t\t} else if ($.inArray(icon.split('-')[0], files) !== -1) {\n\t\t\treturn icon.split('-')[0];\n\t\t} else if ($.inArray('file', files) !== -1) {\n\t\t\treturn 'file';\n\t\t}\n\n\t\treturn null;\n\t},\n\n\t\/**\n\t * Return the url to icon of the given mimeType\n\t *\n\t * @param {string} mimeType The mimeType to get the icon for\n\t * @return {string} Url to the icon for mimeType\n\t *\/\n\tgetIconUrl: function(mimeType) {\n\t\tif (_.isUndefined(mimeType)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\twhile (mimeType in OC.MimeTypeList.aliases) {\n\t\t\tmimeType = OC.MimeTypeList.aliases[mimeType];\n\t\t}\n\t\tif (mimeType in OC.MimeType._mimeTypeIcons) {\n\t\t\treturn OC.MimeType._mimeTypeIcons[mimeType];\n\t\t}\n\n\t\t\/\/ First try to get the correct icon from the current theme\n\t\tvar gotIcon = null;\n\t\tvar path = '';\n\t\tif (OC.theme.folder !== '' && $.isArray(OC.MimeTypeList.themes[OC.theme.folder])) {\n\t\t\tpath = OC.getRootPath() + '\/themes\/' + OC.theme.folder + '\/core\/img\/filetypes\/';\n\t\t\tvar icon = OC.MimeType._getFile(mimeType, OC.MimeTypeList.themes[OC.theme.folder]);\n\n\t\t\tif (icon !== null) {\n\t\t\t\tgotIcon = true;\n\t\t\t\tpath += icon;\n\t\t\t}\n\t\t}\n\t\tif(OCA.Theming && gotIcon === null) {\n\t\t\tpath = OC.generateUrl('\/apps\/theming\/img\/core\/filetypes\/');\n\t\t\tpath += OC.MimeType._getFile(mimeType, OC.MimeTypeList.files);\n\t\t\tgotIcon = true;\n\t\t}\n\n\t\t\/\/ If we do not yet have an icon fall back to the default\n\t\tif (gotIcon === null) {\n\t\t\tpath = OC.getRootPath() + '\/core\/img\/filetypes\/';\n\t\t\tpath += OC.MimeType._getFile(mimeType, OC.MimeTypeList.files);\n\t\t}\n\n\t\tpath += '.svg';\n\n\t\tif(OCA.Theming) {\n\t\t\tpath += \"?v=\" + OCA.Theming.cacheBuster;\n\t\t}\n\n\t\t\/\/ Cache the result\n\t\tOC.MimeType._mimeTypeIcons[mimeType] = path;\n\t\treturn path;\n\t}\n\n};\n\n\n\/**\n* This file is automatically generated\n* DO NOT EDIT MANUALLY!\n*\n* You can update the list of MimeType Aliases in config\/mimetypealiases.json\n* The list of files is fetched from core\/img\/filetypes\n* To regenerate this file run .\/occ maintenance:mimetype:update-js\n*\/\nOC.MimeTypeList={\n\taliases: {\n \"application\/coreldraw\": \"image\",\n \"application\/epub+zip\": \"text\",\n \"application\/font-sfnt\": \"image\",\n \"application\/font-woff\": \"image\",\n \"application\/gpx+xml\": \"location\",\n \"application\/illustrator\": \"image\",\n \"application\/javascript\": \"text\/code\",\n \"application\/json\": \"text\/code\",\n \"application\/msaccess\": \"file\",\n \"application\/msexcel\": \"x-office\/spreadsheet\",\n \"application\/msonenote\": \"x-office\/document\",\n \"application\/mspowerpoint\": \"x-office\/presentation\",\n \"application\/msword\": \"x-office\/document\",\n \"application\/octet-stream\": \"file\",\n \"application\/postscript\": \"image\",\n \"application\/rss+xml\": \"application\/xml\",\n \"application\/vnd.android.package-archive\": \"package\/x-generic\",\n \"application\/vnd.lotus-wordpro\": \"x-office\/document\",\n \"application\/vnd.garmin.tcx+xml\": \"location\",\n \"application\/vnd.google-earth.kml+xml\": \"location\",\n \"application\/vnd.google-earth.kmz\": \"location\",\n \"application\/vnd.ms-excel\": \"x-office\/spreadsheet\",\n \"application\/vnd.ms-excel.addin.macroEnabled.12\": \"x-office\/spreadsheet\",\n \"application\/vnd.ms-excel.sheet.binary.macroEnabled.12\": \"x-office\/spreadsheet\",\n \"application\/vnd.ms-excel.sheet.macroEnabled.12\": \"x-office\/spreadsheet\",\n \"application\/vnd.ms-excel.template.macroEnabled.12\": \"x-office\/spreadsheet\",\n \"application\/vnd.ms-fontobject\": \"image\",\n \"application\/vnd.ms-powerpoint\": \"x-office\/presentation\",\n \"application\/vnd.ms-powerpoint.addin.macroEnabled.12\": \"x-office\/presentation\",\n \"application\/vnd.ms-powerpoint.presentation.macroEnabled.12\": \"x-office\/presentation\",\n \"application\/vnd.ms-powerpoint.slideshow.macroEnabled.12\": \"x-office\/presentation\",\n \"application\/vnd.ms-powerpoint.template.macroEnabled.12\": \"x-office\/presentation\",\n \"application\/vnd.ms-visio.drawing.macroEnabled.12\": \"application\/vnd.visio\",\n \"application\/vnd.ms-visio.drawing\": \"application\/vnd.visio\",\n \"application\/vnd.ms-visio.stencil.macroEnabled.12\": \"application\/vnd.visio\",\n \"application\/vnd.ms-visio.stencil\": \"application\/vnd.visio\",\n \"application\/vnd.ms-visio.template.macroEnabled.12\": \"application\/vnd.visio\",\n \"application\/vnd.ms-visio.template\": \"application\/vnd.visio\",\n \"application\/vnd.ms-word.document.macroEnabled.12\": \"x-office\/document\",\n \"application\/vnd.ms-word.template.macroEnabled.12\": \"x-office\/document\",\n \"application\/vnd.oasis.opendocument.presentation\": \"x-office\/presentation\",\n \"application\/vnd.oasis.opendocument.presentation-template\": \"x-office\/presentation\",\n \"application\/vnd.oasis.opendocument.spreadsheet\": \"x-office\/spreadsheet\",\n \"application\/vnd.oasis.opendocument.spreadsheet-template\": \"x-office\/spreadsheet\",\n \"application\/vnd.oasis.opendocument.text\": \"x-office\/document\",\n \"application\/vnd.oasis.opendocument.text-master\": \"x-office\/document\",\n \"application\/vnd.oasis.opendocument.text-template\": \"x-office\/document\",\n \"application\/vnd.oasis.opendocument.text-web\": \"x-office\/document\",\n \"application\/vnd.openxmlformats-officedocument.presentationml.presentation\": \"x-office\/presentation\",\n \"application\/vnd.openxmlformats-officedocument.presentationml.slideshow\": \"x-office\/presentation\",\n \"application\/vnd.openxmlformats-officedocument.presentationml.template\": \"x-office\/presentation\",\n \"application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet\": \"x-office\/spreadsheet\",\n \"application\/vnd.openxmlformats-officedocument.spreadsheetml.template\": \"x-office\/spreadsheet\",\n \"application\/vnd.openxmlformats-officedocument.wordprocessingml.document\": \"x-office\/document\",\n \"application\/vnd.openxmlformats-officedocument.wordprocessingml.template\": \"x-office\/document\",\n \"application\/vnd.visio\": \"x-office\/document\",\n \"application\/vnd.wordperfect\": \"x-office\/document\",\n \"application\/x-7z-compressed\": \"package\/x-generic\",\n \"application\/x-bzip2\": \"package\/x-generic\",\n \"application\/x-cbr\": \"text\",\n \"application\/x-compressed\": \"package\/x-generic\",\n \"application\/x-dcraw\": \"image\",\n \"application\/x-deb\": \"package\/x-generic\",\n \"application\/x-fictionbook+xml\": \"text\",\n \"application\/x-font\": \"image\",\n \"application\/x-gimp\": \"image\",\n \"application\/x-gzip\": \"package\/x-generic\",\n \"application\/x-iwork-keynote-sffkey\": \"x-office\/presentation\",\n \"application\/x-iwork-numbers-sffnumbers\": \"x-office\/spreadsheet\",\n \"application\/x-iwork-pages-sffpages\": \"x-office\/document\",\n \"application\/x-mobipocket-ebook\": \"text\",\n \"application\/x-perl\": \"text\/code\",\n \"application\/x-photoshop\": \"image\",\n \"application\/x-php\": \"text\/code\",\n \"application\/x-rar-compressed\": \"package\/x-generic\",\n \"application\/x-tar\": \"package\/x-generic\",\n \"application\/x-tex\": \"text\",\n \"application\/xml\": \"text\/html\",\n \"application\/yaml\": \"text\/code\",\n \"application\/zip\": \"package\/x-generic\",\n \"database\": \"file\",\n \"httpd\/unix-directory\": \"dir\",\n \"text\/css\": \"text\/code\",\n \"text\/csv\": \"x-office\/spreadsheet\",\n \"text\/html\": \"text\/code\",\n \"text\/x-c\": \"text\/code\",\n \"text\/x-c++src\": \"text\/code\",\n \"text\/x-h\": \"text\/code\",\n \"text\/x-java-source\": \"text\/code\",\n \"text\/x-ldif\": \"text\/code\",\n \"text\/x-python\": \"text\/code\",\n \"text\/x-shellscript\": \"text\/code\",\n \"web\": \"text\/code\",\n \"application\/internet-shortcut\": \"link\"\n},\n\tfiles: [\n \"application\",\n \"application-pdf\",\n \"audio\",\n \"file\",\n \"folder\",\n \"folder-drag-accept\",\n \"folder-encrypted\",\n \"folder-external\",\n \"folder-public\",\n \"folder-shared\",\n \"folder-starred\",\n \"image\",\n \"link\",\n \"location\",\n \"package-x-generic\",\n \"text\",\n \"text-calendar\",\n \"text-code\",\n \"text-vcard\",\n \"video\",\n \"x-office-document\",\n \"x-office-presentation\",\n \"x-office-spreadsheet\"\n],\n\tthemes: []\n};\n\n\n\/*\n * Copyright (c) 2015\n *\n * This file is licensed under the Affero General Public License version 3\n * or later.\n *\n * See the COPYING-README file.\n *\n *\/\n\n\/* global Backbone *\/\nif(!_.isUndefined(Backbone)) {\n\tOC.Backbone = Backbone.noConflict();\n}\n\n\n\/*\n * Copyright (c) 2015\n *\n * This file is licensed under the Affero General Public License version 3\n * or later.\n *\n * See the COPYING-README file.\n *\n *\/\n\n\/* global Select2 *\/\n\n\/**\n * Select2 extension for toggling values in a multi-select dropdown\n *\/\n(function(Select2) {\n\n\tvar Select2FindHighlightableChoices = Select2.class.multi.prototype.findHighlightableChoices;\n\tSelect2.class.multi.prototype.findHighlightableChoices = function () {\n\t\tif (this.opts.toggleSelect) {\n\t\t\treturn this.results.find('.select2-result-selectable:not(.select2-disabled)');\n\t\t}\n\t\treturn Select2FindHighlightableChoices.apply(this, arguments);\n\t};\n\n\tvar Select2TriggerSelect = Select2.class.multi.prototype.triggerSelect;\n\tSelect2.class.multi.prototype.triggerSelect = function (data) {\n\t\tif (this.opts.toggleSelect && this.val().indexOf(this.id(data)) !== -1) {\n\t\t\tvar self = this;\n\t\t\tvar val = this.id(data);\n\n\t\t\tvar selectionEls = this.container.find('.select2-search-choice').filter(function() {\n\t\t\t\treturn (self.id($(this).data('select2-data')) === val);\n\t\t\t});\n\n\t\t\tif (this.unselect(selectionEls)) {\n\t\t\t\t\/\/ also unselect in dropdown\n\t\t\t\tthis.results.find('.select2-result.select2-selected').each(function () {\n\t\t\t\t\tvar $this = $(this);\n\t\t\t\t\tif (self.id($this.data('select2-data')) === val) {\n\t\t\t\t\t\t$this.removeClass('select2-selected');\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.clearSearch();\n\t\t\t}\n\n\t\t\treturn false;\n\t\t} else {\n\t\t\treturn Select2TriggerSelect.apply(this, arguments);\n\t\t}\n\t};\n\n})(Select2);\n\n\n\n\/**\n * ownCloud\n *\n * @author John Molakvo\u00e6\n * @copyright 2016-2018 John Molakvo\u00e6 <skjnldsv@protonmail.com>\n * @author Morris Jobke\n * @copyright 2013 Morris Jobke <morris.jobke@gmail.com>\n *\n * This library is free software; you can redistribute it and\/or\n * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE\n * License as published by the Free Software Foundation; either\n * version 3 of the License, or any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU AFFERO GENERAL PUBLIC LICENSE for more details.\n *\n * You should have received a copy of the GNU Affero General Public\n * License along with this library. If not, see <http:\/\/www.gnu.org\/licenses\/>.\n *\n *\/\n\n\/*\n * Adds a background color to the element called on and adds the first character\n * of the passed in string. This string is also the seed for the generation of\n * the background color.\n *\n * You have following HTML:\n *\n * <div id=\"albumart\"><\/div>\n *\n * And call this from Javascript:\n *\n * $('#albumart').imageplaceholder('The Album Title');\n *\n * Which will result in:\n *\n * <div id=\"albumart\" style=\"background-color: rgb(121, 90, 171); ... \">T<\/div>\n *\n * You may also call it like this, to have a different background, than the seed:\n *\n * $('#albumart').imageplaceholder('The Album Title', 'Album Title');\n *\n * Resulting in:\n *\n * <div id=\"albumart\" style=\"background-color: rgb(121, 90, 171); ... \">A<\/div>\n *\n *\/\n\n \/*\n * Alternatively, you can use the prototype function to convert your string to rgb colors:\n *\n * \"a6741a86aded5611a8e46ce16f2ad646\".toRgb()\n *\n * Will return the rgb parameters within the following object:\n *\n * Color\u00a0{r: 208, g: 158, b: 109}\n *\n *\/\n\n(function ($) {\n\n\tString.prototype.toRgb = function() {\n\t\t\/\/ Normalize hash\t\t\n\t\tvar hash = this.toLowerCase();\n\n\t\t\/\/ Already a md5 hash?\n\t\tif( hash.match(\/^([0-9a-f]{4}-?){8}$\/) === null ) {\n\t\t\thash = md5(hash);\n\t\t}\n\n\t\thash = hash.replace(\/[^0-9a-f]\/g, '');\n\n\t\tfunction Color(r,g,b) {\n\t\t\tthis.r = r;\n\t\t\tthis.g = g;\n\t\t\tthis.b = b;\n\t\t}\n\n\t\tfunction stepCalc(steps, ends) {\n\t\t\tvar step = new Array(3);\n\t\t\tstep[0] = (ends[1].r - ends[0].r) \/ steps;\n\t\t\tstep[1] = (ends[1].g - ends[0].g) \/ steps;\n\t\t\tstep[2] = (ends[1].b - ends[0].b) \/ steps;\n\t\t\treturn step;\n\t\t}\n\n\t\tfunction mixPalette(steps, color1, color2) {\n\t\t\tvar count = steps + 1;\n\t\t\tvar palette = new Array();\n\t\t\tpalette.push(color1);\n\t\t\tvar step = stepCalc(steps, [color1, color2])\n\t\t\tfor (var i = 1; i < steps; i++) {\n\t\t\t\tvar r = parseInt(color1.r + (step[0] * i));\n\t\t\t\tvar g = parseInt(color1.g + (step[1] * i));\n\t\t\t\tvar b = parseInt(color1.b + (step[2] * i));\n\t\t\t\t\tpalette.push(new Color(r,g,b));\n\t\t\t}\n\t\t\treturn palette;\n\t\t}\n\n\t\tvar red = new Color(182, 70, 157);\n\t\tvar yellow = new Color(221, 203, 85);\n\t\tvar blue = new Color(0, 130, 201); \/\/ Nextcloud blue\n\t\t\/\/ Number of steps to go from a color to another\n\t\t\/\/ 3 colors * 6 will result in 18 generated colors\n\t\tvar steps = 6;\n\n\t\tvar palette1 = mixPalette(steps, red, yellow);\n\t\tvar palette2 = mixPalette(steps, yellow, blue);\n\t\tvar palette3 = mixPalette(steps, blue, red);\n\n\t\tvar finalPalette = palette1.concat(palette2).concat(palette3);\n\n\t\t\/\/ Convert a string to an integer evenly\n\t\tfunction hashToInt(hash, maximum) {\n\t\t\tvar finalInt = 0;\n\t\t\tvar result = Array();\n\n\t\t\t\/\/ Splitting evenly the string\n\t\t\tfor (var i=0; i<hash.length; i++) {\n\t\t\t\t\/\/ chars in md5 goes up to f, hex:16\n\t\t\t\tresult.push(parseInt(hash.charAt(i), 16) % 16);\n\t\t\t}\n\t\t\t\/\/ Adds up all results\n\t\t\tfor (var j in result) {\n\t\t\t\tfinalInt += result[j];\n\t\t\t}\n\t\t\t\/\/ chars in md5 goes up to f, hex:16\n\t\t\t\/\/ make sure we're always using int in our operation\n\t\t\treturn parseInt(parseInt(finalInt) % maximum);\n\t\t}\n\t\treturn finalPalette[hashToInt(hash, steps * 3 )];\n\t};\n\n\t$.fn.imageplaceholder = function(seed, text, size) {\n\t\ttext = text || seed;\n\n\t\t\/\/ Compute the hash\n\t\tvar rgb = seed.toRgb();\n\t\tthis.css('background-color', 'rgb('+rgb.r+', '+rgb.g+', '+rgb.b+')');\n\n\t\t\/\/ Placeholders are square\n\t\tvar height = this.height() || size || 32;\n\t\tthis.height(height);\n\t\tthis.width(height);\n\n\t\t\/\/ CSS rules\n\t\tthis.css('color', '#fff');\n\t\tthis.css('font-weight', 'normal');\n\t\tthis.css('text-align', 'center');\n\n\t\t\/\/ calculate the height\n\t\tthis.css('line-height', height + 'px');\n\t\tthis.css('font-size', (height * 0.55) + 'px');\n\n\t\tif(seed !== null && seed.length) {\n\t\t\tthis.html(text[0].toUpperCase());\n\t\t}\n\t};\n\n\t$.fn.clearimageplaceholder = function() {\n\t\tthis.css('background-color', '');\n\t\tthis.css('color', '');\n\t\tthis.css('font-weight', '');\n\t\tthis.css('text-align', '');\n\t\tthis.css('line-height', '');\n\t\tthis.css('font-size', '');\n\t\tthis.html('');\n\t\tthis.removeClass('icon-loading');\n\t\tthis.removeClass('icon-loading-small');\n\t};\n}(jQuery));\n\n\n\/**\n * Copyright (c) 2013 Christopher Sch\u00e4pers <christopher@schaepers.it>\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING-README file.\n *\/\n\n\/**\n * This plugin inserts the right avatar for the user, depending on, whether a\n * custom avatar is uploaded - which it uses then - or not, and display a\n * placeholder with the first letter of the users name instead.\n * For this it queries the core_avatar_get route, thus this plugin is fit very\n * tightly for owncloud, and it may not work anywhere else.\n *\n * You may use this on any <div><\/div>\n * Here I'm using <div class=\"avatardiv\"><\/div> as an example.\n *\n * There are 5 ways to call this:\n *\n * 1. $('.avatardiv').avatar('jdoe', 128);\n * This will make the div to jdoe's fitting avatar, with a size of 128px.\n *\n * 2. $('.avatardiv').avatar('jdoe');\n * This will make the div to jdoe's fitting avatar. If the div already has a\n * height, it will be used for the avatars size. Otherwise this plugin will\n * search for 'size' DOM data, to use for avatar size. If neither are available\n * it will default to 64px.\n *\n * 3. $('.avatardiv').avatar();\n * This will search the DOM for 'user' data, to use as the username. If there\n * is no username available it will default to a placeholder with the value of\n * \"?\". The size will be determined the same way, as the second example.\n *\n * 4. $('.avatardiv').avatar('jdoe', 128, true);\n * This will behave like the first example, except it will also append random\n * hashes to the custom avatar images, to force image reloading in IE8.\n *\n * 5. $('.avatardiv').avatar('jdoe', 128, undefined, true);\n * This will behave like the first example, but it will hide the avatardiv, if\n * it will display the default placeholder. undefined is the ie8fix from\n * example 4 and can be either true, or false\/undefined, to be ignored.\n *\n * 6. $('.avatardiv').avatar('jdoe', 128, undefined, true, callback);\n * This will behave like the above example, but it will call the function\n * defined in callback after the avatar is placed into the DOM.\n *\n *\/\n\n(function ($) {\n\t$.fn.avatar = function(user, size, ie8fix, hidedefault, callback, displayname) {\n\t\tvar setAvatarForUnknownUser = function(target) {\n\t\t\ttarget.imageplaceholder('?');\n\t\t\ttarget.css('background-color', '#b9b9b9');\n\t\t};\n\n\t\tif (typeof(user) !== 'undefined') {\n\t\t\tuser = String(user);\n\t\t}\n\t\tif (typeof(displayname) !== 'undefined') {\n\t\t\tdisplayname = String(displayname);\n\t\t}\n\n\t\tif (typeof(size) === 'undefined') {\n\t\t\tif (this.height() > 0) {\n\t\t\t\tsize = this.height();\n\t\t\t} else if (this.data('size') > 0) {\n\t\t\t\tsize = this.data('size');\n\t\t\t} else {\n\t\t\t\tsize = 64;\n\t\t\t}\n\t\t}\n\n\t\tthis.height(size);\n\t\tthis.width(size);\n\n\t\tif (typeof(user) === 'undefined') {\n\t\t\tif (typeof(this.data('user')) !== 'undefined') {\n\t\t\t\tuser = this.data('user');\n\t\t\t} else {\n\t\t\t\tsetAvatarForUnknownUser(this);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t\/\/ sanitize\n\t\tuser = String(user).replace(\/\\\/\/g,'');\n\n\t\tvar $div = this;\n\t\tvar url;\n\n\t\t\/\/ If this is our own avatar we have to use the version attribute\n\t\tif (user === OC.getCurrentUser().uid) {\n\t\t\turl = OC.generateUrl(\n\t\t\t\t'\/avatar\/{user}\/{size}?v={version}',\n\t\t\t\t{\n\t\t\t\t\tuser: user,\n\t\t\t\t\tsize: Math.ceil(size * window.devicePixelRatio),\n\t\t\t\t\tversion: oc_userconfig.avatar.version\n\t\t\t\t});\n\t\t} else {\n\t\t\turl = OC.generateUrl(\n\t\t\t\t'\/avatar\/{user}\/{size}',\n\t\t\t\t{\n\t\t\t\t\tuser: user,\n\t\t\t\t\tsize: Math.ceil(size * window.devicePixelRatio)\n\t\t\t\t});\n\t\t}\n\n\t\tvar img = new Image();\n\n\t\t\/\/ If the new image loads successfully set it.\n\t\timg.onload = function() {\n\t\t\t$div.clearimageplaceholder();\n\t\t\t$div.append(img);\n\n\t\t\tif(typeof callback === 'function') {\n\t\t\t\tcallback();\n\t\t\t}\n\t\t};\n\t\t\/\/ Fallback when avatar loading fails:\n\t\t\/\/ Use old placeholder when a displayname attribute is defined,\n\t\t\/\/ otherwise show the unknown user placeholder.\n\t\timg.onerror = function () {\n\t\t\t$div.clearimageplaceholder();\n\t\t\tif (typeof(displayname) !== 'undefined') {\n\t\t\t\t$div.imageplaceholder(user, displayname);\n\t\t\t} else {\n\t\t\t\tsetAvatarForUnknownUser($div);\n\t\t\t}\n\n\t\t\tif(typeof callback === 'function') {\n\t\t\t\tcallback();\n\t\t\t}\n\t\t};\n\n\t\tif (size < 32) {\n\t\t\t$div.addClass('icon-loading-small');\n\t\t} else {\n\t\t\t$div.addClass('icon-loading');\n\t\t}\n\t\timg.width = size;\n\t\timg.height = size;\n\t\timg.src = url;\n\t\timg.alt = '';\n\t};\n}(jQuery));\n\n\n\/**\n * Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com>\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING-README file.\n *\/\n\n(function ($) {\n\n\tvar LIST = ''\n\t\t+ '<div class=\"menu popovermenu menu-left hidden contactsmenu-popover\">'\n\t\t+ ' <ul>'\n\t\t+ ' <li>'\n\t\t+ ' <a>'\n\t\t+ ' <span class=\"icon-loading-small\"><\/span>'\n\t\t+ ' <\/a>'\n\t\t+ ' <\/li>'\n\t\t+ ' <\/ul>'\n\t\t+ '<\/div>';\n\n\t$.fn.contactsMenu = function(shareWith, shareType, appendTo) {\n\t\t\/\/ 0 - user, 4 - email, 6 - remote\n\t\tvar allowedTypes = [0, 4, 6];\n\t\tif (allowedTypes.indexOf(shareType) === -1) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar $div = this;\n\t\tappendTo.append(LIST);\n\t\tvar $list = appendTo.find('div.contactsmenu-popover');\n\n\t\t$div.click(function() {\n\t\t\tif (!$list.hasClass('hidden')) {\n\t\t\t\t$list.addClass('hidden');\n\t\t\t\t$list.hide();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t$list.removeClass('hidden');\n\t\t\t$list.show();\n\n\t\t\tif ($list.hasClass('loaded')) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t$list.addClass('loaded');\n\t\t\t$.ajax(OC.generateUrl('\/contactsmenu\/findOne'), {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tdata: {\n\t\t\t\t\tshareType: shareType,\n\t\t\t\t\tshareWith: shareWith\n\t\t\t\t}\n\t\t\t}).then(function(data) {\n\t\t\t\t$list.find('ul').find('li').addClass('hidden');\n\n\t\t\t\tvar actions;\n\t\t\t\tif (!data.topAction) {\n\t\t\t\t\tactions = [{\n\t\t\t\t\t\thyperlink: '#',\n\t\t\t\t\t\ttitle: t('core', 'No action available')\n\t\t\t\t\t}];\n\t\t\t\t} else {\n\t\t\t\t\tactions = [data.topAction].concat(data.actions);\n\t\t\t\t}\n\n\t\t\t\tactions.forEach(function(action) {\n\t\t\t\t\tvar template = OC.ContactsMenu.Templates['jquery_entry'];\n\t\t\t\t\t$list.find('ul').append(template(action));\n\t\t\t\t});\n\n\t\t\t\tif (actions.length === 0) {\n\n\t\t\t\t}\n\t\t\t}, function(jqXHR) {\n\t\t\t\t$list.find('ul').find('li').addClass('hidden');\n\n\t\t\t\tvar title;\n\t\t\t\tif (jqXHR.status === 404) {\n\t\t\t\t\ttitle = t('core', 'No action available');\n\t\t\t\t} else {\n\t\t\t\t\ttitle = t('core', 'Error fetching contact actions');\n\t\t\t\t}\n\n\t\t\t\tvar template = OC.ContactsMenu.Templates['jquery_entry'];\n\t\t\t\t$list.find('ul').append(template({\n\t\t\t\t\thyperlink: '#',\n\t\t\t\t\ttitle: title\n\t\t\t\t}));\n\t\t\t});\n\t\t});\n\n\t\t$(document).click(function(event) {\n\t\t\tvar clickedList = ($list.has(event.target).length > 0);\n\t\t\tvar clickedTarget = ($div.has(event.target).length > 0);\n\n\t\t\t$div.each(function() {\n\t\t\t\tif ($(this).is(event.target)) {\n\t\t\t\t\tclickedTarget = true;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (clickedList || clickedTarget) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t$list.addClass('hidden');\n\t\t\t$list.hide();\n\t\t});\n\t};\n}(jQuery));\n\n\n"]},{"file":"\/var\/www\/nextcloud\/lib\/private\/Template\/JSCombiner.php","line":194,"function":"putContent","class":"OC\\Files\\SimpleFS\\SimpleFile","type":"->","args":["(function($) {\n\t$.widget('oc.ocdialog', {\n\t\toptions: {\n\t\t\twidth: 'auto',\n\t\t\theight: 'auto',\n\t\t\tcloseButton: true,\n\t\t\tcloseOnEscape: true,\n\t\t\tmodal: false\n\t\t},\n\t\t_create: function() {\n\t\t\tvar self = this;\n\n\t\t\tthis.originalCss = {\n\t\t\t\tdisplay: this.element[0].style.display,\n\t\t\t\twidth: this.element[0].style.width,\n\t\t\t\theight: this.element[0].style.height\n\t\t\t};\n\n\t\t\tthis.originalTitle = this.element.attr('title');\n\t\t\tthis.options.title = this.options.title || this.originalTitle;\n\n\t\t\tthis.$dialog = $('<div class=\"oc-dialog\" \/>')\n\t\t\t\t.attr({\n\t\t\t\t\t\/\/ Setting tabIndex makes the div focusable\n\t\t\t\t\ttabIndex: -1,\n\t\t\t\t\trole: 'dialog'\n\t\t\t\t})\n\t\t\t\t.insertBefore(this.element);\n\t\t\tthis.$dialog.append(this.element.detach());\n\t\t\tthis.element.removeAttr('title').addClass('oc-dialog-content').appendTo(this.$dialog);\n\n\t\t\tthis.$dialog.css({\n\t\t\t\tdisplay: 'inline-block',\n\t\t\t\tposition: 'fixed'\n\t\t\t});\n\n\t\t\t$(document).on('keydown keyup', function(event) {\n\t\t\t\tif (\n\t\t\t\t\tevent.target !== self.$dialog.get(0) &&\n\t\t\t\t\tself.$dialog.find($(event.target)).length === 0\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t\/\/ Escape\n\t\t\t\tif (\n\t\t\t\t\tevent.keyCode === 27 &&\n\t\t\t\t\tevent.type === 'keydown' &&\n\t\t\t\t\tself.options.closeOnEscape\n\t\t\t\t) {\n\t\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t\t\tself.close();\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t\/\/ Enter\n\t\t\t\tif(event.keyCode === 13) {\n\t\t\t\t\tevent.stopImmediatePropagation();\n\t\t\t\t\tif(event.type === 'keyup') {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\t\/\/ If no button is selected we trigger the primary\n\t\t\t\t\tif (\n\t\t\t\t\t\tself.$buttonrow &&\n\t\t\t\t\t\tself.$buttonrow.find($(event.target)).length === 0\n\t\t\t\t\t) {\n\t\t\t\t\t\tvar $button = self.$buttonrow.find('button.primary');\n\t\t\t\t\t\tif($button && !$button.prop('disabled')) {\n\t\t\t\t\t\t\t$button.trigger('click');\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if(self.$buttonrow) {\n\t\t\t\t\t\t$(event.target).trigger('click');\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tthis._setOptions(this.options);\n\t\t\tthis._createOverlay();\n\t\t},\n\t\t_init: function() {\n\t\t\tthis.$dialog.focus();\n\t\t\tthis._trigger('open');\n\t\t},\n\t\t_setOption: function(key, value) {\n\t\t\tvar self = this;\n\t\t\tswitch(key) {\n\t\t\t\tcase 'title':\n\t\t\t\t\tif(this.$title) {\n\t\t\t\t\t\tthis.$title.text(value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar $title = $('<h2 class=\"oc-dialog-title\">'\n\t\t\t\t\t\t\t+ value\n\t\t\t\t\t\t\t+ '<\/h2>');\n\t\t\t\t\t\tthis.$title = $title.prependTo(this.$dialog);\n\t\t\t\t\t}\n\t\t\t\t\tthis._setSizes();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'buttons':\n\t\t\t\t\tif(this.$buttonrow) {\n\t\t\t\t\t\tthis.$buttonrow.empty();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar $buttonrow = $('<div class=\"oc-dialog-buttonrow\" \/>');\n\t\t\t\t\t\tthis.$buttonrow = $buttonrow.appendTo(this.$dialog);\n\t\t\t\t\t}\n\t\t\t\t\tif (value.length === 1) {\n\t\t\t\t\t\tthis.$buttonrow.addClass('onebutton');\n\t\t\t\t\t} else if (value.length === 2) {\n\t\t\t\t\t\tthis.$buttonrow.addClass('twobuttons');\n\t\t\t\t\t} else if (value.length === 3) {\n\t\t\t\t\t\tthis.$buttonrow.addClass('threebuttons');\n\t\t\t\t\t}\n\t\t\t\t\t$.each(value, function(idx, val) {\n\t\t\t\t\t\tvar $button = $('<button>').text(val.text);\n\t\t\t\t\t\tif (val.classes) {\n\t\t\t\t\t\t\t$button.addClass(val.classes);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif(val.defaultButton) {\n\t\t\t\t\t\t\t$button.addClass('primary');\n\t\t\t\t\t\t\tself.$defaultButton = $button;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tself.$buttonrow.append($button);\n\t\t\t\t\t\t$button.click(function() {\n\t\t\t\t\t\t\tval.click.apply(self.element[0], arguments);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t\tthis.$buttonrow.find('button')\n\t\t\t\t\t\t.on('focus', function(event) {\n\t\t\t\t\t\t\tself.$buttonrow.find('button').removeClass('primary');\n\t\t\t\t\t\t\t$(this).addClass('primary');\n\t\t\t\t\t\t});\n\t\t\t\t\tthis._setSizes();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'style':\n\t\t\t\t\tif (value.buttons !== undefined) {\n\t\t\t\t\t\tthis.$buttonrow.addClass(value.buttons);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'closeButton':\n\t\t\t\t\tif(value) {\n\t\t\t\t\t\tvar $closeButton = $('<a class=\"oc-dialog-close\"><\/a>');\n\t\t\t\t\t\tthis.$dialog.prepend($closeButton);\n\t\t\t\t\t\t$closeButton.on('click', function() {\n\t\t\t\t\t\t\tself.close();\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.$dialog.find('.oc-dialog-close').remove();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'width':\n\t\t\t\t\tthis.$dialog.css('width', value);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'height':\n\t\t\t\t\tthis.$dialog.css('height', value);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'close':\n\t\t\t\t\tthis.closeCB = value;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\/\/this._super(key, value);\n\t\t\t$.Widget.prototype._setOption.apply(this, arguments );\n\t\t},\n\t\t_setOptions: function(options) {\n\t\t\t\/\/this._super(options);\n\t\t\t$.Widget.prototype._setOptions.apply(this, arguments);\n\t\t},\n\t\t_setSizes: function() {\n\t\t\tvar lessHeight = 0;\n\t\t\tif(this.$title) {\n\t\t\t\tlessHeight += this.$title.outerHeight(true);\n\t\t\t}\n\t\t\tif(this.$buttonrow) {\n\t\t\t\tlessHeight += this.$buttonrow.outerHeight(true);\n\t\t\t}\n\t\t\tthis.element.css({\n\t\t\t\t'height': 'calc(100% - ' + lessHeight + 'px)'\n\t\t\t});\n\t\t},\n\t\t_createOverlay: function() {\n\t\t\tif(!this.options.modal) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar self = this;\n\t\t\tthis.overlay = $('<div>')\n\t\t\t\t.addClass('oc-dialog-dim')\n\t\t\t\t.appendTo($('#content'));\n\t\t\tthis.overlay.on('click keydown keyup', function(event) {\n\t\t\t\tif(event.target !== self.$dialog.get(0) && self.$dialog.find($(event.target)).length === 0) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\t_destroyOverlay: function() {\n\t\t\tif (!this.options.modal) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (this.overlay) {\n\t\t\t\tthis.overlay.off('click keydown keyup');\n\t\t\t\tthis.overlay.remove();\n\t\t\t\tthis.overlay = null;\n\t\t\t}\n\t\t},\n\t\twidget: function() {\n\t\t\treturn this.$dialog;\n\t\t},\n\t\tclose: function() {\n\t\t\tthis._destroyOverlay();\n\t\t\tvar self = this;\n\t\t\t\/\/ Ugly hack to catch remaining keyup events.\n\t\t\tsetTimeout(function() {\n\t\t\t\tself._trigger('close', self);\n\t\t\t}, 200);\n\n\t\t\tself.$dialog.remove();\n\t\t\tthis.destroy();\n\t\t},\n\t\tdestroy: function() {\n\t\t\tif(this.$title) {\n\t\t\t\tthis.$title.remove();\n\t\t\t}\n\t\t\tif(this.$buttonrow) {\n\t\t\t\tthis.$buttonrow.remove();\n\t\t\t}\n\n\t\t\tif(this.originalTitle) {\n\t\t\t\tthis.element.attr('title', this.originalTitle);\n\t\t\t}\n\t\t\tthis.element.removeClass('oc-dialog-content')\n\t\t\t\t\t.css(this.originalCss).detach().insertBefore(this.$dialog);\n\t\t\tthis.$dialog.remove();\n\t\t}\n\t});\n}(jQuery));\n\n\n\/**\n * ownCloud\n *\n * @author Bartek Przybylski, Christopher Sch\u00e4pers, Thomas Tanghus\n * @copyright 2012 Bartek Przybylski bartek@alefzero.eu\n *\n * This library is free software; you can redistribute it and\/or\n * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE\n * License as published by the Free Software Foundation; either\n * version 3 of the License, or any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU AFFERO GENERAL PUBLIC LICENSE for more details.\n *\n * You should have received a copy of the GNU Affero General Public\n * License along with this library. If not, see <http:\/\/www.gnu.org\/licenses\/>.\n *\n *\/\n\n\/* global alert *\/\n\n\/**\n * this class to ease the usage of jquery dialogs\n * @lends OC.dialogs\n *\/\nvar OCdialogs = {\n\t\/\/ dialog button types\n\tYES_NO_BUTTONS:\t\t70,\n\tOK_BUTTONS:\t\t71,\n\n\tFILEPICKER_TYPE_CHOOSE: 1,\n\tFILEPICKER_TYPE_MOVE: 2,\n\tFILEPICKER_TYPE_COPY: 3,\n\tFILEPICKER_TYPE_COPY_MOVE: 4,\n\n\t\/\/ used to name each dialog\n\tdialogsCounter: 0,\n\t\/**\n\t* displays alert dialog\n\t* @param text content of dialog\n\t* @param title dialog title\n\t* @param callback which will be triggered when user presses OK\n\t* @param modal make the dialog modal\n\t*\/\n\talert:function(text, title, callback, modal) {\n\t\tthis.message(\n\t\t\ttext,\n\t\t\ttitle,\n\t\t\t'alert',\n\t\t\tOCdialogs.OK_BUTTON,\n\t\t\tcallback,\n\t\t\tmodal\n\t\t);\n\t},\n\t\/**\n\t* displays info dialog\n\t* @param text content of dialog\n\t* @param title dialog title\n\t* @param callback which will be triggered when user presses OK\n\t* @param modal make the dialog modal\n\t*\/\n\tinfo:function(text, title, callback, modal) {\n\t\tthis.message(text, title, 'info', OCdialogs.OK_BUTTON, callback, modal);\n\t},\n\t\/**\n\t* displays confirmation dialog\n\t* @param text content of dialog\n\t* @param title dialog title\n\t* @param callback which will be triggered when user presses YES or NO\n\t* (true or false would be passed to callback respectively)\n\t* @param modal make the dialog modal\n\t*\/\n\tconfirm:function(text, title, callback, modal) {\n\t\treturn this.message(\n\t\t\ttext,\n\t\t\ttitle,\n\t\t\t'notice',\n\t\t\tOCdialogs.YES_NO_BUTTONS,\n\t\t\tcallback,\n\t\t\tmodal\n\t\t);\n\t},\n\t\/**\n\t* displays confirmation dialog\n\t* @param text content of dialog\n\t* @param title dialog title\n\t* @param callback which will be triggered when user presses YES or NO\n\t* (true or false would be passed to callback respectively)\n\t* @param modal make the dialog modal\n\t*\/\n\tconfirmHtml:function(text, title, callback, modal) {\n\t\treturn this.message(\n\t\t\ttext,\n\t\t\ttitle,\n\t\t\t'notice',\n\t\t\tOCdialogs.YES_NO_BUTTONS,\n\t\t\tcallback,\n\t\t\tmodal,\n\t\t\ttrue\n\t\t);\n\t},\n\t\/**\n\t * displays prompt dialog\n\t * @param text content of dialog\n\t * @param title dialog title\n\t * @param callback which will be triggered when user presses YES or NO\n\t * (true or false would be passed to callback respectively)\n\t * @param modal make the dialog modal\n\t * @param name name of the input field\n\t * @param password whether the input should be a password input\n\t *\/\n\tprompt: function (text, title, callback, modal, name, password) {\n\t\treturn $.when(this._getMessageTemplate()).then(function ($tmpl) {\n\t\t\tvar dialogName = 'oc-dialog-' + OCdialogs.dialogsCounter + '-content';\n\t\t\tvar dialogId = '#' + dialogName;\n\t\t\tvar $dlg = $tmpl.octemplate({\n\t\t\t\tdialog_name: dialogName,\n\t\t\t\ttitle : title,\n\t\t\t\tmessage : text,\n\t\t\t\ttype : 'notice'\n\t\t\t});\n\t\t\tvar input = $('<input\/>');\n\t\t\tinput.attr('type', password ? 'password' : 'text').attr('id', dialogName + '-input').attr('placeholder', name);\n\t\t\tvar label = $('<label\/>').attr('for', dialogName + '-input').text(name + ': ');\n\t\t\t$dlg.append(label);\n\t\t\t$dlg.append(input);\n\t\t\tif (modal === undefined) {\n\t\t\t\tmodal = false;\n\t\t\t}\n\t\t\t$('body').append($dlg);\n\n\t\t\t\/\/ wrap callback in _.once():\n\t\t\t\/\/ only call callback once and not twice (button handler and close\n\t\t\t\/\/ event) but call it for the close event, if ESC or the x is hit\n\t\t\tif (callback !== undefined) {\n\t\t\t\tcallback = _.once(callback);\n\t\t\t}\n\n\t\t\tvar buttonlist = [{\n\t\t\t\t\ttext : t('core', 'No'),\n\t\t\t\t\tclick: function () {\n\t\t\t\t\t\tif (callback !== undefined) {\n\t\t\t\t\t\t\tcallback(false, input.val());\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\t}\n\t\t\t\t}, {\n\t\t\t\t\ttext : t('core', 'Yes'),\n\t\t\t\t\tclick : function () {\n\t\t\t\t\t\tif (callback !== undefined) {\n\t\t\t\t\t\t\tcallback(true, input.val());\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\t},\n\t\t\t\t\tdefaultButton: true\n\t\t\t\t}\n\t\t\t];\n\n\t\t\t$(dialogId).ocdialog({\n\t\t\t\tcloseOnEscape: true,\n\t\t\t\tmodal : modal,\n\t\t\t\tbuttons : buttonlist,\n\t\t\t\tclose : function() {\n\t\t\t\t\t\/\/ callback is already fired if Yes\/No is clicked directly\n\t\t\t\t\tif (callback !== undefined) {\n\t\t\t\t\t\tcallback(false, input.val());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t\tinput.focus();\n\t\t\tOCdialogs.dialogsCounter++;\n\t\t});\n\t},\n\t\/**\n\t * show a file picker to pick a file from\n\t *\n\t * In order to pick several types of mime types they need to be passed as an\n\t * array of strings.\n\t *\n\t * When no mime type filter is given only files can be selected. In order to\n\t * be able to select both files and folders \"['*', 'httpd\/unix-directory']\"\n\t * should be used instead.\n\t *\n\t * @param title dialog title\n\t * @param callback which will be triggered when user presses Choose\n\t * @param multiselect whether it should be possible to select multiple files\n\t * @param mimetypeFilter mimetype to filter by - directories will always be included\n\t * @param modal make the dialog modal\n\t * @param type Type of file picker : Choose, copy, move, copy and move\n\t*\/\n\tfilepicker:function(title, callback, multiselect, mimetypeFilter, modal, type) {\n\t\tvar self = this;\n\n\t\tthis.filepicker.sortField = 'name';\n\t\tthis.filepicker.sortOrder = 'asc';\n\t\t\/\/ avoid opening the picker twice\n\t\tif (this.filepicker.loading) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (type === undefined) {\n\t\t\ttype = this.FILEPICKER_TYPE_CHOOSE;\n\t\t}\n\n\t\tvar emptyText = t('core', 'No files in here');\n\t\tif (type === this.FILEPICKER_TYPE_COPY || type === this.FILEPICKER_TYPE_MOVE || type === this.FILEPICKER_TYPE_COPY_MOVE) {\n\t\t\temptyText = t('core', 'No more subfolders in here');\n\t\t}\n\n\t\tthis.filepicker.loading = true;\n\t\tthis.filepicker.filesClient = (OCA.Sharing && OCA.Sharing.PublicApp && OCA.Sharing.PublicApp.fileList)? OCA.Sharing.PublicApp.fileList.filesClient: OC.Files.getClient();\n\n\t\t$.when(this._getFilePickerTemplate()).then(function($tmpl) {\n\t\t\tself.filepicker.loading = false;\n\t\t\tvar dialogName = 'oc-dialog-filepicker-content';\n\t\t\tif(self.$filePicker) {\n\t\t\t\tself.$filePicker.ocdialog('close');\n\t\t\t}\n\n\t\t\tif (mimetypeFilter === undefined || mimetypeFilter === null) {\n\t\t\t\tmimetypeFilter = [];\n\t\t\t}\n\t\t\tif (typeof(mimetypeFilter) === \"string\") {\n\t\t\t\tmimetypeFilter = [mimetypeFilter];\n\t\t\t}\n\n\t\t\tself.$filePicker = $tmpl.octemplate({\n\t\t\t\tdialog_name: dialogName,\n\t\t\t\ttitle: title,\n\t\t\t\temptytext: emptyText\n\t\t\t}).data('path', '').data('multiselect', multiselect).data('mimetype', mimetypeFilter);\n\n\t\t\tif (modal === undefined) {\n\t\t\t\tmodal = false;\n\t\t\t}\n\t\t\tif (multiselect === undefined) {\n\t\t\t\tmultiselect = false;\n\t\t\t}\n\n\t\t\t\/\/ No grid for IE!\n\t\t\tif (OC.Util.isIE()) {\n\t\t\t\tself.$filePicker.find('#picker-view-toggle').remove();\n\t\t\t\tself.$filePicker.find('#picker-filestable').removeClass('view-grid');\n\t\t\t}\n\n\t\t\t$('body').append(self.$filePicker);\n\n\t\t\tself.$showGridView = $('input#picker-showgridview');\n\t\t\tself.$showGridView.on('change', _.bind(self._onGridviewChange, self));\n\n\t\t\tif (!OC.Util.isIE()) {\n\t\t\t\tself._getGridSettings();\n\t\t\t}\n\n\t\t\tself.$filePicker.ready(function() {\n\t\t\t\tself.$fileListHeader = self.$filePicker.find('.filelist thead tr');\n\t\t\t\tself.$filelist = self.$filePicker.find('.filelist tbody');\n\t\t\t\tself.$filelistContainer = self.$filePicker.find('.filelist-container');\n\t\t\t\tself.$dirTree = self.$filePicker.find('.dirtree');\n\t\t\t\tself.$dirTree.on('click', 'div:not(:last-child)', self, function (event) {\n\t\t\t\t\tself._handleTreeListSelect(event, type);\n\t\t\t\t});\n\t\t\t\tself.$filelist.on('click', 'tr', function(event) {\n\t\t\t\t\tself._handlePickerClick(event, $(this), type);\n\t\t\t\t});\n\t\t\t\tself.$fileListHeader.on('click', 'a', function(event) {\n\t\t\t\t\tvar dir = self.$filePicker.data('path');\n\t\t\t\t\tself.filepicker.sortField = $(event.currentTarget).data('sort');\n\t\t\t\t\tself.filepicker.sortOrder = self.filepicker.sortOrder === 'asc' ? 'desc' : 'asc';\n\t\t\t\t\tself._fillFilePicker(dir);\n\t\t\t\t});\n\t\t\t\tself._fillFilePicker('');\n\t\t\t});\n\n\t\t\t\/\/ build buttons\n\t\t\tvar functionToCall = function(returnType) {\n\t\t\t\tif (callback !== undefined) {\n\t\t\t\t\tvar datapath;\n\t\t\t\t\tif (multiselect === true) {\n\t\t\t\t\t\tdatapath = [];\n\t\t\t\t\t\tself.$filelist.find('tr.filepicker_element_selected').each(function(index, element) {\n\t\t\t\t\t\t\tdatapath.push(self.$filePicker.data('path') + '\/' + $(element).data('entryname'));\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdatapath = self.$filePicker.data('path');\n\t\t\t\t\t\tvar selectedName = self.$filelist.find('tr.filepicker_element_selected').data('entryname');\n\t\t\t\t\t\tif (selectedName) {\n\t\t\t\t\t\t\tdatapath += '\/' + selectedName;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcallback(datapath, returnType);\n\t\t\t\t\tself.$filePicker.ocdialog('close');\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tvar chooseCallback = function () {\n\t\t\t\tfunctionToCall(OCdialogs.FILEPICKER_TYPE_CHOOSE);\n\t\t\t};\n\n\t\t\tvar copyCallback = function () {\n\t\t\t\tfunctionToCall(OCdialogs.FILEPICKER_TYPE_COPY);\n\t\t\t};\n\n\t\t\tvar moveCallback = function () {\n\t\t\t\tfunctionToCall(OCdialogs.FILEPICKER_TYPE_MOVE);\n\t\t\t};\n\n\t\t\tvar buttonlist = [];\n\t\t\tif (type === OCdialogs.FILEPICKER_TYPE_CHOOSE) {\n\t\t\t\tbuttonlist.push({\n\t\t\t\t\ttext: t('core', 'Choose'),\n\t\t\t\t\tclick: chooseCallback,\n\t\t\t\t\tdefaultButton: true\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tif (type === OCdialogs.FILEPICKER_TYPE_COPY || type === OCdialogs.FILEPICKER_TYPE_COPY_MOVE) {\n\t\t\t\t\tbuttonlist.push({\n\t\t\t\t\t\ttext: t('core', 'Copy'),\n\t\t\t\t\t\tclick: copyCallback,\n\t\t\t\t\t\tdefaultButton: false\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (type === OCdialogs.FILEPICKER_TYPE_MOVE || type === OCdialogs.FILEPICKER_TYPE_COPY_MOVE) {\n\t\t\t\t\tbuttonlist.push({\n\t\t\t\t\t\ttext: t('core', 'Move'),\n\t\t\t\t\t\tclick: moveCallback,\n\t\t\t\t\t\tdefaultButton: true\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tself.$filePicker.ocdialog({\n\t\t\t\tcloseOnEscape: true,\n\t\t\t\t\/\/ max-width of 600\n\t\t\t\twidth: 600,\n\t\t\t\theight: 500,\n\t\t\t\tmodal: modal,\n\t\t\t\tbuttons: buttonlist,\n\t\t\t\tstyle: {\n\t\t\t\t\tbuttons: 'aside',\n\t\t\t\t},\n\t\t\t\tclose: function() {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t$(this).ocdialog('destroy').remove();\n\t\t\t\t\t} catch(e) {}\n\t\t\t\t\tself.$filePicker = null;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t\/\/ We can access primary class only from oc-dialog.\n\t\t\t\/\/ Hence this is one of the approach to get the choose button.\n\t\t\tvar getOcDialog = self.$filePicker.closest('.oc-dialog');\n\t\t\tvar buttonEnableDisable = getOcDialog.find('.primary');\n\t\t\tif (self.$filePicker.data('mimetype').indexOf(\"httpd\/unix-directory\") !== -1) {\n\t\t\t\tbuttonEnableDisable.prop(\"disabled\", false);\n\t\t\t} else {\n\t\t\t\tbuttonEnableDisable.prop(\"disabled\", true);\n\t\t\t}\n\t\t})\n\t\t.fail(function(status, error) {\n\t\t\t\/\/ If the method is called while navigating away\n\t\t\t\/\/ from the page, it is probably not needed ;)\n\t\t\tself.filepicker.loading = false;\n\t\t\tif(status !== 0) {\n\t\t\t\talert(t('core', 'Error loading file picker template: {error}', {error: error}));\n\t\t\t}\n\t\t});\n\t},\n\t\/**\n\t * Displays raw dialog\n\t * You better use a wrapper instead ...\n\t*\/\n\tmessage:function(content, title, dialogType, buttons, callback, modal, allowHtml) {\n\t\treturn $.when(this._getMessageTemplate()).then(function($tmpl) {\n\t\t\tvar dialogName = 'oc-dialog-' + OCdialogs.dialogsCounter + '-content';\n\t\t\tvar dialogId = '#' + dialogName;\n\t\t\tvar $dlg = $tmpl.octemplate({\n\t\t\t\tdialog_name: dialogName,\n\t\t\t\ttitle: title,\n\t\t\t\tmessage: content,\n\t\t\t\ttype: dialogType\n\t\t\t}, allowHtml ? {escapeFunction: ''} : {});\n\t\t\tif (modal === undefined) {\n\t\t\t\tmodal = false;\n\t\t\t}\n\t\t\t$('body').append($dlg);\n\t\t\tvar buttonlist = [];\n\t\t\tswitch (buttons) {\n\t\t\tcase OCdialogs.YES_NO_BUTTONS:\n\t\t\t\tbuttonlist = [{\n\t\t\t\t\ttext: t('core', 'No'),\n\t\t\t\t\tclick: function(){\n\t\t\t\t\t\tif (callback !== undefined) {\n\t\t\t\t\t\t\tcallback(false);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\ttext: t('core', 'Yes'),\n\t\t\t\t\tclick: function(){\n\t\t\t\t\t\tif (callback !== undefined) {\n\t\t\t\t\t\t\tcallback(true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\t},\n\t\t\t\t\tdefaultButton: true\n\t\t\t\t}];\n\t\t\t\tbreak;\n\t\t\tcase OCdialogs.OK_BUTTON:\n\t\t\t\tvar functionToCall = function() {\n\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\tif(callback !== undefined) {\n\t\t\t\t\t\tcallback();\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tbuttonlist[0] = {\n\t\t\t\t\ttext: t('core', 'OK'),\n\t\t\t\t\tclick: functionToCall,\n\t\t\t\t\tdefaultButton: true\n\t\t\t\t};\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t$(dialogId).ocdialog({\n\t\t\t\tcloseOnEscape: true,\n\t\t\t\tmodal: modal,\n\t\t\t\tbuttons: buttonlist\n\t\t\t});\n\t\t\tOCdialogs.dialogsCounter++;\n\t\t})\n\t\t.fail(function(status, error) {\n\t\t\t\/\/ If the method is called while navigating away from\n\t\t\t\/\/ the page, we still want to deliver the message.\n\t\t\tif(status === 0) {\n\t\t\t\talert(title + ': ' + content);\n\t\t\t} else {\n\t\t\t\talert(t('core', 'Error loading message template: {error}', {error: error}));\n\t\t\t}\n\t\t});\n\t},\n\t_fileexistsshown: false,\n\t\/**\n\t * Displays file exists dialog\n\t * @param {object} data upload object\n\t * @param {object} original file with name, size and mtime\n\t * @param {object} replacement file with name, size and mtime\n\t * @param {object} controller with onCancel, onSkip, onReplace and onRename methods\n\t * @return {Promise} jquery promise that resolves after the dialog template was loaded\n\t*\/\n\tfileexists:function(data, original, replacement, controller) {\n\t\tvar self = this;\n\t\tvar dialogDeferred = new $.Deferred();\n\n\t\tvar getCroppedPreview = function(file) {\n\t\t\tvar deferred = new $.Deferred();\n\t\t\t\/\/ Only process image files.\n\t\t\tvar type = file.type && file.type.split('\/').shift();\n\t\t\tif (window.FileReader && type === 'image') {\n\t\t\t\tvar reader = new FileReader();\n\t\t\t\treader.onload = function (e) {\n\t\t\t\t\tvar blob = new Blob([e.target.result]);\n\t\t\t\t\twindow.URL = window.URL || window.webkitURL;\n\t\t\t\t\tvar originalUrl = window.URL.createObjectURL(blob);\n\t\t\t\t\tvar image = new Image();\n\t\t\t\t\timage.src = originalUrl;\n\t\t\t\t\timage.onload = function () {\n\t\t\t\t\t\tvar url = crop(image);\n\t\t\t\t\t\tdeferred.resolve(url);\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t\treader.readAsArrayBuffer(file);\n\t\t\t} else {\n\t\t\t\tdeferred.reject();\n\t\t\t}\n\t\t\treturn deferred;\n\t\t};\n\n\t\tvar crop = function(img) {\n\t\t\tvar canvas = document.createElement('canvas'),\n\t\t\t\t\ttargetSize = 96,\n\t\t\t\t\twidth = img.width,\n\t\t\t\t\theight = img.height,\n\t\t\t\t\tx, y, size;\n\n\t\t\t\/\/ Calculate the width and height, constraining the proportions\n\t\t\tif (width > height) {\n\t\t\t\ty = 0;\n\t\t\t\tx = (width - height) \/ 2;\n\t\t\t} else {\n\t\t\t\ty = (height - width) \/ 2;\n\t\t\t\tx = 0;\n\t\t\t}\n\t\t\tsize = Math.min(width, height);\n\n\t\t\t\/\/ Set canvas size to the cropped area\n\t\t\tcanvas.width = size;\n\t\t\tcanvas.height = size;\n\t\t\tvar ctx = canvas.getContext(\"2d\");\n\t\t\tctx.drawImage(img, x, y, size, size, 0, 0, size, size);\n\n\t\t\t\/\/ Resize the canvas to match the destination (right size uses 96px)\n\t\t\tresampleHermite(canvas, size, size, targetSize, targetSize);\n\n\t\t\treturn canvas.toDataURL(\"image\/png\", 0.7);\n\t\t};\n\n\t\t\/**\n\t\t * Fast image resize\/resample using Hermite filter with JavaScript.\n\t\t *\n\t\t * @author: ViliusL\n\t\t *\n\t\t * @param {*} canvas\n\t\t * @param {number} W\n\t\t * @param {number} H\n\t\t * @param {number} W2\n\t\t * @param {number} H2\n\t\t *\/\n\t\tvar resampleHermite = function (canvas, W, H, W2, H2) {\n\t\t\tW2 = Math.round(W2);\n\t\t\tH2 = Math.round(H2);\n\t\t\tvar img = canvas.getContext(\"2d\").getImageData(0, 0, W, H);\n\t\t\tvar img2 = canvas.getContext(\"2d\").getImageData(0, 0, W2, H2);\n\t\t\tvar data = img.data;\n\t\t\tvar data2 = img2.data;\n\t\t\tvar ratio_w = W \/ W2;\n\t\t\tvar ratio_h = H \/ H2;\n\t\t\tvar ratio_w_half = Math.ceil(ratio_w \/ 2);\n\t\t\tvar ratio_h_half = Math.ceil(ratio_h \/ 2);\n\n\t\t\tfor (var j = 0; j < H2; j++) {\n\t\t\t\tfor (var i = 0; i < W2; i++) {\n\t\t\t\t\tvar x2 = (i + j * W2) * 4;\n\t\t\t\t\tvar weight = 0;\n\t\t\t\t\tvar weights = 0;\n\t\t\t\t\tvar weights_alpha = 0;\n\t\t\t\t\tvar gx_r = 0;\n\t\t\t\t\tvar gx_g = 0;\n\t\t\t\t\tvar gx_b = 0;\n\t\t\t\t\tvar gx_a = 0;\n\t\t\t\t\tvar center_y = (j + 0.5) * ratio_h;\n\t\t\t\t\tfor (var yy = Math.floor(j * ratio_h); yy < (j + 1) * ratio_h; yy++) {\n\t\t\t\t\t\tvar dy = Math.abs(center_y - (yy + 0.5)) \/ ratio_h_half;\n\t\t\t\t\t\tvar center_x = (i + 0.5) * ratio_w;\n\t\t\t\t\t\tvar w0 = dy * dy; \/\/pre-calc part of w\n\t\t\t\t\t\tfor (var xx = Math.floor(i * ratio_w); xx < (i + 1) * ratio_w; xx++) {\n\t\t\t\t\t\t\tvar dx = Math.abs(center_x - (xx + 0.5)) \/ ratio_w_half;\n\t\t\t\t\t\t\tvar w = Math.sqrt(w0 + dx * dx);\n\t\t\t\t\t\t\tif (w >= -1 && w <= 1) {\n\t\t\t\t\t\t\t\t\/\/hermite filter\n\t\t\t\t\t\t\t\tweight = 2 * w * w * w - 3 * w * w + 1;\n\t\t\t\t\t\t\t\tif (weight > 0) {\n\t\t\t\t\t\t\t\t\tdx = 4 * (xx + yy * W);\n\t\t\t\t\t\t\t\t\t\/\/alpha\n\t\t\t\t\t\t\t\t\tgx_a += weight * data[dx + 3];\n\t\t\t\t\t\t\t\t\tweights_alpha += weight;\n\t\t\t\t\t\t\t\t\t\/\/colors\n\t\t\t\t\t\t\t\t\tif (data[dx + 3] < 255)\n\t\t\t\t\t\t\t\t\t\tweight = weight * data[dx + 3] \/ 250;\n\t\t\t\t\t\t\t\t\tgx_r += weight * data[dx];\n\t\t\t\t\t\t\t\t\tgx_g += weight * data[dx + 1];\n\t\t\t\t\t\t\t\t\tgx_b += weight * data[dx + 2];\n\t\t\t\t\t\t\t\t\tweights += weight;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tdata2[x2] = gx_r \/ weights;\n\t\t\t\t\tdata2[x2 + 1] = gx_g \/ weights;\n\t\t\t\t\tdata2[x2 + 2] = gx_b \/ weights;\n\t\t\t\t\tdata2[x2 + 3] = gx_a \/ weights_alpha;\n\t\t\t\t}\n\t\t\t}\n\t\t\tcanvas.getContext(\"2d\").clearRect(0, 0, Math.max(W, W2), Math.max(H, H2));\n\t\t\tcanvas.width = W2;\n\t\t\tcanvas.height = H2;\n\t\t\tcanvas.getContext(\"2d\").putImageData(img2, 0, 0);\n\t\t};\n\n\t\tvar addConflict = function($conflicts, original, replacement) {\n\n\t\t\tvar $conflict = $conflicts.find('.template').clone().removeClass('template').addClass('conflict');\n\t\t\tvar $originalDiv = $conflict.find('.original');\n\t\t\tvar $replacementDiv = $conflict.find('.replacement');\n\n\t\t\t$conflict.data('data',data);\n\n\t\t\t$conflict.find('.filename').text(original.name);\n\t\t\t$originalDiv.find('.size').text(humanFileSize(original.size));\n\t\t\t$originalDiv.find('.mtime').text(formatDate(original.mtime));\n\t\t\t\/\/ ie sucks\n\t\t\tif (replacement.size && replacement.lastModifiedDate) {\n\t\t\t\t$replacementDiv.find('.size').text(humanFileSize(replacement.size));\n\t\t\t\t$replacementDiv.find('.mtime').text(formatDate(replacement.lastModifiedDate));\n\t\t\t}\n\t\t\tvar path = original.directory + '\/' +original.name;\n\t\t\tvar urlSpec = {\n\t\t\t\tfile:\t\tpath,\n\t\t\t\tx:\t\t96,\n\t\t\t\ty:\t\t96,\n\t\t\t\tc:\t\toriginal.etag,\n\t\t\t\tforceIcon:\t0\n\t\t\t};\n\t\t\tvar previewpath = Files.generatePreviewUrl(urlSpec);\n\t\t\t\/\/ Escaping single quotes\n\t\t\tpreviewpath = previewpath.replace(\/'\/g, \"%27\");\n\t\t\t$originalDiv.find('.icon').css({\"background-image\": \"url('\" + previewpath + \"')\"});\n\t\t\tgetCroppedPreview(replacement).then(\n\t\t\t\tfunction(path){\n\t\t\t\t\t$replacementDiv.find('.icon').css('background-image','url(' + path + ')');\n\t\t\t\t}, function(){\n\t\t\t\t\tpath = OC.MimeType.getIconUrl(replacement.type);\n\t\t\t\t\t$replacementDiv.find('.icon').css('background-image','url(' + path + ')');\n\t\t\t\t}\n\t\t\t);\n\t\t\t\/\/ connect checkboxes with labels\n\t\t\tvar checkboxId = $conflicts.find('.conflict').length;\n\t\t\t$originalDiv.find('input:checkbox').attr('id', 'checkbox_original_'+checkboxId);\n\t\t\t$replacementDiv.find('input:checkbox').attr('id', 'checkbox_replacement_'+checkboxId);\n\n\t\t\t$conflicts.append($conflict);\n\n\t\t\t\/\/set more recent mtime bold\n\t\t\t\/\/ ie sucks\n\t\t\tif (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() > original.mtime) {\n\t\t\t\t$replacementDiv.find('.mtime').css('font-weight', 'bold');\n\t\t\t} else if (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() < original.mtime) {\n\t\t\t\t$originalDiv.find('.mtime').css('font-weight', 'bold');\n\t\t\t} else {\n\t\t\t\t\/\/TODO add to same mtime collection?\n\t\t\t}\n\n\t\t\t\/\/ set bigger size bold\n\t\t\tif (replacement.size && replacement.size > original.size) {\n\t\t\t\t$replacementDiv.find('.size').css('font-weight', 'bold');\n\t\t\t} else if (replacement.size && replacement.size < original.size) {\n\t\t\t\t$originalDiv.find('.size').css('font-weight', 'bold');\n\t\t\t} else {\n\t\t\t\t\/\/TODO add to same size collection?\n\t\t\t}\n\n\t\t\t\/\/TODO show skip action for files with same size and mtime in bottom row\n\n\t\t\t\/\/ always keep readonly files\n\n\t\t\tif (original.status === 'readonly') {\n\t\t\t\t$originalDiv\n\t\t\t\t\t.addClass('readonly')\n\t\t\t\t\t.find('input[type=\"checkbox\"]')\n\t\t\t\t\t\t.prop('checked', true)\n\t\t\t\t\t\t.prop('disabled', true);\n\t\t\t\t$originalDiv.find('.message')\n\t\t\t\t\t.text(t('core','read-only'));\n\t\t\t}\n\t\t};\n\t\t\/\/var selection = controller.getSelection(data.originalFiles);\n\t\t\/\/if (selection.defaultAction) {\n\t\t\/\/\tcontroller[selection.defaultAction](data);\n\t\t\/\/} else {\n\t\tvar dialogName = 'oc-dialog-fileexists-content';\n\t\tvar dialogId = '#' + dialogName;\n\t\tif (this._fileexistsshown) {\n\t\t\t\/\/ add conflict\n\n\t\t\tvar $conflicts = $(dialogId+ ' .conflicts');\n\t\t\taddConflict($conflicts, original, replacement);\n\n\t\t\tvar count = $(dialogId+ ' .conflict').length;\n\t\t\tvar title = n('core',\n\t\t\t\t\t\t\t'{count} file conflict',\n\t\t\t\t\t\t\t'{count} file conflicts',\n\t\t\t\t\t\t\tcount,\n\t\t\t\t\t\t\t{count:count}\n\t\t\t\t\t\t);\n\t\t\t$(dialogId).parent().children('.oc-dialog-title').text(title);\n\n\t\t\t\/\/recalculate dimensions\n\t\t\t$(window).trigger('resize');\n\t\t\tdialogDeferred.resolve();\n\t\t} else {\n\t\t\t\/\/create dialog\n\t\t\tthis._fileexistsshown = true;\n\t\t\t$.when(this._getFileExistsTemplate()).then(function($tmpl) {\n\t\t\t\tvar title = t('core','One file conflict');\n\t\t\t\tvar $dlg = $tmpl.octemplate({\n\t\t\t\t\tdialog_name: dialogName,\n\t\t\t\t\ttitle: title,\n\t\t\t\t\ttype: 'fileexists',\n\n\t\t\t\t\tallnewfiles: t('core','New Files'),\n\t\t\t\t\tallexistingfiles: t('core','Already existing files'),\n\n\t\t\t\t\twhy: t('core','Which files do you want to keep?'),\n\t\t\t\t\twhat: t('core','If you select both versions, the copied file will have a number added to its name.')\n\t\t\t\t});\n\t\t\t\t$('body').append($dlg);\n\n\t\t\t\tif (original && replacement) {\n\t\t\t\t\tvar $conflicts = $dlg.find('.conflicts');\n\t\t\t\t\taddConflict($conflicts, original, replacement);\n\t\t\t\t}\n\n\t\t\t\tvar buttonlist = [{\n\t\t\t\t\t\ttext: t('core', 'Cancel'),\n\t\t\t\t\t\tclasses: 'cancel',\n\t\t\t\t\t\tclick: function(){\n\t\t\t\t\t\t\tif ( typeof controller.onCancel !== 'undefined') {\n\t\t\t\t\t\t\t\tcontroller.onCancel(data);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttext: t('core', 'Continue'),\n\t\t\t\t\t\tclasses: 'continue',\n\t\t\t\t\t\tclick: function(){\n\t\t\t\t\t\t\tif ( typeof controller.onContinue !== 'undefined') {\n\t\t\t\t\t\t\t\tcontroller.onContinue($(dialogId + ' .conflict'));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t$(dialogId).ocdialog('close');\n\t\t\t\t\t\t}\n\t\t\t\t\t}];\n\n\t\t\t\t$(dialogId).ocdialog({\n\t\t\t\t\twidth: 500,\n\t\t\t\t\tcloseOnEscape: true,\n\t\t\t\t\tmodal: true,\n\t\t\t\t\tbuttons: buttonlist,\n\t\t\t\t\tcloseButton: null,\n\t\t\t\t\tclose: function() {\n\t\t\t\t\t\t\tself._fileexistsshown = false;\n\t\t\t\t\t\t\t$(this).ocdialog('destroy').remove();\n\t\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\t$(dialogId).css('height','auto');\n\n\t\t\t\tvar $primaryButton = $dlg.closest('.oc-dialog').find('button.continue');\n\t\t\t\t$primaryButton.prop('disabled', true);\n\n\t\t\t\tfunction updatePrimaryButton() {\n\t\t\t\t\tvar checkedCount = $dlg.find('.conflicts .checkbox:checked').length;\n\t\t\t\t\t$primaryButton.prop('disabled', checkedCount === 0);\n\t\t\t\t}\n\n\t\t\t\t\/\/add checkbox toggling actions\n\t\t\t\t$(dialogId).find('.allnewfiles').on('click', function() {\n\t\t\t\t\tvar $checkboxes = $(dialogId).find('.conflict .replacement input[type=\"checkbox\"]');\n\t\t\t\t\t$checkboxes.prop('checked', $(this).prop('checked'));\n\t\t\t\t});\n\t\t\t\t$(dialogId).find('.allexistingfiles').on('click', function() {\n\t\t\t\t\tvar $checkboxes = $(dialogId).find('.conflict .original:not(.readonly) input[type=\"checkbox\"]');\n\t\t\t\t\t$checkboxes.prop('checked', $(this).prop('checked'));\n\t\t\t\t});\n\t\t\t\t$(dialogId).find('.conflicts').on('click', '.replacement,.original:not(.readonly)', function() {\n\t\t\t\t\tvar $checkbox = $(this).find('input[type=\"checkbox\"]');\n\t\t\t\t\t$checkbox.prop('checked', !$checkbox.prop('checked'));\n\t\t\t\t});\n\t\t\t\t$(dialogId).find('.conflicts').on('click', '.replacement input[type=\"checkbox\"],.original:not(.readonly) input[type=\"checkbox\"]', function() {\n\t\t\t\t\tvar $checkbox = $(this);\n\t\t\t\t\t$checkbox.prop('checked', !$checkbox.prop('checked'));\n\t\t\t\t});\n\n\t\t\t\t\/\/update counters\n\t\t\t\t$(dialogId).on('click', '.replacement,.allnewfiles', function() {\n\t\t\t\t\tvar count = $(dialogId).find('.conflict .replacement input[type=\"checkbox\"]:checked').length;\n\t\t\t\t\tif (count === $(dialogId+ ' .conflict').length) {\n\t\t\t\t\t\t$(dialogId).find('.allnewfiles').prop('checked', true);\n\t\t\t\t\t\t$(dialogId).find('.allnewfiles + .count').text(t('core','(all selected)'));\n\t\t\t\t\t} else if (count > 0) {\n\t\t\t\t\t\t$(dialogId).find('.allnewfiles').prop('checked', false);\n\t\t\t\t\t\t$(dialogId).find('.allnewfiles + .count').text(t('core','({count} selected)',{count:count}));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$(dialogId).find('.allnewfiles').prop('checked', false);\n\t\t\t\t\t\t$(dialogId).find('.allnewfiles + .count').text('');\n\t\t\t\t\t}\n\t\t\t\t\tupdatePrimaryButton();\n\t\t\t\t});\n\t\t\t\t$(dialogId).on('click', '.original,.allexistingfiles', function(){\n\t\t\t\t\tvar count = $(dialogId).find('.conflict .original input[type=\"checkbox\"]:checked').length;\n\t\t\t\t\tif (count === $(dialogId+ ' .conflict').length) {\n\t\t\t\t\t\t$(dialogId).find('.allexistingfiles').prop('checked', true);\n\t\t\t\t\t\t$(dialogId).find('.allexistingfiles + .count').text(t('core','(all selected)'));\n\t\t\t\t\t} else if (count > 0) {\n\t\t\t\t\t\t$(dialogId).find('.allexistingfiles').prop('checked', false);\n\t\t\t\t\t\t$(dialogId).find('.allexistingfiles + .count')\n\t\t\t\t\t\t\t.text(t('core','({count} selected)',{count:count}));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$(dialogId).find('.allexistingfiles').prop('checked', false);\n\t\t\t\t\t\t$(dialogId).find('.allexistingfiles + .count').text('');\n\t\t\t\t\t}\n\t\t\t\t\tupdatePrimaryButton();\n\t\t\t\t});\n\n\t\t\t\tdialogDeferred.resolve();\n\t\t\t})\n\t\t\t.fail(function() {\n\t\t\t\tdialogDeferred.reject();\n\t\t\t\talert(t('core', 'Error loading file exists template'));\n\t\t\t});\n\t\t}\n\t\t\/\/}\n\t\treturn dialogDeferred.promise();\n\t},\n\t\/\/ get the gridview setting and set the input accordingly\n\t_getGridSettings: function() {\n\t\tvar self = this;\n\t\t$.get(OC.generateUrl('\/apps\/files\/api\/v1\/showgridview'), function(response) {\n\t\t\tself.$showGridView.get(0).checked = response.gridview;\n\t\t\tself.$showGridView.next('#picker-view-toggle')\n\t\t\t\t.removeClass('icon-toggle-filelist icon-toggle-pictures')\n\t\t\t\t.addClass(response.gridview ? 'icon-toggle-filelist' : 'icon-toggle-pictures')\n\t\t\t$('.list-container').toggleClass('view-grid', response.gridview);\n\t\t});\n\t},\n\t_onGridviewChange: function() {\n\t\tvar show = this.$showGridView.is(':checked');\n\t\t\/\/ only save state if user is logged in\n\t\tif (OC.currentUser) {\n\t\t\t$.post(OC.generateUrl('\/apps\/files\/api\/v1\/showgridview'), {\n\t\t\t\tshow: show\n\t\t\t});\n\t\t}\n\t\tthis.$showGridView.next('#picker-view-toggle')\n\t\t\t.removeClass('icon-toggle-filelist icon-toggle-pictures')\n\t\t\t.addClass(show ? 'icon-toggle-filelist' : 'icon-toggle-pictures')\n\t\t$('.list-container').toggleClass('view-grid', show);\n\t},\n\t_getFilePickerTemplate: function() {\n\t\tvar defer = $.Deferred();\n\t\tif(!this.$filePickerTemplate) {\n\t\t\tvar self = this;\n\t\t\t$.get(OC.filePath('core', 'templates', 'filepicker.html'), function(tmpl) {\n\t\t\t\tself.$filePickerTemplate = $(tmpl);\n\t\t\t\tself.$listTmpl = self.$filePickerTemplate.find('.filelist tbody tr:first-child').detach();\n\t\t\t\tdefer.resolve(self.$filePickerTemplate);\n\t\t\t})\n\t\t\t.fail(function(jqXHR, textStatus, errorThrown) {\n\t\t\t\tdefer.reject(jqXHR.status, errorThrown);\n\t\t\t});\n\t\t} else {\n\t\t\tdefer.resolve(this.$filePickerTemplate);\n\t\t}\n\t\treturn defer.promise();\n\t},\n\t_getMessageTemplate: function() {\n\t\tvar defer = $.Deferred();\n\t\tif(!this.$messageTemplate) {\n\t\t\tvar self = this;\n\t\t\t$.get(OC.filePath('core', 'templates', 'message.html'), function(tmpl) {\n\t\t\t\tself.$messageTemplate = $(tmpl);\n\t\t\t\tdefer.resolve(self.$messageTemplate);\n\t\t\t})\n\t\t\t.fail(function(jqXHR, textStatus, errorThrown) {\n\t\t\t\tdefer.reject(jqXHR.status, errorThrown);\n\t\t\t});\n\t\t} else {\n\t\t\tdefer.resolve(this.$messageTemplate);\n\t\t}\n\t\treturn defer.promise();\n\t},\n\t_getFileExistsTemplate: function () {\n\t\tvar defer = $.Deferred();\n\t\tif (!this.$fileexistsTemplate) {\n\t\t\tvar self = this;\n\t\t\t$.get(OC.filePath('files', 'templates', 'fileexists.html'), function (tmpl) {\n\t\t\t\tself.$fileexistsTemplate = $(tmpl);\n\t\t\t\tdefer.resolve(self.$fileexistsTemplate);\n\t\t\t})\n\t\t\t.fail(function () {\n\t\t\t\tdefer.reject();\n\t\t\t});\n\t\t} else {\n\t\t\tdefer.resolve(this.$fileexistsTemplate);\n\t\t}\n\t\treturn defer.promise();\n\t},\n\t_getFileList: function(dir, mimeType) { \/\/this is only used by the spreedme app atm\n\t\tif (typeof(mimeType) === \"string\") {\n\t\t\tmimeType = [mimeType];\n\t\t}\n\n\t\treturn $.getJSON(\n\t\t\tOC.filePath('files', 'ajax', 'list.php'),\n\t\t\t{\n\t\t\t\tdir: dir,\n\t\t\t\tmimetypes: JSON.stringify(mimeType)\n\t\t\t}\n\t\t);\n\t},\n\n\t\/**\n\t * fills the filepicker with files\n\t*\/\n\t_fillFilePicker:function(dir) {\n\t\tvar self = this;\n\t\tthis.$filelist.empty();\n\t\tthis.$filePicker.find('.emptycontent').hide();\n\t\tthis.$filelistContainer.addClass('icon-loading');\n\t\tthis.$filePicker.data('path', dir);\n\t\tvar filter = this.$filePicker.data('mimetype');\n\t\tif (typeof(filter) === \"string\") {\n\t\t\tfilter = [filter];\n\t\t}\n\t\tself.$fileListHeader.find('.sort-indicator').addClass('hidden').removeClass('icon-triangle-n').removeClass('icon-triangle-s');\n\t\tself.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').removeClass('hidden');\n\t\tif (self.filepicker.sortOrder === 'asc') {\n\t\t\tself.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-n');\n\t\t} else {\n\t\t\tself.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-s');\n\t\t}\n\t\tself.filepicker.filesClient.getFolderContents(dir).then(function(status, files) {\n\t\t\tif (filter && filter.length > 0 && filter.indexOf('*') === -1) {\n\t\t\t\tfiles = files.filter(function (file) {\n\t\t\t\t\treturn file.type === 'dir' || filter.indexOf(file.mimetype) !== -1;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tvar Comparators = {\n\t\t\t\tname: function(fileInfo1, fileInfo2) {\n\t\t\t\t\tif (fileInfo1.type === 'dir' && fileInfo2.type !== 'dir') {\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t\tif (fileInfo1.type !== 'dir' && fileInfo2.type === 'dir') {\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\treturn OC.Util.naturalSortCompare(fileInfo1.name, fileInfo2.name);\n\t\t\t\t},\n\t\t\t\tsize: function(fileInfo1, fileInfo2) {\n\t\t\t\t\treturn fileInfo1.size - fileInfo2.size;\n\t\t\t\t},\n\t\t\t\tmtime: function(fileInfo1, fileInfo2) {\n\t\t\t\t\treturn fileInfo1.mtime - fileInfo2.mtime;\n\t\t\t\t}\n\t\t\t};\n\t\t\tvar comparator = Comparators[self.filepicker.sortField] || Comparators.name;\n\t\t\tfiles = files.sort(function(file1, file2) {\n\t\t\t\tvar isFavorite = function(fileInfo) {\n\t\t\t\t\treturn fileInfo.tags && fileInfo.tags.indexOf(OC.TAG_FAVORITE) >= 0;\n\t\t\t\t};\n\n\t\t\t\tif (isFavorite(file1) && !isFavorite(file2)) {\n\t\t\t\t\treturn -1;\n\t\t\t\t} else if (!isFavorite(file1) && isFavorite(file2)) {\n\t\t\t\t\treturn 1;\n\t\t\t\t}\n\n\t\t\t\treturn self.filepicker.sortOrder === 'asc' ? comparator(file1, file2) : -comparator(file1, file2);\n\t\t\t});\n\n\t\t\tself._fillSlug();\n\n\t\t\tif (files.length === 0) {\n\t\t\t\tself.$filePicker.find('.emptycontent').show();\n\t\t\t\tself.$fileListHeader.hide();\n\t\t\t} else {\n\t\t\t\tself.$filePicker.find('.emptycontent').hide();\n\t\t\t\tself.$fileListHeader.show();\n\t\t\t}\n\n\t\t\t$.each(files, function(idx, entry) {\n\t\t\t\tentry.icon = OC.MimeType.getIconUrl(entry.mimetype);\n\t\t\t\tvar simpleSize, sizeColor;\n\t\t\t\tif (typeof(entry.size) !== 'undefined' && entry.size >= 0) {\n\t\t\t\t\tsimpleSize = humanFileSize(parseInt(entry.size, 10), true);\n\t\t\t\t\tsizeColor = Math.round(160 - Math.pow((entry.size \/ (1024 * 1024)), 2));\n\t\t\t\t} else {\n\t\t\t\t\tsimpleSize = t('files', 'Pending');\n\t\t\t\t\tsizeColor = 80;\n\t\t\t\t}\n\t\t\t\tvar $row = self.$listTmpl.octemplate({\n\t\t\t\t\ttype: entry.type,\n\t\t\t\t\tdir: dir,\n\t\t\t\t\tfilename: entry.name,\n\t\t\t\t\tdate: OC.Util.relativeModifiedDate(entry.mtime),\n\t\t\t\t\tsize: simpleSize,\n\t\t\t\t\tsizeColor: sizeColor,\n\t\t\t\t\ticon: entry.icon\n\t\t\t\t});\n\t\t\t\tif (entry.type === 'file') {\n\t\t\t\t\tvar urlSpec = {\n\t\t\t\t\t\tfile: dir + '\/' + entry.name,\n\t\t\t\t\t\tx: 100,\n\t\t\t\t\t\ty: 100\n\t\t\t\t\t};\n\t\t\t\t\tvar img = new Image();\n\t\t\t\t\tvar previewUrl = OC.generateUrl('\/core\/preview.png?') + $.param(urlSpec);\n\t\t\t\t\timg.onload = function() {\n\t\t\t\t\t\tif (img.width > 5) {\n\t\t\t\t\t\t\t$row.find('td.filename').attr('style', 'background-image:url(' + previewUrl + ')');\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t\timg.src = previewUrl;\n\t\t\t\t}\n\t\t\t\tself.$filelist.append($row);\n\t\t\t});\n\n\t\t\tself.$filelistContainer.removeClass('icon-loading');\n\t\t});\n\t},\n\t\/**\n\t * fills the tree list with directories\n\t*\/\n\t_fillSlug: function() {\n\t\tthis.$dirTree.empty();\n\t\tvar self = this;\n\t\tvar dir;\n\t\tvar path = this.$filePicker.data('path');\n\t\tvar $template = $('<div data-dir=\"{dir}\"><a>{name}<\/a><\/div>').addClass('crumb');\n\t\tif(path) {\n\t\t\tvar paths = path.split('\/');\n\t\t\t$.each(paths, function(index, dir) {\n\t\t\t\tdir = paths.pop();\n\t\t\t\tif(dir === '') {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tself.$dirTree.prepend($template.octemplate({\n\t\t\t\t\tdir: paths.join('\/') + '\/' + dir,\n\t\t\t\t\tname: dir\n\t\t\t\t}));\n\t\t\t});\n\t\t}\n\t\t$template.octemplate({\n\t\t\tdir: '',\n\t\t\tname: '' \/\/ Ugly but works ;)\n\t\t}, {escapeFunction: null}).prependTo(this.$dirTree);\n\t},\n\t\/**\n\t * handle selection made in the tree list\n\t*\/\n\t_handleTreeListSelect:function(event, type) {\n\t\tvar self = event.data;\n\t\tvar dir = $(event.target).closest('.crumb').data('dir');\n\t\tself._fillFilePicker(dir);\n\t\tvar getOcDialog = (event.target).closest('.oc-dialog');\n\t\tvar buttonEnableDisable = $('.primary', getOcDialog);\n\t\tthis._changeButtonsText(type, dir.split(\/[\/]+\/).pop());\n\t\tif (this.$filePicker.data('mimetype').indexOf(\"httpd\/unix-directory\") !== -1) {\n\t\t\tbuttonEnableDisable.prop(\"disabled\", false);\n\t\t} else {\n\t\t\tbuttonEnableDisable.prop(\"disabled\", true);\n\t\t}\n\t},\n\t\/**\n\t * handle clicks made in the filepicker\n\t*\/\n\t_handlePickerClick:function(event, $element, type) {\n\t\tvar getOcDialog = this.$filePicker.closest('.oc-dialog');\n\t\tvar buttonEnableDisable = getOcDialog.find('.primary');\n\t\tif ($element.data('type') === 'file') {\n\t\t\tif (this.$filePicker.data('multiselect') !== true || !event.ctrlKey) {\n\t\t\t\tthis.$filelist.find('.filepicker_element_selected').removeClass('filepicker_element_selected');\n\t\t\t}\n\t\t\t$element.toggleClass('filepicker_element_selected');\n\t\t\tbuttonEnableDisable.prop(\"disabled\", false);\n\t\t} else if ( $element.data('type') === 'dir' ) {\n\t\t\tthis._fillFilePicker(this.$filePicker.data('path') + '\/' + $element.data('entryname'));\n\t\t\tthis._changeButtonsText(type, $element.data('entryname'));\n\t\t\tif (this.$filePicker.data('mimetype').indexOf(\"httpd\/unix-directory\") !== -1) {\n\t\t\t\tbuttonEnableDisable.prop(\"disabled\", false);\n\t\t\t} else {\n\t\t\t\tbuttonEnableDisable.prop(\"disabled\", true);\n\t\t\t}\n\t\t}\n\t},\n\n\t\/**\n\t * Handle\n\t * @param type of action\n\t * @param dir on which to change buttons text\n\t * @private\n\t *\/\n\t_changeButtonsText: function(type, dir) {\n\t\tvar copyText = dir === '' ? t('core', 'Copy') : t('core', 'Copy to {folder}', {folder: dir});\n\t\tvar moveText = dir === '' ? t('core', 'Move') : t('core', 'Move to {folder}', {folder: dir});\n\t\tvar buttons = $('.oc-dialog-buttonrow button');\n\t\tswitch (type) {\n\t\t\tcase this.FILEPICKER_TYPE_CHOOSE:\n\t\t\t\tbreak;\n\t\t\tcase this.FILEPICKER_TYPE_COPY:\n\t\t\t\tbuttons.text(copyText);\n\t\t\t\tbreak;\n\t\t\tcase this.FILEPICKER_TYPE_MOVE:\n\t\t\t\tbuttons.text(moveText);\n\t\t\t\tbreak;\n\t\t\tcase this.FILEPICKER_TYPE_COPY_MOVE:\n\t\t\t\tbuttons.eq(0).text(copyText);\n\t\t\t\tbuttons.eq(1).text(moveText);\n\t\t\t\tbreak;\n\t\t}\n\t}\n};\n\n\n\/* global oc_isadmin *\/\n\nvar oc_debug;\nvar oc_webroot;\n\nvar oc_current_user = document.getElementsByTagName('head')[0].getAttribute('data-user');\nvar oc_requesttoken = document.getElementsByTagName('head')[0].getAttribute('data-requesttoken');\n\nwindow.oc_config = window.oc_config || {};\n\nif (typeof oc_webroot === \"undefined\") {\n\toc_webroot = location.pathname;\n\tvar pos = oc_webroot.indexOf('\/index.php\/');\n\tif (pos !== -1) {\n\t\toc_webroot = oc_webroot.substr(0, pos);\n\t}\n\telse {\n\t\toc_webroot = oc_webroot.substr(0, oc_webroot.lastIndexOf('\/'));\n\t}\n}\nif (typeof console === \"undefined\" || typeof console.log === \"undefined\") {\n\tif (!window.console) {\n\t\twindow.console = {};\n\t}\n\tvar noOp = function() { };\n\tvar methods = ['log', 'debug', 'warn', 'info', 'error', 'assert', 'time', 'timeEnd'];\n\tfor (var i = 0; i < methods.length; i++) {\n\t\tconsole[methods[i]] = noOp;\n\t}\n}\n\n\/**\n* Sanitizes a HTML string by replacing all potential dangerous characters with HTML entities\n* @param {string} s String to sanitize\n* @return {string} Sanitized string\n*\/\nfunction escapeHTML(s) {\n\treturn s.toString().split('&').join('&amp;').split('<').join('&lt;').split('>').join('&gt;').split('\"').join('&quot;').split('\\'').join('&#039;');\n}\n\n\/** @namespace OCP *\/\nvar OCP = {},\n\t\/**\n\t * @namespace OC\n\t *\/\n\tOC = {\n\tPERMISSION_NONE:0,\n\tPERMISSION_CREATE:4,\n\tPERMISSION_READ:1,\n\tPERMISSION_UPDATE:2,\n\tPERMISSION_DELETE:8,\n\tPERMISSION_SHARE:16,\n\tPERMISSION_ALL:31,\n\tTAG_FAVORITE: '_$!<Favorite>!$_',\n\t\/* jshint camelcase: false *\/\n\t\/**\n\t * Relative path to Nextcloud root.\n\t * For example: \"\/nextcloud\"\n\t *\n\t * @type string\n\t *\n\t * @deprecated since 8.2, use OC.getRootPath() instead\n\t * @see OC#getRootPath\n\t *\/\n\twebroot:oc_webroot,\n\n\t\/**\n\t * Capabilities\n\t *\n\t * @type array\n\t *\/\n\t_capabilities: window.oc_capabilities || null,\n\n\tappswebroots:(typeof oc_appswebroots !== 'undefined') ? oc_appswebroots:false,\n\t\/**\n\t * Currently logged in user or null if none\n\t *\n\t * @type String\n\t * @deprecated use {@link OC.getCurrentUser} instead\n\t *\/\n\tcurrentUser:(typeof oc_current_user!=='undefined')?oc_current_user:false,\n\tconfig: window.oc_config,\n\tappConfig: window.oc_appconfig || {},\n\ttheme: window.oc_defaults || {},\n\tcoreApps:['', 'admin','log','core\/search','settings','core','3rdparty'],\n\trequestToken: oc_requesttoken,\n\tmenuSpeed: 50,\n\n\t\/**\n\t * Get an absolute url to a file in an app\n\t * @param {string} app the id of the app the file belongs to\n\t * @param {string} file the file path relative to the app folder\n\t * @return {string} Absolute URL to a file\n\t *\/\n\tlinkTo:function(app,file){\n\t\treturn OC.filePath(app,'',file);\n\t},\n\n\t\/**\n\t * Creates a relative url for remote use\n\t * @param {string} service id\n\t * @return {string} the url\n\t *\/\n\tlinkToRemoteBase:function(service) {\n\t\treturn OC.getRootPath() + '\/remote.php\/' + service;\n\t},\n\n\t\/**\n\t * @brief Creates an absolute url for remote use\n\t * @param {string} service id\n\t * @return {string} the url\n\t *\/\n\tlinkToRemote:function(service) {\n\t\treturn window.location.protocol + '\/\/' + window.location.host + OC.linkToRemoteBase(service);\n\t},\n\n\t\/**\n\t * Gets the base path for the given OCS API service.\n\t * @param {string} service name\n\t * @param {int} version OCS API version\n\t * @return {string} OCS API base path\n\t *\/\n\tlinkToOCS: function(service, version) {\n\t\tversion = (version !== 2) ? 1 : 2;\n\t\treturn window.location.protocol + '\/\/' + window.location.host + OC.getRootPath() + '\/ocs\/v' + version + '.php\/' + service + '\/';\n\t},\n\n\t\/**\n\t * Generates the absolute url for the given relative url, which can contain parameters.\n\t * Parameters will be URL encoded automatically.\n\t * @param {string} url\n\t * @param [params] params\n\t * @param [options] options\n\t * @param {bool} [options.escape=true] enable\/disable auto escape of placeholders (by default enabled)\n\t * @return {string} Absolute URL for the given relative URL\n\t *\/\n\tgenerateUrl: function(url, params, options) {\n\t\tvar defaultOptions = {\n\t\t\t\tescape: true\n\t\t\t},\n\t\t\tallOptions = options || {};\n\t\t_.defaults(allOptions, defaultOptions);\n\n\t\tvar _build = function (text, vars) {\n\t\t\tvars = vars || [];\n\t\t\treturn text.replace(\/{([^{}]*)}\/g,\n\t\t\t\tfunction (a, b) {\n\t\t\t\t\tvar r = (vars[b]);\n\t\t\t\t\tif(allOptions.escape) {\n\t\t\t\t\t\treturn (typeof r === 'string' || typeof r === 'number') ? encodeURIComponent(r) : encodeURIComponent(a);\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn (typeof r === 'string' || typeof r === 'number') ? r : a;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\t\t};\n\t\tif (url.charAt(0) !== '\/') {\n\t\t\turl = '\/' + url;\n\n\t\t}\n\n\t\tif(oc_config.modRewriteWorking == true) {\n\t\t\treturn OC.getRootPath() + _build(url, params);\n\t\t}\n\n\t\treturn OC.getRootPath() + '\/index.php' + _build(url, params);\n\t},\n\n\t\/**\n\t * Get the absolute url for a file in an app\n\t * @param {string} app the id of the app\n\t * @param {string} type the type of the file to link to (e.g. css,img,ajax.template)\n\t * @param {string} file the filename\n\t * @return {string} Absolute URL for a file in an app\n\t *\/\n\tfilePath:function(app,type,file){\n\t\tvar isCore=OC.coreApps.indexOf(app)!==-1,\n\t\t\tlink=OC.getRootPath();\n\t\tif(file.substring(file.length-3) === 'php' && !isCore){\n\t\t\tlink+='\/index.php\/apps\/' + app;\n\t\t\tif (file != 'index.php') {\n\t\t\t\tlink+='\/';\n\t\t\t\tif(type){\n\t\t\t\t\tlink+=encodeURI(type + '\/');\n\t\t\t\t}\n\t\t\t\tlink+= file;\n\t\t\t}\n\t\t}else if(file.substring(file.length-3) !== 'php' && !isCore){\n\t\t\tlink=OC.appswebroots[app];\n\t\t\tif(type){\n\t\t\t\tlink+= '\/'+type+'\/';\n\t\t\t}\n\t\t\tif(link.substring(link.length-1) !== '\/'){\n\t\t\t\tlink+='\/';\n\t\t\t}\n\t\t\tlink+=file;\n\t\t}else{\n\t\t\tif ((app == 'settings' || app == 'core' || app == 'search') && type == 'ajax') {\n\t\t\t\tlink+='\/index.php\/';\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlink+='\/';\n\t\t\t}\n\t\t\tif(!isCore){\n\t\t\t\tlink+='apps\/';\n\t\t\t}\n\t\t\tif (app !== '') {\n\t\t\t\tapp+='\/';\n\t\t\t\tlink+=app;\n\t\t\t}\n\t\t\tif(type){\n\t\t\t\tlink+=type+'\/';\n\t\t\t}\n\t\t\tlink+=file;\n\t\t}\n\t\treturn link;\n\t},\n\n\t\/**\n\t * Check if a user file is allowed to be handled.\n\t * @param {string} file to check\n\t *\/\n\tfileIsBlacklisted: function(file) {\n\t\treturn !!(file.match(oc_config.blacklist_files_regex));\n\t},\n\n\t\/**\n\t * Redirect to the target URL, can also be used for downloads.\n\t * @param {string} targetURL URL to redirect to\n\t *\/\n\tredirect: function(targetURL) {\n\t\twindow.location = targetURL;\n\t},\n\n\t\/**\n\t * Reloads the current page\n\t *\/\n\treload: function() {\n\t\twindow.location.reload();\n\t},\n\n\t\/**\n\t * Protocol that is used to access this Nextcloud instance\n\t * @return {string} Used protocol\n\t *\/\n\tgetProtocol: function() {\n\t\treturn window.location.protocol.split(':')[0];\n\t},\n\n\t\/**\n\t * Returns the host used to access this Nextcloud instance\n\t * Host is sometimes the same as the hostname but now always.\n\t *\n\t * Examples:\n\t * http:\/\/example.com => example.com\n\t * https:\/\/example.com => example.com\n\t * http:\/\/example.com:8080 => example.com:8080\n\t *\n\t * @return {string} host\n\t *\n\t * @since 8.2\n\t *\/\n\tgetHost: function() {\n\t\treturn window.location.host;\n\t},\n\n\t\/**\n\t * Returns the hostname used to access this Nextcloud instance\n\t * The hostname is always stripped of the port\n\t *\n\t * @return {string} hostname\n\t * @since 9.0\n\t *\/\n\tgetHostName: function() {\n\t\treturn window.location.hostname;\n\t},\n\n\t\/**\n\t * Returns the port number used to access this Nextcloud instance\n\t *\n\t * @return {int} port number\n\t *\n\t * @since 8.2\n\t *\/\n\tgetPort: function() {\n\t\treturn window.location.port;\n\t},\n\n\t\/**\n\t * Returns the web root path where this Nextcloud instance\n\t * is accessible, with a leading slash.\n\t * For example \"\/nextcloud\".\n\t *\n\t * @return {string} web root path\n\t *\n\t * @since 8.2\n\t *\/\n\tgetRootPath: function() {\n\t\treturn OC.webroot;\n\t},\n\n\n\t\/**\n\t * Returns the capabilities\n\t *\n\t * @return {array} capabilities\n\t *\n\t * @since 14.0\n\t *\/\n\tgetCapabilities: function() {\n\t\treturn OC._capabilities;\n\t},\n\n\t\/**\n\t * Returns the currently logged in user or null if there is no logged in\n\t * user (public page mode)\n\t *\n\t * @return {OC.CurrentUser} user spec\n\t * @since 9.0.0\n\t *\/\n\tgetCurrentUser: function() {\n\t\tif (_.isUndefined(this._currentUserDisplayName)) {\n\t\t\tthis._currentUserDisplayName = document.getElementsByTagName('head')[0].getAttribute('data-user-displayname');\n\t\t}\n\t\treturn {\n\t\t\tuid: this.currentUser,\n\t\t\tdisplayName: this._currentUserDisplayName\n\t\t};\n\t},\n\n\t\/**\n\t * get the absolute path to an image file\n\t * if no extension is given for the image, it will automatically decide\n\t * between .png and .svg based on what the browser supports\n\t * @param {string} app the app id to which the image belongs\n\t * @param {string} file the name of the image file\n\t * @return {string}\n\t *\/\n\timagePath:function(app,file){\n\t\tif(file.indexOf('.')==-1){\/\/if no extension is given, use svg\n\t\t\tfile+='.svg';\n\t\t}\n\t\treturn OC.filePath(app,'img',file);\n\t},\n\n\t\/**\n\t * URI-Encodes a file path but keep the path slashes.\n\t *\n\t * @param path path\n\t * @return encoded path\n\t *\/\n\tencodePath: function(path) {\n\t\tif (!path) {\n\t\t\treturn path;\n\t\t}\n\t\tvar parts = path.split('\/');\n\t\tvar result = [];\n\t\tfor (var i = 0; i < parts.length; i++) {\n\t\t\tresult.push(encodeURIComponent(parts[i]));\n\t\t}\n\t\treturn result.join('\/');\n\t},\n\n\t\/**\n\t * Load a script for the server and load it. If the script is already loaded,\n\t * the event handler will be called directly\n\t * @param {string} app the app id to which the script belongs\n\t * @param {string} script the filename of the script\n\t * @param ready event handler to be called when the script is loaded\n\t *\/\n\taddScript:function(app,script,ready){\n\t\tvar deferred, path=OC.filePath(app,'js',script+'.js');\n\t\tif(!OC.addScript.loaded[path]) {\n\t\t\tdeferred = $.Deferred();\n\t\t\t$.getScript(path, function() {\n\t\t\t\tdeferred.resolve();\n\t\t\t});\n\t\t\tOC.addScript.loaded[path] = deferred;\n\t\t} else {\n\t\t\tif (ready) {\n\t\t\t\tready();\n\t\t\t}\n\t\t}\n\t\treturn OC.addScript.loaded[path];\n\t},\n\t\/**\n\t * Loads a CSS file\n\t * @param {string} app the app id to which the css style belongs\n\t * @param {string} style the filename of the css file\n\t *\/\n\taddStyle:function(app,style){\n\t\tvar path=OC.filePath(app,'css',style+'.css');\n\t\tif(OC.addStyle.loaded.indexOf(path)===-1){\n\t\t\tOC.addStyle.loaded.push(path);\n\t\t\tif (document.createStyleSheet) {\n\t\t\t\tdocument.createStyleSheet(path);\n\t\t\t} else {\n\t\t\t\tstyle=$('<link rel=\"stylesheet\" type=\"text\/css\" href=\"'+path+'\"\/>');\n\t\t\t\t$('head').append(style);\n\t\t\t}\n\t\t}\n\t},\n\n\t\/**\n\t * Loads translations for the given app asynchronously.\n\t *\n\t * @param {String} app app name\n\t * @param {Function} callback callback to call after loading\n\t * @return {Promise}\n\t *\/\n\taddTranslations: function(app, callback) {\n\t\treturn OC.L10N.load(app, callback);\n\t},\n\n\t\/**\n\t * Returns the base name of the given path.\n\t * For example for \"\/abc\/somefile.txt\" it will return \"somefile.txt\"\n\t *\n\t * @param {String} path\n\t * @return {String} base name\n\t *\/\n\tbasename: function(path) {\n\t\treturn path.replace(\/\\\\\/g,'\/').replace( \/.*\\\/\/, '' );\n\t},\n\n\t\/**\n\t * Returns the dir name of the given path.\n\t * For example for \"\/abc\/somefile.txt\" it will return \"\/abc\"\n\t *\n\t * @param {String} path\n\t * @return {String} dir name\n\t *\/\n\tdirname: function(path) {\n\t\treturn path.replace(\/\\\\\/g,'\/').replace(\/\\\/[^\\\/]*$\/, '');\n\t},\n\n\t\/**\n\t * Returns whether the given paths are the same, without\n\t * leading, trailing or doubled slashes and also removing\n\t * the dot sections.\n\t *\n\t * @param {String} path1 first path\n\t * @param {String} path2 second path\n\t * @return {bool} true if the paths are the same\n\t *\n\t * @since 9.0\n\t *\/\n\tisSamePath: function(path1, path2) {\n\t\tvar filterDot = function(p) {\n\t\t\treturn p !== '.';\n\t\t};\n\t\tvar pathSections1 = _.filter((path1 || '').split('\/'), filterDot);\n\t\tvar pathSections2 = _.filter((path2 || '').split('\/'), filterDot);\n\t\tpath1 = OC.joinPaths.apply(OC, pathSections1);\n\t\tpath2 = OC.joinPaths.apply(OC, pathSections2);\n\t\treturn path1 === path2;\n\t},\n\n\t\/**\n\t * Join path sections\n\t *\n\t * @param {...String} path sections\n\t *\n\t * @return {String} joined path, any leading or trailing slash\n\t * will be kept\n\t *\n\t * @since 8.2\n\t *\/\n\tjoinPaths: function() {\n\t\tif (arguments.length < 1) {\n\t\t\treturn '';\n\t\t}\n\t\tvar path = '';\n\t\t\/\/ convert to array\n\t\tvar args = Array.prototype.slice.call(arguments);\n\t\t\/\/ discard empty arguments\n\t\targs = _.filter(args, function(arg) {\n\t\t\treturn arg.length > 0;\n\t\t});\n\t\tif (args.length < 1) {\n\t\t\treturn '';\n\t\t}\n\n\t\tvar lastArg = args[args.length - 1];\n\t\tvar leadingSlash = args[0].charAt(0) === '\/';\n\t\tvar trailingSlash = lastArg.charAt(lastArg.length - 1) === '\/';\n\t\tvar sections = [];\n\t\tvar i;\n\t\tfor (i = 0; i < args.length; i++) {\n\t\t\tsections = sections.concat(args[i].split('\/'));\n\t\t}\n\t\tvar first = !leadingSlash;\n\t\tfor (i = 0; i < sections.length; i++) {\n\t\t\tif (sections[i] !== '') {\n\t\t\t\tif (first) {\n\t\t\t\t\tfirst = false;\n\t\t\t\t} else {\n\t\t\t\t\tpath += '\/';\n\t\t\t\t}\n\t\t\t\tpath += sections[i];\n\t\t\t}\n\t\t}\n\n\t\tif (trailingSlash) {\n\t\t\t\/\/ add it back\n\t\t\tpath += '\/';\n\t\t}\n\t\treturn path;\n\t},\n\n\t\/**\n\t * Do a search query and display the results\n\t * @param {string} query the search query\n\t *\/\n\tsearch: function (query) {\n\t\tOC.Search.search(query, null, 0, 30);\n\t},\n\t\/**\n\t * Dialog helper for jquery dialogs.\n\t *\n\t * @namespace OC.dialogs\n\t *\/\n\tdialogs:OCdialogs,\n\t\/**\n\t * Parses a URL query string into a JS map\n\t * @param {string} queryString query string in the format param1=1234&param2=abcde&param3=xyz\n\t * @return {Object.<string, string>} map containing key\/values matching the URL parameters\n\t *\/\n\tparseQueryString:function(queryString){\n\t\tvar parts,\n\t\t\tpos,\n\t\t\tcomponents,\n\t\t\tresult = {},\n\t\t\tkey,\n\t\t\tvalue;\n\t\tif (!queryString){\n\t\t\treturn null;\n\t\t}\n\t\tpos = queryString.indexOf('?');\n\t\tif (pos >= 0){\n\t\t\tqueryString = queryString.substr(pos + 1);\n\t\t}\n\t\tparts = queryString.replace(\/\\+\/g, '%20').split('&');\n\t\tfor (var i = 0; i < parts.length; i++){\n\t\t\t\/\/ split on first equal sign\n\t\t\tvar part = parts[i];\n\t\t\tpos = part.indexOf('=');\n\t\t\tif (pos >= 0) {\n\t\t\t\tcomponents = [\n\t\t\t\t\tpart.substr(0, pos),\n\t\t\t\t\tpart.substr(pos + 1)\n\t\t\t\t];\n\t\t\t}\n\t\t\telse {\n\t\t\t\t\/\/ key only\n\t\t\t\tcomponents = [part];\n\t\t\t}\n\t\t\tif (!components.length){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tkey = decodeURIComponent(components[0]);\n\t\t\tif (!key){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\/\/ if equal sign was there, return string\n\t\t\tif (components.length > 1) {\n\t\t\t\tresult[key] = decodeURIComponent(components[1]);\n\t\t\t}\n\t\t\t\/\/ no equal sign => null value\n\t\t\telse {\n\t\t\t\tresult[key] = null;\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t},\n\n\t\/**\n\t * Builds a URL query from a JS map.\n\t * @param {Object.<string, string>} params map containing key\/values matching the URL parameters\n\t * @return {string} String containing a URL query (without question) mark\n\t *\/\n\tbuildQueryString: function(params) {\n\t\tif (!params) {\n\t\t\treturn '';\n\t\t}\n\t\treturn $.map(params, function(value, key) {\n\t\t\tvar s = encodeURIComponent(key);\n\t\t\tif (value !== null && typeof(value) !== 'undefined') {\n\t\t\t\ts += '=' + encodeURIComponent(value);\n\t\t\t}\n\t\t\treturn s;\n\t\t}).join('&');\n\t},\n\n\t\/**\n\t * Opens a popup with the setting for an app.\n\t * @param {string} appid The ID of the app e.g. 'calendar', 'contacts' or 'files'.\n\t * @param {boolean|string}\u00a0loadJS If true 'js\/settings.js' is loaded. If it's a string\n\t * it will attempt to load a script by that name in the 'js' directory.\n\t * @param {boolean} [cache] If true the javascript file won't be forced refreshed. Defaults to true.\n\t * @param {string} [scriptName] The name of the PHP file to load. Defaults to 'settings.php' in\n\t * the root of the app directory hierarchy.\n\t *\/\n\tappSettings:function(args) {\n\t\tif(typeof args === 'undefined' || typeof args.appid === 'undefined') {\n\t\t\tthrow { name: 'MissingParameter', message: 'The parameter appid is missing' };\n\t\t}\n\t\tvar props = {scriptName:'settings.php', cache:true};\n\t\t$.extend(props, args);\n\t\tvar settings = $('#appsettings');\n\t\tif(settings.length === 0) {\n\t\t\tthrow { name: 'MissingDOMElement', message: 'There has be be an element with id \"appsettings\" for the popup to show.' };\n\t\t}\n\t\tvar popup = $('#appsettings_popup');\n\t\tif(popup.length === 0) {\n\t\t\t$('body').prepend('<div class=\"popup hidden\" id=\"appsettings_popup\"><\/div>');\n\t\t\tpopup = $('#appsettings_popup');\n\t\t\tpopup.addClass(settings.hasClass('topright') ? 'topright' : 'bottomleft');\n\t\t}\n\t\tif(popup.is(':visible')) {\n\t\t\tpopup.hide().remove();\n\t\t} else {\n\t\t\tvar arrowclass = settings.hasClass('topright') ? 'up' : 'left';\n\t\t\tvar jqxhr = $.get(OC.filePath(props.appid, '', props.scriptName), function(data) {\n\t\t\t\tpopup.html(data).ready(function() {\n\t\t\t\t\tpopup.prepend('<span class=\"arrow '+arrowclass+'\"><\/span><h2>'+t('core', 'Settings')+'<\/h2><a class=\"close\"><\/a>').show();\n\t\t\t\t\tpopup.find('.close').bind('click', function() {\n\t\t\t\t\t\tpopup.remove();\n\t\t\t\t\t});\n\t\t\t\t\tif(typeof props.loadJS !== 'undefined') {\n\t\t\t\t\t\tvar scriptname;\n\t\t\t\t\t\tif(props.loadJS === true) {\n\t\t\t\t\t\t\tscriptname = 'settings.js';\n\t\t\t\t\t\t} else if(typeof props.loadJS === 'string') {\n\t\t\t\t\t\t\tscriptname = props.loadJS;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow { name: 'InvalidParameter', message: 'The \"loadJS\" parameter must be either boolean or a string.' };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif(props.cache) {\n\t\t\t\t\t\t\t$.ajaxSetup({cache: true});\n\t\t\t\t\t\t}\n\t\t\t\t\t\t$.getScript(OC.filePath(props.appid, 'js', scriptname))\n\t\t\t\t\t\t.fail(function(jqxhr, settings, e) {\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}).show();\n\t\t\t}, 'html');\n\t\t}\n\t},\n\n\t\/**\n\t * For menu toggling\n\t * @todo Write documentation\n\t *\n\t * @param {jQuery} $toggle\n\t * @param {jQuery} $menuEl\n\t * @param {function|undefined} toggle callback invoked everytime the menu is opened\n\t * @param {boolean} headerMenu is this a top right header menu?\n\t * @returns {undefined}\n\t *\/\n\tregisterMenu: function($toggle, $menuEl, toggle, headerMenu) {\n\t\tvar self = this;\n\t\t$menuEl.addClass('menu');\n\n\t\t\/\/ On link, the enter key trigger a click event\n\t\t\/\/ Only use the click to avoid two fired events\n\t\t$toggle.on($toggle.prop('tagName') === 'A'\n\t\t\t? 'click.menu'\n\t\t\t: 'click.menu keyup.menu', function(event) {\n\t\t\t\/\/ prevent the link event (append anchor to URL)\n\t\t\tevent.preventDefault();\n\n\t\t\t\/\/ allow enter key as a trigger\n\t\t\tif (event.key && event.key !== \"Enter\") {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ($menuEl.is(OC._currentMenu)) {\n\t\t\t\tself.hideMenus();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t\/\/ another menu was open?\n\t\t\telse if (OC._currentMenu) {\n\t\t\t\t\/\/ close it\n\t\t\t\tself.hideMenus();\n\t\t\t}\n\n\t\t\tif (headerMenu === true) {\n\t\t\t\t$menuEl.parent().addClass('openedMenu');\n\t\t\t}\n\n\t\t\t\/\/ Set menu to expanded\n\t\t\t$toggle.attr('aria-expanded', true);\n\n\t\t\t$menuEl.slideToggle(OC.menuSpeed, toggle);\n\t\t\tOC._currentMenu = $menuEl;\n\t\t\tOC._currentMenuToggle = $toggle;\n\t\t});\n\t},\n\n\t\/**\n\t * @todo Write documentation\n\t *\/\n\tunregisterMenu: function($toggle, $menuEl) {\n\t\t\/\/ close menu if opened\n\t\tif ($menuEl.is(OC._currentMenu)) {\n\t\t\tthis.hideMenus();\n\t\t}\n\t\t$toggle.off('click.menu').removeClass('menutoggle');\n\t\t$menuEl.removeClass('menu');\n\t},\n\n\t\/**\n\t * Hides any open menus\n\t *\n\t * @param {Function} complete callback when the hiding animation is done\n\t *\/\n\thideMenus: function(complete) {\n\t\tif (OC._currentMenu) {\n\t\t\tvar lastMenu = OC._currentMenu;\n\t\t\tOC._currentMenu.trigger(new $.Event('beforeHide'));\n\t\t\tOC._currentMenu.slideUp(OC.menuSpeed, function() {\n\t\t\t\tlastMenu.trigger(new $.Event('afterHide'));\n\t\t\t\tif (complete) {\n\t\t\t\t\tcomplete.apply(this, arguments);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\n\t\t\/\/ Set menu to closed\n\t\t$('.menutoggle').attr('aria-expanded', false);\n\n\t\t$('.openedMenu').removeClass('openedMenu');\n\t\tOC._currentMenu = null;\n\t\tOC._currentMenuToggle = null;\n\t},\n\n\t\/**\n\t * Shows a given element as menu\n\t *\n\t * @param {Object} [$toggle=null] menu toggle\n\t * @param {Object} $menuEl menu element\n\t * @param {Function} complete callback when the showing animation is done\n\t *\/\n\tshowMenu: function($toggle, $menuEl, complete) {\n\t\tif ($menuEl.is(OC._currentMenu)) {\n\t\t\treturn;\n\t\t}\n\t\tthis.hideMenus();\n\t\tOC._currentMenu = $menuEl;\n\t\tOC._currentMenuToggle = $toggle;\n\t\t$menuEl.trigger(new $.Event('beforeShow'));\n\t\t$menuEl.show();\n\t\t$menuEl.trigger(new $.Event('afterShow'));\n\t\t\/\/ no animation\n\t\tif (_.isFunction(complete)) {\n\t\t\tcomplete();\n\t\t}\n\t},\n\n\t\/**\n\t * Wrapper for matchMedia\n\t *\n\t * This is makes it possible for unit tests to\n\t * stub matchMedia (which doesn't work in PhantomJS)\n\t * @private\n\t *\/\n\t_matchMedia: function(media) {\n\t\tif (window.matchMedia) {\n\t\t\treturn window.matchMedia(media);\n\t\t}\n\t\treturn false;\n\t},\n\n\t\/**\n\t * Returns the user's locale as a BCP 47 compliant language tag\n\t *\n\t * @return {String} locale string\n\t *\/\n\tgetCanonicalLocale: function() {\n\t\tvar locale = this.getLocale();\n\t\treturn typeof locale === 'string' ? locale.replace(\/_\/g, '-') : locale;\n\t},\n\n\t\/**\n\t * Returns the user's locale\n\t *\n\t * @return {String} locale string\n\t *\/\n\tgetLocale: function() {\n\t\treturn $('html').data('locale');\n\t},\n\n\t\/**\n\t * Returns the user's language\n\t *\n\t * @returns {String} language string\n\t *\/\n\tgetLanguage: function () {\n\t\treturn $('html').prop('lang');\n\t},\n\n\t\/**\n\t * Returns whether the current user is an administrator\n\t *\n\t * @return {bool} true if the user is an admin, false otherwise\n\t * @since 9.0.0\n\t *\/\n\tisUserAdmin: function() {\n\t\treturn oc_isadmin;\n\t},\n\n\t\/**\n\t * Warn users that the connection to the server was lost temporarily\n\t *\n\t * This function is throttled to prevent stacked notfications.\n\t * After 7sec the first notification is gone, then we can show another one\n\t * if necessary.\n\t *\/\n\t_ajaxConnectionLostHandler: _.throttle(function() {\n\t\tOC.Notification.showTemporary(t('core', 'Connection to server lost'));\n\t}, 7 * 1000, {trailing: false}),\n\n\t\/**\n\t * Process ajax error, redirects to main page\n\t * if an error\/auth error status was returned.\n\t *\/\n\t_processAjaxError: function(xhr) {\n\t\tvar self = this;\n\t\t\/\/ purposefully aborted request ?\n\t\t\/\/ this._userIsNavigatingAway needed to distinguish ajax calls cancelled by navigating away\n\t\t\/\/ from calls cancelled by failed cross-domain ajax due to SSO redirect\n\t\tif (xhr.status === 0 && (xhr.statusText === 'abort' || xhr.statusText === 'timeout' || self._reloadCalled)) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (_.contains([302, 303, 307, 401], xhr.status) && OC.currentUser) {\n\t\t\t\/\/ sometimes \"beforeunload\" happens later, so need to defer the reload a bit\n\t\t\tsetTimeout(function() {\n\t\t\t\tif (!self._userIsNavigatingAway && !self._reloadCalled) {\n\t\t\t\t\tvar timer = 0;\n\t\t\t\t\tvar seconds = 5;\n\t\t\t\t\tvar interval = setInterval( function() {\n\t\t\t\t\t\tOC.Notification.showUpdate(n('core', 'Problem loading page, reloading in %n second', 'Problem loading page, reloading in %n seconds', seconds - timer));\n\t\t\t\t\t\tif (timer >= seconds) {\n\t\t\t\t\t\t\tclearInterval(interval);\n\t\t\t\t\t\t\tOC.reload();\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttimer++;\n\t\t\t\t\t\t}, 1000 \/\/ 1 second interval\n\t\t\t\t\t);\n\n\t\t\t\t\t\/\/ only call reload once\n\t\t\t\t\tself._reloadCalled = true;\n\t\t\t\t}\n\t\t\t}, 100);\n\t\t} else if(xhr.status === 0) {\n\t\t\t\/\/ Connection lost (e.g. WiFi disconnected or server is down)\n\t\t\tsetTimeout(function() {\n\t\t\t\tif (!self._userIsNavigatingAway && !self._reloadCalled) {\n\t\t\t\t\tself._ajaxConnectionLostHandler();\n\t\t\t\t}\n\t\t\t}, 100);\n\t\t}\n\t},\n\n\t\/**\n\t * Registers XmlHttpRequest object for global error processing.\n\t *\n\t * This means that if this XHR object returns 401 or session timeout errors,\n\t * the current page will automatically be reloaded.\n\t *\n\t * @param {XMLHttpRequest} xhr\n\t *\/\n\tregisterXHRForErrorProcessing: function(xhr) {\n\t\tvar loadCallback = function() {\n\t\t\tif (xhr.readyState !== 4) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t\/\/ fire jquery global ajax error handler\n\t\t\t$(document).trigger(new $.Event('ajaxError'), xhr);\n\t\t};\n\n\t\tvar errorCallback = function() {\n\t\t\t\/\/ fire jquery global ajax error handler\n\t\t\t$(document).trigger(new $.Event('ajaxError'), xhr);\n\t\t};\n\n\t\tif (xhr.addEventListener) {\n\t\t\txhr.addEventListener('load', loadCallback);\n\t\t\txhr.addEventListener('error', errorCallback);\n\t\t}\n\n\t}\n};\n\n\/**\n * Current user attributes\n *\n * @typedef {Object} OC.CurrentUser\n *\n * @property {String} uid user id\n * @property {String} displayName display name\n *\/\n\n\/**\n * @namespace OC.Plugins\n *\/\nOC.Plugins = {\n\t\/**\n\t * @type Array.<OC.Plugin>\n\t *\/\n\t_plugins: {},\n\n\t\/**\n\t * Register plugin\n\t *\n\t * @param {String} targetName app name \/ class name to hook into\n\t * @param {OC.Plugin} plugin\n\t *\/\n\tregister: function(targetName, plugin) {\n\t\tvar plugins = this._plugins[targetName];\n\t\tif (!plugins) {\n\t\t\tplugins = this._plugins[targetName] = [];\n\t\t}\n\t\tplugins.push(plugin);\n\t},\n\n\t\/**\n\t * Returns all plugin registered to the given target\n\t * name \/ app name \/ class name.\n\t *\n\t * @param {String} targetName app name \/ class name to hook into\n\t * @return {Array.<OC.Plugin>} array of plugins\n\t *\/\n\tgetPlugins: function(targetName) {\n\t\treturn this._plugins[targetName] || [];\n\t},\n\n\t\/**\n\t * Call attach() on all plugins registered to the given target name.\n\t *\n\t * @param {String} targetName app name \/ class name\n\t * @param {Object} object to be extended\n\t * @param {Object} [options] options\n\t *\/\n\tattach: function(targetName, targetObject, options) {\n\t\tvar plugins = this.getPlugins(targetName);\n\t\tfor (var i = 0; i < plugins.length; i++) {\n\t\t\tif (plugins[i].attach) {\n\t\t\t\tplugins[i].attach(targetObject, options);\n\t\t\t}\n\t\t}\n\t},\n\n\t\/**\n\t * Call detach() on all plugins registered to the given target name.\n\t *\n\t * @param {String} targetName app name \/ class name\n\t * @param {Object} object to be extended\n\t * @param {Object} [options] options\n\t *\/\n\tdetach: function(targetName, targetObject, options) {\n\t\tvar plugins = this.getPlugins(targetName);\n\t\tfor (var i = 0; i < plugins.length; i++) {\n\t\t\tif (plugins[i].detach) {\n\t\t\t\tplugins[i].detach(targetObject, options);\n\t\t\t}\n\t\t}\n\t},\n\n\t\/**\n\t * Plugin\n\t *\n\t * @todo make this a real class in the future\n\t * @typedef {Object} OC.Plugin\n\t *\n\t * @property {String} name plugin name\n\t * @property {Function} attach function that will be called when the\n\t * plugin is attached\n\t * @property {Function} [detach] function that will be called when the\n\t * plugin is detached\n\t *\/\n\n};\n\n\/**\n * @namespace OC.search\n *\/\nOC.search.customResults = {};\n\/**\n * @deprecated use get\/setFormatter() instead\n *\/\nOC.search.resultTypes = {};\n\nOC.addStyle.loaded=[];\nOC.addScript.loaded=[];\n\n\/**\n * A little class to manage a status field for a \"saving\" process.\n * It can be used to display a starting message (e.g. \"Saving...\") and then\n * replace it with a green success message or a red error message.\n *\n * @namespace OC.msg\n *\/\nOC.msg = {\n\t\/**\n\t * Displayes a \"Saving...\" message in the given message placeholder\n\t *\n\t * @param {Object} selector\tPlaceholder to display the message in\n\t *\/\n\tstartSaving: function(selector) {\n\t\tthis.startAction(selector, t('core', 'Saving...'));\n\t},\n\n\t\/**\n\t * Displayes a custom message in the given message placeholder\n\t *\n\t * @param {Object} selector\tPlaceholder to display the message in\n\t * @param {string} message\tPlain text message to display (no HTML allowed)\n\t *\/\n\tstartAction: function(selector, message) {\n\t\t$(selector).text(message)\n\t\t\t.removeClass('success')\n\t\t\t.removeClass('error')\n\t\t\t.stop(true, true)\n\t\t\t.show();\n\t},\n\n\t\/**\n\t * Displayes an success\/error message in the given selector\n\t *\n\t * @param {Object} selector\tPlaceholder to display the message in\n\t * @param {Object} response\tResponse of the server\n\t * @param {Object} response.data\tData of the servers response\n\t * @param {string} response.data.message\tPlain text message to display (no HTML allowed)\n\t * @param {string} response.status\tis being used to decide whether the message\n\t * is displayed as an error\/success\n\t *\/\n\tfinishedSaving: function(selector, response) {\n\t\tthis.finishedAction(selector, response);\n\t},\n\n\t\/**\n\t * Displayes an success\/error message in the given selector\n\t *\n\t * @param {Object} selector\tPlaceholder to display the message in\n\t * @param {Object} response\tResponse of the server\n\t * @param {Object} response.data Data of the servers response\n\t * @param {string} response.data.message Plain text message to display (no HTML allowed)\n\t * @param {string} response.status is being used to decide whether the message\n\t * is displayed as an error\/success\n\t *\/\n\tfinishedAction: function(selector, response) {\n\t\tif (response.status === \"success\") {\n\t\t\tthis.finishedSuccess(selector, response.data.message);\n\t\t} else {\n\t\t\tthis.finishedError(selector, response.data.message);\n\t\t}\n\t},\n\n\t\/**\n\t * Displayes an success message in the given selector\n\t *\n\t * @param {Object} selector Placeholder to display the message in\n\t * @param {string} message Plain text success message to display (no HTML allowed)\n\t *\/\n\tfinishedSuccess: function(selector, message) {\n\t\t$(selector).text(message)\n\t\t\t.addClass('success')\n\t\t\t.removeClass('error')\n\t\t\t.stop(true, true)\n\t\t\t.delay(3000)\n\t\t\t.fadeOut(900)\n\t\t\t.show();\n\t},\n\n\t\/**\n\t * Displayes an error message in the given selector\n\t *\n\t * @param {Object} selector Placeholder to display the message in\n\t * @param {string} message Plain text error message to display (no HTML allowed)\n\t *\/\n\tfinishedError: function(selector, message) {\n\t\t$(selector).text(message)\n\t\t\t.addClass('error')\n\t\t\t.removeClass('success')\n\t\t\t.show();\n\t}\n};\n\n\/**\n * @todo Write documentation\n * @namespace\n *\/\nOC.Notification={\n\tqueuedNotifications: [],\n\tgetDefaultNotificationFunction: null,\n\n\t\/**\n\t * @type Array<int>\n\t * @description array of notification timers\n\t *\/\n\tnotificationTimers: [],\n\n\t\/**\n\t * @param callback\n\t * @todo Write documentation\n\t *\/\n\tsetDefault: function(callback) {\n\t\tOC.Notification.getDefaultNotificationFunction = callback;\n\t},\n\n\t\/**\n\t * Hides a notification.\n\t *\n\t * If a row is given, only hide that one.\n\t * If no row is given, hide all notifications.\n\t *\n\t * @param {jQuery} [$row] notification row\n\t * @param {Function} [callback] callback\n\t *\/\n\thide: function($row, callback) {\n\t\tvar self = this;\n\t\tvar $notification = $('#notification');\n\n\t\tif (_.isFunction($row)) {\n\t\t\t\/\/ first arg is the callback\n\t\t\tcallback = $row;\n\t\t\t$row = undefined;\n\t\t}\n\n\t\tif (!$row) {\n\t\t\tconsole.warn('Missing argument $row in OC.Notification.hide() call, caller needs to be adjusted to only dismiss its own notification');\n\t\t\t\/\/ assume that the row to be hidden is the first one\n\t\t\t$row = $notification.find('.row:first');\n\t\t}\n\n\t\tif ($row && $notification.find('.row').length > 1) {\n\t\t\t\/\/ remove the row directly\n\t\t\t$row.remove();\n\t\t\tif (callback) {\n\t\t\t\tcallback.call();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t_.defer(function() {\n\t\t\t\/\/ fade out is supposed to only fade when there is a single row\n\t\t\t\/\/ however, some code might call hide() and show() directly after,\n\t\t\t\/\/ which results in more than one element\n\t\t\t\/\/ in this case, simply delete that one element that was supposed to\n\t\t\t\/\/ fade out\n\t\t\t\/\/\n\t\t\t\/\/ FIXME: remove once all callers are adjusted to only hide their own notifications\n\t\t\tif ($notification.find('.row').length > 1) {\n\t\t\t\t$row.remove();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t\/\/ else, fade out whatever was present\n\t\t\t$notification.fadeOut('400', function(){\n\t\t\t\tif (self.isHidden()) {\n\t\t\t\t\tif (self.getDefaultNotificationFunction) {\n\t\t\t\t\t\tself.getDefaultNotificationFunction.call();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (callback) {\n\t\t\t\t\tcallback.call();\n\t\t\t\t}\n\t\t\t\t$notification.empty();\n\t\t\t});\n\t\t});\n\t},\n\n\t\/**\n\t * Shows a notification as HTML without being sanitized before.\n\t * If you pass unsanitized user input this may lead to a XSS vulnerability.\n\t * Consider using show() instead of showHTML()\n\t *\n\t * @param {string} html Message to display\n\t * @param {Object} [options] options\n\t * @param {string} [options.type] notification type\n\t * @param {int} [options.timeout=0] timeout value, defaults to 0 (permanent)\n\t * @return {jQuery} jQuery element for notification row\n\t *\/\n\tshowHtml: function(html, options) {\n\t\toptions = options || {};\n\t\t_.defaults(options, {\n\t\t\ttimeout: 0\n\t\t});\n\n\t\tvar self = this;\n\t\tvar $notification = $('#notification');\n\t\tif (this.isHidden()) {\n\t\t\t$notification.fadeIn().css('display','inline-block');\n\t\t}\n\t\tvar $row = $('<div class=\"row\"><\/div>');\n\t\tif (options.type) {\n\t\t\t$row.addClass('type-' + options.type);\n\t\t}\n\t\tif (options.type === 'error') {\n\t\t\t\/\/ add a close button\n\t\t\tvar $closeButton = $('<a class=\"action close icon-close\" href=\"#\"><\/a>');\n\t\t\t$closeButton.attr('alt', t('core', 'Dismiss'));\n\t\t\t$row.append($closeButton);\n\t\t\t$closeButton.one('click', function() {\n\t\t\t\tself.hide($row);\n\t\t\t\treturn false;\n\t\t\t});\n\t\t\t$row.addClass('closeable');\n\t\t}\n\n\t\t$row.prepend(html);\n\t\t$notification.append($row);\n\n\t\tif(options.timeout > 0) {\n\t\t\t\/\/ register timeout to vanish notification\n\t\t\tthis.notificationTimers.push(setTimeout(function() {\n\t\t\t\tself.hide($row);\n\t\t\t}, (options.timeout * 1000)));\n\t\t}\n\n\t\treturn $row;\n\t},\n\n\t\/**\n\t * Shows a sanitized notification\n\t *\n\t * @param {string} text Message to display\n\t * @param {Object} [options] options\n\t * @param {string} [options.type] notification type\n\t * @param {int} [options.timeout=0] timeout value, defaults to 0 (permanent)\n\t * @return {jQuery} jQuery element for notification row\n\t *\/\n\tshow: function(text, options) {\n\t\treturn this.showHtml($('<div\/>').text(text).html(), options);\n\t},\n\n\t\/**\n\t * Updates (replaces) a sanitized notification.\n\t *\n\t * @param {string} text Message to display\n\t * @return {jQuery} JQuery element for notificaiton row\n\t *\/\n\tshowUpdate: function(text) {\n\t\tvar $notification = $('#notification');\n\t\t\/\/ sanitise\n\t\tvar $html = $('<div\/>').text(text).html();\n\n\t\t\/\/ new notification\n\t\tif (text && $notification.find('.row').length == 0) {\n\t\t\treturn this.showHtml($html);\n\t\t}\n\n\t\tvar $row = $('<div class=\"row\"><\/div>').prepend($html);\n\n\t\t\/\/ just update html in notification\n\t\t$notification.html($row);\n\n\t\treturn $row;\n\t},\n\n\t\/**\n\t * Shows a notification that disappears after x seconds, default is\n\t * 7 seconds\n\t *\n\t * @param {string} text Message to show\n\t * @param {array} [options] options array\n\t * @param {int} [options.timeout=7] timeout in seconds, if this is 0 it will show the message permanently\n\t * @param {boolean} [options.isHTML=false] an indicator for HTML notifications (true) or text (false)\n\t * @param {string} [options.type] notification type\n\t *\/\n\tshowTemporary: function(text, options) {\n\t\tvar defaults = {\n\t\t\tisHTML: false,\n\t\t\ttimeout: 7\n\t\t};\n\t\toptions = options || {};\n\t\t\/\/ merge defaults with passed in options\n\t\t_.defaults(options, defaults);\n\n\t\tvar $row;\n\t\tif(options.isHTML) {\n\t\t\t$row = this.showHtml(text, options);\n\t\t} else {\n\t\t\t$row = this.show(text, options);\n\t\t}\n\t\treturn $row;\n\t},\n\n\t\/**\n\t * Returns whether a notification is hidden.\n\t * @return {boolean}\n\t *\/\n\tisHidden: function() {\n\t\treturn !$(\"#notification\").find('.row').length;\n\t}\n};\n\n\/**\n * Initializes core\n *\/\nfunction initCore() {\n\t\/**\n\t * Disable automatic evaluation of responses for $.ajax() functions (and its\n\t * higher-level alternatives like $.get() and $.post()).\n\t *\n\t * If a response to a $.ajax() request returns a content type of \"application\/javascript\"\n\t * JQuery would previously execute the response body. This is a pretty unexpected\n\t * behaviour and can result in a bypass of our Content-Security-Policy as well as\n\t * multiple unexpected XSS vectors.\n\t *\/\n\t$.ajaxSetup({\n\t\tcontents: {\n\t\t\tscript: false\n\t\t}\n\t});\n\n\t\/**\n\t * Disable execution of eval in jQuery. We do require an allowed eval CSP\n\t * configuration at the moment for handlebars et al. But for jQuery there is\n\t * not much of a reason to execute JavaScript directly via eval.\n\t *\n\t * This thus mitigates some unexpected XSS vectors.\n\t *\/\n\tjQuery.globalEval = function(){};\n\n\t\/**\n\t * Set users locale to moment.js as soon as possible\n\t *\/\n\tmoment.locale(OC.getLocale());\n\n\tvar userAgent = window.navigator.userAgent;\n\tvar msie = userAgent.indexOf('MSIE ');\n\tvar trident = userAgent.indexOf('Trident\/');\n\tvar edge = userAgent.indexOf('Edge\/');\n\n\tif (msie > 0 || trident > 0) {\n\t\t\/\/ (IE 10 or older) || IE 11\n\t\t$('html').addClass('ie');\n\t} else if (edge > 0) {\n\t\t\/\/ for edge\n\t\t$('html').addClass('edge');\n\t}\n\n\t\/\/ css variables fallback for IE\n\tif (msie > 0 || trident > 0) {\n\t\tcssVars();\n\t}\n\n\t$(window).on('unload.main', function() {\n\t\tOC._unloadCalled = true;\n\t});\n\t$(window).on('beforeunload.main', function() {\n\t\t\/\/ super-trick thanks to http:\/\/stackoverflow.com\/a\/4651049\n\t\t\/\/ in case another handler displays a confirmation dialog (ex: navigating away\n\t\t\/\/ during an upload), there are two possible outcomes: user clicked \"ok\" or\n\t\t\/\/ \"cancel\"\n\n\t\t\/\/ first timeout handler is called after unload dialog is closed\n\t\tsetTimeout(function() {\n\t\t\tOC._userIsNavigatingAway = true;\n\n\t\t\t\/\/ second timeout event is only called if user cancelled (Chrome),\n\t\t\t\/\/ but in other browsers it might still be triggered, so need to\n\t\t\t\/\/ set a higher delay...\n\t\t\tsetTimeout(function() {\n\t\t\t\tif (!OC._unloadCalled) {\n\t\t\t\t\tOC._userIsNavigatingAway = false;\n\t\t\t\t}\n\t\t\t}, 10000);\n\t\t},1);\n\t});\n\t$(document).on('ajaxError.main', function( event, request, settings ) {\n\t\tif (settings && settings.allowAuthErrors) {\n\t\t\treturn;\n\t\t}\n\t\tOC._processAjaxError(request);\n\t});\n\n\t\/**\n\t * Calls the server periodically to ensure that session and CSRF\n\t * token doesn't expire\n\t *\/\n\tfunction initSessionHeartBeat() {\n\t\t\/\/ interval in seconds\n\t\tvar interval = NaN;\n\t\tif (oc_config.session_lifetime) {\n\t\t\tinterval = Math.floor(oc_config.session_lifetime \/ 2);\n\t\t}\n\t\tinterval = isNaN(interval)? 900: interval;\n\n\t\t\/\/ minimum one minute\n\t\tinterval = Math.max(60, interval);\n\t\t\/\/ max interval in seconds set to 24 hours\n\t\tinterval = Math.min(24 * 3600, interval);\n\n\t\tvar url = OC.generateUrl('\/csrftoken');\n\t\tsetInterval(function() {\n\t\t\t$.ajax(url).then(function(resp) {\n\t\t\t\toc_requesttoken = resp.token;\n\t\t\t\tOC.requestToken = resp.token;\n\t\t\t}).fail(function(e) {\n\t\t\t\tconsole.error('session heartbeat failed', e);\n\t\t\t});\n\t\t}, interval * 1000);\n\t}\n\n\t\/\/ session heartbeat (defaults to enabled)\n\tif (typeof(oc_config.session_keepalive) === 'undefined' ||\n\t\t!!oc_config.session_keepalive) {\n\n\t\tinitSessionHeartBeat();\n\t}\n\n\tOC.registerMenu($('#expand'), $('#expanddiv'), false, true);\n\n\t\/\/ toggle for menus\n\t\/\/$(document).on('mouseup.closemenus keyup', function(event) {\n\t$(document).on('mouseup.closemenus', function(event) {\n\n\t\t\/\/ allow enter as a trigger\n\t\t\/\/ if (event.key && event.key !== \"Enter\") {\n\t\t\/\/ \treturn;\n\t\t\/\/ }\n\n\t\tvar $el = $(event.target);\n\t\tif ($el.closest('.menu').length || $el.closest('.menutoggle').length) {\n\t\t\t\/\/ don't close when clicking on the menu directly or a menu toggle\n\t\t\treturn false;\n\t\t}\n\n\t\tOC.hideMenus();\n\t});\n\n\t\/**\n\t * Set up the main menu toggle to react to media query changes.\n\t * If the screen is small enough, the main menu becomes a toggle.\n\t * If the screen is bigger, the main menu is not a toggle any more.\n\t *\/\n\tfunction setupMainMenu() {\n\n\t\t\/\/ init the more-apps menu\n\t\tOC.registerMenu($('#more-apps > a'), $('#navigation'));\n\n\t\t\/\/ toggle the navigation\n\t\tvar $toggle = $('#header .header-appname-container');\n\t\tvar $navigation = $('#navigation');\n\t\tvar $appmenu = $('#appmenu');\n\n\t\t\/\/ init the menu\n\t\tOC.registerMenu($toggle, $navigation);\n\t\t$toggle.data('oldhref', $toggle.attr('href'));\n\t\t$toggle.attr('href', '#');\n\t\t$navigation.hide();\n\n\t\t\/\/ show loading feedback\n\t\t$navigation.delegate('a', 'click', function(event) {\n\t\t\tvar $app = $(event.target);\n\t\t\tif(!$app.is('a')) {\n\t\t\t\t$app = $app.closest('a');\n\t\t\t}\n\t\t\tif(event.which === 1 && !event.ctrlKey && !event.metaKey) {\n\t\t\t\t$app.addClass('app-loading');\n\t\t\t} else {\n\t\t\t\t\/\/ Close navigation when opening app in\n\t\t\t\t\/\/ a new tab\n\t\t\t\tOC.hideMenus(function(){return false;});\n\t\t\t}\n\t\t});\n\n\t\t$navigation.delegate('a', 'mouseup', function(event) {\n\t\t\tif(event.which === 2) {\n\t\t\t\t\/\/ Close navigation when opening app in\n\t\t\t\t\/\/ a new tab via middle click\n\t\t\t\tOC.hideMenus(function(){return false;});\n\t\t\t}\n\t\t});\n\n\t\t$appmenu.delegate('a', 'click', function(event) {\n\t\t\tvar $app = $(event.target);\n\t\t\tif(!$app.is('a')) {\n\t\t\t\t$app = $app.closest('a');\n\t\t\t}\n\t\t\tif(event.which === 1 && !event.ctrlKey && !event.metaKey) {\n\t\t\t\t$app.addClass('app-loading');\n\t\t\t} else {\n\t\t\t\t\/\/ Close navigation when opening app in\n\t\t\t\t\/\/ a new tab\n\t\t\t\tOC.hideMenus(function(){return false;});\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction setupUserMenu() {\n\t\tvar $menu = $('#header #settings');\n\n\t\t\/\/ show loading feedback\n\t\t$menu.delegate('a', 'click', function(event) {\n\t\t\tvar $page = $(event.target);\n\t\t\tif (!$page.is('a')) {\n\t\t\t\t$page = $page.closest('a');\n\t\t\t}\n\t\t\tif(event.which === 1 && !event.ctrlKey && !event.metaKey) {\n\t\t\t\t$page.find('img').remove();\n\t\t\t\t$page.find('div').remove(); \/\/ prevent odd double-clicks\n\t\t\t\t$page.prepend($('<div\/>').addClass('icon-loading-small'));\n\t\t\t} else {\n\t\t\t\t\/\/ Close navigation when opening menu entry in\n\t\t\t\t\/\/ a new tab\n\t\t\t\tOC.hideMenus(function(){return false;});\n\t\t\t}\n\t\t});\n\n\t\t$menu.delegate('a', 'mouseup', function(event) {\n\t\t\tif(event.which === 2) {\n\t\t\t\t\/\/ Close navigation when opening app in\n\t\t\t\t\/\/ a new tab via middle click\n\t\t\t\tOC.hideMenus(function(){return false;});\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction setupContactsMenu() {\n\t\tnew OC.ContactsMenu({\n\t\t\tel: $('#contactsmenu .menu'),\n\t\t\ttrigger: $('#contactsmenu .menutoggle')\n\t\t});\n\t}\n\n\tsetupMainMenu();\n\tsetupUserMenu();\n\tsetupContactsMenu();\n\n\t\/\/ move triangle of apps dropdown to align with app name triangle\n\t\/\/ 2 is the additional offset between the triangles\n\tif($('#navigation').length) {\n\t\t$('#header #nextcloud + .menutoggle').on('click', function(){\n\t\t\t$('#menu-css-helper').remove();\n\t\t\tvar caretPosition = $('.header-appname + .icon-caret').offset().left - 2;\n\t\t\tif(caretPosition > 255) {\n\t\t\t\t\/\/ if the app name is longer than the menu, just put the triangle in the middle\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\t$('head').append('<style id=\"menu-css-helper\">#navigation:after { left: '+ caretPosition +'px; }<\/style>');\n\t\t\t}\n\t\t});\n\t\t$('#header #appmenu .menutoggle').on('click', function() {\n\t\t\t$('#appmenu').toggleClass('menu-open');\n\t\t\tif($('#appmenu').is(':visible')) {\n\t\t\t\t$('#menu-css-helper').remove();\n\t\t\t}\n\t\t});\n\t}\n\n\tvar resizeMenu = function() {\n\t\tvar appList = $('#appmenu li');\n\t\tvar rightHeaderWidth = $('.header-right').outerWidth();\n\t\tvar headerWidth = $('header').outerWidth();\n\t\tvar usePercentualAppMenuLimit = 0.33;\n\t\tvar minAppsDesktop = 8;\n\t\tvar availableWidth = headerWidth - $('#nextcloud').outerWidth() - (rightHeaderWidth > 210 ? rightHeaderWidth : 210)\n\t\tvar isMobile = $(window).width() < 768;\n\t\tif (!isMobile) {\n\t\t\tavailableWidth = availableWidth * usePercentualAppMenuLimit;\n\t\t}\n\t\tvar appCount = Math.floor((availableWidth \/ $(appList).width()));\n\t\tif (isMobile && appCount > minAppsDesktop) {\n\t\t\tappCount = minAppsDesktop;\n\t\t}\n\t\tif (!isMobile && appCount < minAppsDesktop) {\n\t\t\tappCount = minAppsDesktop;\n\t\t}\n\n\t\t\/\/ show at least 2 apps in the popover\n\t\tif(appList.length-1-appCount >= 1) {\n\t\t\tappCount--;\n\t\t}\n\n\t\t$('#more-apps a').removeClass('active');\n\t\tvar lastShownApp;\n\t\tfor (var k = 0; k < appList.length-1; k++) {\n\t\t\tvar name = $(appList[k]).data('id');\n\t\t\tif(k < appCount) {\n\t\t\t\t$(appList[k]).removeClass('hidden');\n\t\t\t\t$('#apps li[data-id=' + name + ']').addClass('in-header');\n\t\t\t\tlastShownApp = appList[k];\n\t\t\t} else {\n\t\t\t\t$(appList[k]).addClass('hidden');\n\t\t\t\t$('#apps li[data-id=' + name + ']').removeClass('in-header');\n\t\t\t\t\/\/ move active app to last position if it is active\n\t\t\t\tif(appCount > 0 && $(appList[k]).children('a').hasClass('active')) {\n\t\t\t\t\t$(lastShownApp).addClass('hidden');\n\t\t\t\t\t$('#apps li[data-id=' + $(lastShownApp).data('id') + ']').removeClass('in-header');\n\t\t\t\t\t$(appList[k]).removeClass('hidden');\n\t\t\t\t\t$('#apps li[data-id=' + name + ']').addClass('in-header');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t\/\/ show\/hide more apps icon\n\t\tif($('#apps li:not(.in-header)').length === 0) {\n\t\t\t$('#more-apps').hide();\n\t\t\t$('#navigation').hide();\n\t\t} else {\n\t\t\t$('#more-apps').show();\n\t\t}\n\t};\n\t$(window).resize(resizeMenu);\n\tsetTimeout(resizeMenu, 0);\n\n\t\/\/ just add snapper for logged in users\n\tif($('#app-navigation').length && !$('html').hasClass('lte9')) {\n\n\t\t\/\/ App sidebar on mobile\n\t\tvar snapper = new Snap({\n\t\t\telement: document.getElementById('app-content'),\n\t\t\tdisable: 'right',\n\t\t\tmaxPosition: 300, \/\/ $navigation-width\n\t\t\tminDragDistance: 100\n\t\t});\n\n\t\t$('#app-content').prepend('<div id=\"app-navigation-toggle\" class=\"icon-menu\" style=\"display:none;\" tabindex=\"0\"><\/div>');\n\n\t\tvar toggleSnapperOnButton = function(){\n\t\t\tif(snapper.state().state == 'left'){\n\t\t\t\tsnapper.close();\n\t\t\t} else {\n\t\t\t\tsnapper.open('left');\n\t\t\t}\n\t\t};\n\n\t\t$('#app-navigation-toggle').click(function(){\n\t\t\ttoggleSnapperOnButton();\n\t\t});\n\n\t\t$('#app-navigation-toggle').keypress(function(e) {\n\t\t\tif(e.which == 13) {\n\t\t\t\ttoggleSnapperOnButton();\n\t\t\t}\n\t\t});\n\n\t\t\/\/ close sidebar when switching navigation entry\n\t\tvar $appNavigation = $('#app-navigation');\n\t\t$appNavigation.delegate('a, :button', 'click', function(event) {\n\t\t\tvar $target = $(event.target);\n\t\t\t\/\/ don't hide navigation when changing settings or adding things\n\t\t\tif($target.is('.app-navigation-noclose') ||\n\t\t\t\t$target.closest('.app-navigation-noclose').length) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif($target.is('.app-navigation-entry-utils-menu-button') ||\n\t\t\t\t$target.closest('.app-navigation-entry-utils-menu-button').length) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif($target.is('.add-new') ||\n\t\t\t\t$target.closest('.add-new').length) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif($target.is('#app-settings') ||\n\t\t\t\t$target.closest('#app-settings').length) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsnapper.close();\n\t\t});\n\n\t\tvar navigationBarSlideGestureEnabled = false;\n\t\tvar navigationBarSlideGestureAllowed = true;\n\t\tvar navigationBarSlideGestureEnablePending = false;\n\n\t\tOC.allowNavigationBarSlideGesture = function() {\n\t\t\tnavigationBarSlideGestureAllowed = true;\n\n\t\t\tif (navigationBarSlideGestureEnablePending) {\n\t\t\t\tsnapper.enable();\n\n\t\t\t\tnavigationBarSlideGestureEnabled = true;\n\t\t\t\tnavigationBarSlideGestureEnablePending = false;\n\t\t\t}\n\t\t};\n\n\t\tOC.disallowNavigationBarSlideGesture = function() {\n\t\t\tnavigationBarSlideGestureAllowed = false;\n\n\t\t\tif (navigationBarSlideGestureEnabled) {\n\t\t\t\tvar endCurrentDrag = true;\n\t\t\t\tsnapper.disable(endCurrentDrag);\n\n\t\t\t\tnavigationBarSlideGestureEnabled = false;\n\t\t\t\tnavigationBarSlideGestureEnablePending = true;\n\t\t\t}\n\t\t};\n\n\t\tvar toggleSnapperOnSize = function() {\n\t\t\tif($(window).width() > 768) {\n\t\t\t\tsnapper.close();\n\t\t\t\tsnapper.disable();\n\n\t\t\t\tnavigationBarSlideGestureEnabled = false;\n\t\t\t\tnavigationBarSlideGestureEnablePending = false;\n\t\t\t} else if (navigationBarSlideGestureAllowed) {\n\t\t\t\tsnapper.enable();\n\n\t\t\t\tnavigationBarSlideGestureEnabled = true;\n\t\t\t\tnavigationBarSlideGestureEnablePending = false;\n\t\t\t} else {\n\t\t\t\tnavigationBarSlideGestureEnablePending = true;\n\t\t\t}\n\t\t};\n\n\t\t$(window).resize(_.debounce(toggleSnapperOnSize, 250));\n\n\t\t\/\/ initial call\n\t\ttoggleSnapperOnSize();\n\n\t}\n\n\t\/\/ Update live timestamps every 30 seconds\n\tsetInterval(function() {\n\t\t$('.live-relative-timestamp').each(function() {\n\t\t\t$(this).text(OC.Util.relativeModifiedDate(parseInt($(this).attr('data-timestamp'), 10)));\n\t\t});\n\t}, 30 * 1000);\n\n\tOC.PasswordConfirmation.init();\n}\n\nOC.PasswordConfirmation = {\n\tcallback: null,\n\tpageLoadTime: null,\n\tinit: function() {\n\t\t$('.password-confirm-required').on('click', _.bind(this.requirePasswordConfirmation, this));\n\t\tthis.pageLoadTime = moment.now();\n\t},\n\n\trequiresPasswordConfirmation: function() {\n\t\tvar serverTimeDiff = this.pageLoadTime - (nc_pageLoad * 1000);\n\t\tvar timeSinceLogin = moment.now() - (serverTimeDiff + (nc_lastLogin * 1000));\n\n\t\t\/\/ if timeSinceLogin > 30 minutes and user backend allows password confirmation\n\t\treturn (backendAllowsPasswordConfirmation && timeSinceLogin > 30 * 60 * 1000);\n\t},\n\n\t\/**\n\t * @param {function} callback\n\t *\/\n\trequirePasswordConfirmation: function(callback, options, rejectCallback) {\n\t\toptions = typeof options !== 'undefined' ? options : {};\n\t\tvar defaults = {\n\t\t\ttitle: t('core','Authentication required'),\n\t\t\ttext: t(\n\t\t\t\t'core',\n\t\t\t\t'This action requires you to confirm your password'\n\t\t\t),\n\t\t\tconfirm: t('core', 'Confirm'),\n\t\t\tlabel: t('core','Password'),\n\t\t\terror: '',\n\t\t};\n\n\t\tvar config = _.extend(defaults, options);\n\n\t\tvar self = this;\n\n\t\tif (this.requiresPasswordConfirmation()) {\n\t\t\tOC.dialogs.prompt(\n\t\t\t\tconfig.text,\n\t\t\t\tconfig.title,\n\t\t\t\tfunction (result, password) {\n\t\t\t\t\tif (result && password !== '') {\n\t\t\t\t\t\tself._confirmPassword(password, config);\n\t\t\t\t\t} else if (_.isFunction(rejectCallback)) {\n\t\t\t\t\t\trejectCallback()\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\ttrue,\n\t\t\t\tconfig.label,\n\t\t\t\ttrue\n\t\t\t).then(function() {\n\t\t\t\tvar $dialog = $('.oc-dialog:visible');\n\t\t\t\t$dialog.find('.ui-icon').remove();\n\t\t\t\t$dialog.addClass('password-confirmation');\n\t\t\t\tif (config.error !== '') {\n\t\t\t\t\tvar $error = $('<p><\/p>').addClass('msg warning').text(config.error);\n\t\t\t\t}\n\t\t\t\t$dialog.find('.oc-dialog-content').append($error);\n\t\t\t\t$dialog.find('.oc-dialog-buttonrow').addClass('aside');\n\n\t\t\t\tvar $buttons = $dialog.find('button');\n\t\t\t\t$buttons.eq(0).hide();\n\t\t\t\t$buttons.eq(1).text(config.confirm);\n\t\t\t});\n\t\t}\n\n\t\tthis.callback = callback;\n\t},\n\n\t_confirmPassword: function(password, config) {\n\t\tvar self = this;\n\n\t\t$.ajax({\n\t\t\turl: OC.generateUrl('\/login\/confirm'),\n\t\t\tdata: {\n\t\t\t\tpassword: password\n\t\t\t},\n\t\t\ttype: 'POST',\n\t\t\tsuccess: function(response) {\n\t\t\t\tnc_lastLogin = response.lastLogin;\n\n\t\t\t\tif (_.isFunction(self.callback)) {\n\t\t\t\t\tself.callback();\n\t\t\t\t}\n\t\t\t},\n\t\t\terror: function() {\n\t\t\t\tconfig.error = t('core', 'Failed to authenticate, try again');\n\t\t\t\tOC.PasswordConfirmation.requirePasswordConfirmation(self.callback, config);\n\t\t\t}\n\t\t});\n\t}\n};\n\n$(document).ready(initCore);\n\n\/**\n * Filter Jquery selector by attribute value\n *\/\n$.fn.filterAttr = function(attr_name, attr_value) {\n\treturn this.filter(function() { return $(this).attr(attr_name) === attr_value; });\n};\n\n\/**\n * Returns a human readable file size\n * @param {number} size Size in bytes\n * @param {boolean} skipSmallSizes return '< 1 kB' for small files\n * @return {string}\n *\/\nfunction humanFileSize(size, skipSmallSizes) {\n\tvar humanList = ['B', 'KB', 'MB', 'GB', 'TB'];\n\t\/\/ Calculate Log with base 1024: size = 1024 ** order\n\tvar order = size > 0 ? Math.floor(Math.log(size) \/ Math.log(1024)) : 0;\n\t\/\/ Stay in range of the byte sizes that are defined\n\torder = Math.min(humanList.length - 1, order);\n\tvar readableFormat = humanList[order];\n\tvar relativeSize = (size \/ Math.pow(1024, order)).toFixed(1);\n\tif(skipSmallSizes === true && order === 0) {\n\t\tif(relativeSize !== \"0.0\"){\n\t\t\treturn '< 1 KB';\n\t\t} else {\n\t\t\treturn '0 KB';\n\t\t}\n\t}\n\tif(order < 2){\n\t\trelativeSize = parseFloat(relativeSize).toFixed(0);\n\t}\n\telse if(relativeSize.substr(relativeSize.length-2,2)==='.0'){\n\t\trelativeSize=relativeSize.substr(0,relativeSize.length-2);\n\t}\n\telse{\n\t\trelativeSize = parseFloat(relativeSize).toLocaleString(OC.getCanonicalLocale());\n\t}\n\treturn relativeSize + ' ' + readableFormat;\n}\n\n\/**\n * Format an UNIX timestamp to a human understandable format\n * @param {number} timestamp UNIX timestamp\n * @return {string} Human readable format\n *\/\nfunction formatDate(timestamp){\n\treturn OC.Util.formatDate(timestamp);\n}\n\n\/\/\n\/**\n * Get the value of a URL parameter\n * @link http:\/\/stackoverflow.com\/questions\/1403888\/get-url-parameter-with-jquery\n * @param {string} name URL parameter\n * @return {string}\n *\/\nfunction getURLParameter(name) {\n\treturn decodeURIComponent(\n\t\t(new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(\n\t\t\tlocation.search)||[,''])[1].replace(\/\\+\/g, '%20')\n\t\t)||'';\n}\n\n\/**\n * Takes an absolute timestamp and return a string with a human-friendly relative date\n * @param {number} timestamp A Unix timestamp\n *\/\nfunction relative_modified_date(timestamp) {\n\t\/*\n\t Were multiplying by 1000 to bring the timestamp back to its original value\n\t per https:\/\/github.com\/owncloud\/core\/pull\/10647#discussion_r16790315\n\t *\/\n\treturn OC.Util.relativeModifiedDate(timestamp * 1000);\n}\n\n\/**\n * Utility functions\n * @namespace\n *\/\nOC.Util = {\n\t\/\/ TODO: remove original functions from global namespace\n\thumanFileSize: humanFileSize,\n\n\t\/**\n\t * Returns a file size in bytes from a humanly readable string\n\t * Makes 2kB to 2048.\n\t * Inspired by computerFileSize in helper.php\n\t * @param {string} string file size in human readable format\n\t * @return {number} or null if string could not be parsed\n\t *\n\t *\n\t *\/\n\tcomputerFileSize: function (string) {\n\t\tif (typeof string !== 'string') {\n\t\t\treturn null;\n\t\t}\n\n\t\tvar s = string.toLowerCase().trim();\n\t\tvar bytes = null;\n\n\t\tvar bytesArray = {\n\t\t\t'b' : 1,\n\t\t\t'k' : 1024,\n\t\t\t'kb': 1024,\n\t\t\t'mb': 1024 * 1024,\n\t\t\t'm' : 1024 * 1024,\n\t\t\t'gb': 1024 * 1024 * 1024,\n\t\t\t'g' : 1024 * 1024 * 1024,\n\t\t\t'tb': 1024 * 1024 * 1024 * 1024,\n\t\t\t't' : 1024 * 1024 * 1024 * 1024,\n\t\t\t'pb': 1024 * 1024 * 1024 * 1024 * 1024,\n\t\t\t'p' : 1024 * 1024 * 1024 * 1024 * 1024\n\t\t};\n\n\t\tvar matches = s.match(\/^[\\s+]?([0-9]*)(\\.([0-9]+))?( +)?([kmgtp]?b?)$\/i);\n\t\tif (matches !== null) {\n\t\t\tbytes = parseFloat(s);\n\t\t\tif (!isFinite(bytes)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t\tif (matches[5]) {\n\t\t\tbytes = bytes * bytesArray[matches[5]];\n\t\t}\n\n\t\tbytes = Math.round(bytes);\n\t\treturn bytes;\n\t},\n\n\t\/**\n\t * @param timestamp\n\t * @param format\n\t * @returns {string} timestamp formatted as requested\n\t *\/\n\tformatDate: function (timestamp, format) {\n\t\tformat = format || \"LLL\";\n\t\treturn moment(timestamp).format(format);\n\t},\n\n\t\/**\n\t * @param timestamp\n\t * @returns {string} human readable difference from now\n\t *\/\n\trelativeModifiedDate: function (timestamp) {\n\t\tvar diff = moment().diff(moment(timestamp));\n\t\tif (diff >= 0 && diff < 45000 ) {\n\t\t\treturn t('core', 'seconds ago');\n\t\t}\n\t\treturn moment(timestamp).fromNow();\n\t},\n\n\t\/**\n\t * Returns whether this is IE\n\t *\n\t * @return {bool} true if this is IE, false otherwise\n\t *\/\n\tisIE: function() {\n\t\treturn $('html').hasClass('ie');\n\t},\n\n\t\/**\n\t * Returns the width of a generic browser scrollbar\n\t *\n\t * @return {int} width of scrollbar\n\t *\/\n\tgetScrollBarWidth: function() {\n\t\tif (this._scrollBarWidth) {\n\t\t\treturn this._scrollBarWidth;\n\t\t}\n\n\t\tvar inner = document.createElement('p');\n\t\tinner.style.width = \"100%\";\n\t\tinner.style.height = \"200px\";\n\n\t\tvar outer = document.createElement('div');\n\t\touter.style.position = \"absolute\";\n\t\touter.style.top = \"0px\";\n\t\touter.style.left = \"0px\";\n\t\touter.style.visibility = \"hidden\";\n\t\touter.style.width = \"200px\";\n\t\touter.style.height = \"150px\";\n\t\touter.style.overflow = \"hidden\";\n\t\touter.appendChild (inner);\n\n\t\tdocument.body.appendChild (outer);\n\t\tvar w1 = inner.offsetWidth;\n\t\touter.style.overflow = 'scroll';\n\t\tvar w2 = inner.offsetWidth;\n\t\tif(w1 === w2) {\n\t\t\tw2 = outer.clientWidth;\n\t\t}\n\n\t\tdocument.body.removeChild (outer);\n\n\t\tthis._scrollBarWidth = (w1 - w2);\n\n\t\treturn this._scrollBarWidth;\n\t},\n\n\t\/**\n\t * Remove the time component from a given date\n\t *\n\t * @param {Date} date date\n\t * @return {Date} date with stripped time\n\t *\/\n\tstripTime: function(date) {\n\t\t\/\/ FIXME: likely to break when crossing DST\n\t\t\/\/ would be better to use a library like momentJS\n\t\treturn new Date(date.getFullYear(), date.getMonth(), date.getDate());\n\t},\n\n\t_chunkify: function(t) {\n\t\t\/\/ Adapted from http:\/\/my.opera.com\/GreyWyvern\/blog\/show.dml\/1671288\n\t\tvar tz = [], x = 0, y = -1, n = 0, code, c;\n\n\t\twhile (x < t.length) {\n\t\t\tc = t.charAt(x);\n\t\t\t\/\/ only include the dot in strings\n\t\t\tvar m = ((!n && c === '.') || (c >= '0' && c <= '9'));\n\t\t\tif (m !== n) {\n\t\t\t\t\/\/ next chunk\n\t\t\t\ty++;\n\t\t\t\ttz[y] = '';\n\t\t\t\tn = m;\n\t\t\t}\n\t\t\ttz[y] += c;\n\t\t\tx++;\n\t\t}\n\t\treturn tz;\n\t},\n\t\/**\n\t * Compare two strings to provide a natural sort\n\t * @param a first string to compare\n\t * @param b second string to compare\n\t * @return -1 if b comes before a, 1 if a comes before b\n\t * or 0 if the strings are identical\n\t *\/\n\tnaturalSortCompare: function(a, b) {\n\t\tvar x;\n\t\tvar aa = OC.Util._chunkify(a);\n\t\tvar bb = OC.Util._chunkify(b);\n\n\t\tfor (x = 0; aa[x] && bb[x]; x++) {\n\t\t\tif (aa[x] !== bb[x]) {\n\t\t\t\tvar aNum = Number(aa[x]), bNum = Number(bb[x]);\n\t\t\t\t\/\/ note: == is correct here\n\t\t\t\tif (aNum == aa[x] && bNum == bb[x]) {\n\t\t\t\t\treturn aNum - bNum;\n\t\t\t\t} else {\n\t\t\t\t\t\/\/ Note: This locale setting isn't supported by all browsers but for the ones\n\t\t\t\t\t\/\/ that do there will be more consistency between client-server sorting\n\t\t\t\t\treturn aa[x].localeCompare(bb[x], OC.getLanguage());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn aa.length - bb.length;\n\t},\n\t\/**\n\t * Calls the callback in a given interval until it returns true\n\t * @param {function} callback\n\t * @param {integer} interval in milliseconds\n\t *\/\n\twaitFor: function(callback, interval) {\n\t\tvar internalCallback = function() {\n\t\t\tif(callback() !== true) {\n\t\t\t\tsetTimeout(internalCallback, interval);\n\t\t\t}\n\t\t};\n\n\t\tinternalCallback();\n\t},\n\t\/**\n\t * Checks if a cookie with the given name is present and is set to the provided value.\n\t * @param {string} name name of the cookie\n\t * @param {string} value value of the cookie\n\t * @return {boolean} true if the cookie with the given name has the given value\n\t *\/\n\tisCookieSetToValue: function(name, value) {\n\t\tvar cookies = document.cookie.split(';');\n\t\tfor (var i=0; i < cookies.length; i++) {\n\t\t\tvar cookie = cookies[i].split('=');\n\t\t\tif (cookie[0].trim() === name && cookie[1].trim() === value) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n};\n\n\/**\n * Utility class for the history API,\n * includes fallback to using the URL hash when\n * the browser doesn't support the history API.\n *\n * @namespace\n *\/\nOC.Util.History = {\n\t_handlers: [],\n\n\t\/**\n\t * Push the current URL parameters to the history stack\n\t * and change the visible URL.\n\t * Note: this includes a workaround for IE8\/IE9 that uses\n\t * the hash part instead of the search part.\n\t *\n\t * @param {Object|string} params to append to the URL, can be either a string\n\t * or a map\n\t * @param {string} [url] URL to be used, otherwise the current URL will be used,\n\t * using the params as query string\n\t * @param {boolean} [replace=false] whether to replace instead of pushing\n\t *\/\n\t_pushState: function(params, url, replace) {\n\t\tvar strParams;\n\t\tif (typeof(params) === 'string') {\n\t\t\tstrParams = params;\n\t\t}\n\t\telse {\n\t\t\tstrParams = OC.buildQueryString(params);\n\t\t}\n\t\tif (window.history.pushState) {\n\t\t\turl = url || location.pathname + '?' + strParams;\n\t\t\t\/\/ Workaround for bug with SVG and window.history.pushState on Firefox < 51\n\t\t\t\/\/ https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=652991\n\t\t\tvar isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;\n\t\t\tif (isFirefox && parseInt(navigator.userAgent.split('\/').pop()) < 51) {\n\t\t\t\tvar patterns = document.querySelectorAll('[fill^=\"url(#\"], [stroke^=\"url(#\"], [filter^=\"url(#invert\"]');\n\t\t\t\tfor (var i = 0, ii = patterns.length, pattern; i < ii; i++) {\n\t\t\t\t\tpattern = patterns[i];\n\t\t\t\t\tpattern.style.fill = pattern.style.fill;\n\t\t\t\t\tpattern.style.stroke = pattern.style.stroke;\n\t\t\t\t\tpattern.removeAttribute(\"filter\");\n\t\t\t\t\tpattern.setAttribute(\"filter\", \"url(#invert)\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (replace) {\n\t\t\t\twindow.history.replaceState(params, '', url);\n\t\t\t} else {\n\t\t\t\twindow.history.pushState(params, '', url);\n\t\t\t}\n\t\t}\n\t\t\/\/ use URL hash for IE8\n\t\telse {\n\t\t\twindow.location.hash = '?' + strParams;\n\t\t\t\/\/ inhibit next onhashchange that just added itself\n\t\t\t\/\/ to the event queue\n\t\t\tthis._cancelPop = true;\n\t\t}\n\t},\n\n\t\/**\n\t * Push the current URL parameters to the history stack\n\t * and change the visible URL.\n\t * Note: this includes a workaround for IE8\/IE9 that uses\n\t * the hash part instead of the search part.\n\t *\n\t * @param {Object|string} params to append to the URL, can be either a string\n\t * or a map\n\t * @param {string} [url] URL to be used, otherwise the current URL will be used,\n\t * using the params as query string\n\t *\/\n\tpushState: function(params, url) {\n\t\treturn this._pushState(params, url, false);\n\t},\n\n\t\/**\n\t * Push the current URL parameters to the history stack\n\t * and change the visible URL.\n\t * Note: this includes a workaround for IE8\/IE9 that uses\n\t * the hash part instead of the search part.\n\t *\n\t * @param {Object|string} params to append to the URL, can be either a string\n\t * or a map\n\t * @param {string} [url] URL to be used, otherwise the current URL will be used,\n\t * using the params as query string\n\t *\/\n\treplaceState: function(params, url) {\n\t\treturn this._pushState(params, url, true);\n\t},\n\n\t\/**\n\t * Add a popstate handler\n\t *\n\t * @param handler function\n\t *\/\n\taddOnPopStateHandler: function(handler) {\n\t\tthis._handlers.push(handler);\n\t},\n\n\t\/**\n\t * Parse a query string from the hash part of the URL.\n\t * (workaround for IE8 \/ IE9)\n\t *\/\n\t_parseHashQuery: function() {\n\t\tvar hash = window.location.hash,\n\t\t\tpos = hash.indexOf('?');\n\t\tif (pos >= 0) {\n\t\t\treturn hash.substr(pos + 1);\n\t\t}\n\t\tif (hash.length) {\n\t\t\t\/\/ remove hash sign\n\t\t\treturn hash.substr(1);\n\t\t}\n\t\treturn '';\n\t},\n\n\t_decodeQuery: function(query) {\n\t\treturn query.replace(\/\\+\/g, ' ');\n\t},\n\n\t\/**\n\t * Parse the query\/search part of the URL.\n\t * Also try and parse it from the URL hash (for IE8)\n\t *\n\t * @return map of parameters\n\t *\/\n\tparseUrlQuery: function() {\n\t\tvar query = this._parseHashQuery(),\n\t\t\tparams;\n\t\t\/\/ try and parse from URL hash first\n\t\tif (query) {\n\t\t\tparams = OC.parseQueryString(this._decodeQuery(query));\n\t\t}\n\t\t\/\/ else read from query attributes\n\t\tparams = _.extend(params || {}, OC.parseQueryString(this._decodeQuery(location.search)));\n\t\treturn params || {};\n\t},\n\n\t_onPopState: function(e) {\n\t\tif (this._cancelPop) {\n\t\t\tthis._cancelPop = false;\n\t\t\treturn;\n\t\t}\n\t\tvar params;\n\t\tif (!this._handlers.length) {\n\t\t\treturn;\n\t\t}\n\t\tparams = (e && e.state);\n\t\tif (_.isString(params)) {\n\t\t\tparams = OC.parseQueryString(params);\n\t\t} else if (!params) {\n\t\t\tparams = this.parseUrlQuery() || {};\n\t\t}\n\t\tfor (var i = 0; i < this._handlers.length; i++) {\n\t\t\tthis._handlers[i](params);\n\t\t}\n\t}\n};\n\n\/\/ fallback to hashchange when no history support\nif (window.history.pushState) {\n\twindow.onpopstate = _.bind(OC.Util.History._onPopState, OC.Util.History);\n}\nelse {\n\t$(window).on('hashchange', _.bind(OC.Util.History._onPopState, OC.Util.History));\n}\n\n\/**\n * Get a variable by name\n * @param {string} name\n * @return {*}\n *\/\nOC.get=function(name) {\n\tvar namespaces = name.split(\".\");\n\tvar tail = namespaces.pop();\n\tvar context=window;\n\n\tfor(var i = 0; i < namespaces.length; i++) {\n\t\tcontext = context[namespaces[i]];\n\t\tif(!context){\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn context[tail];\n};\n\n\/**\n * Set a variable by name\n * @param {string} name\n * @param {*} value\n *\/\nOC.set=function(name, value) {\n\tvar namespaces = name.split(\".\");\n\tvar tail = namespaces.pop();\n\tvar context=window;\n\n\tfor(var i = 0; i < namespaces.length; i++) {\n\t\tif(!context[namespaces[i]]){\n\t\t\tcontext[namespaces[i]]={};\n\t\t}\n\t\tcontext = context[namespaces[i]];\n\t}\n\tcontext[tail]=value;\n};\n\n\/\/ fix device width on windows phone\n(function() {\n\tif (\"-ms-user-select\" in document.documentElement.style && navigator.userAgent.match(\/IEMobile\\\/10\\.0\/)) {\n\t\tvar msViewportStyle = document.createElement(\"style\");\n\t\tmsViewportStyle.appendChild(\n\t\t\tdocument.createTextNode(\"@-ms-viewport{width:auto!important}\")\n\t\t);\n\t\tdocument.getElementsByTagName(\"head\")[0].appendChild(msViewportStyle);\n\t}\n})();\n\n\/**\n * Namespace for apps\n * @namespace OCA\n *\/\nwindow.OCA = {};\n\n\/**\n * select a range in an input field\n * @link http:\/\/stackoverflow.com\/questions\/499126\/jquery-set-cursor-position-in-text-area\n * @param {type} start\n * @param {type} end\n *\/\njQuery.fn.selectRange = function(start, end) {\n\treturn this.each(function() {\n\t\tif (this.setSelectionRange) {\n\t\t\tthis.focus();\n\t\t\tthis.setSelectionRange(start, end);\n\t\t} else if (this.createTextRange) {\n\t\t\tvar range = this.createTextRange();\n\t\t\trange.collapse(true);\n\t\t\trange.moveEnd('character', end);\n\t\t\trange.moveStart('character', start);\n\t\t\trange.select();\n\t\t}\n\t});\n};\n\n\/**\n * check if an element exists.\n * allows you to write if ($('#myid').exists()) to increase readability\n * @link http:\/\/stackoverflow.com\/questions\/31044\/is-there-an-exists-function-for-jquery\n *\/\njQuery.fn.exists = function(){\n\treturn this.length > 0;\n};\n\n\/**\n * jQuery tipsy shim for the bootstrap tooltip\n *\/\njQuery.fn.tipsy = function(argument) {\n\tconsole.warn('Deprecation warning: tipsy is deprecated. Use tooltip instead.');\n\tif(typeof argument === 'object' && argument !== null) {\n\n\t\t\/\/ tipsy defaults\n\t\tvar options = {\n\t\t\tplacement: 'bottom',\n\t\t\tdelay: { 'show': 0, 'hide': 0},\n\t\t\ttrigger: 'hover',\n\t\t\thtml: false,\n\t\t\tcontainer: 'body'\n\t\t};\n\t\tif(argument.gravity) {\n\t\t\tswitch(argument.gravity) {\n\t\t\t\tcase 'n':\n\t\t\t\tcase 'nw':\n\t\t\t\tcase 'ne':\n\t\t\t\t\toptions.placement='bottom';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 's':\n\t\t\t\tcase 'sw':\n\t\t\t\tcase 'se':\n\t\t\t\t\toptions.placement='top';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'w':\n\t\t\t\t\toptions.placement='right';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'e':\n\t\t\t\t\toptions.placement='left';\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(argument.trigger) {\n\t\t\toptions.trigger = argument.trigger;\n\t\t}\n\t\tif(argument.delayIn) {\n\t\t\toptions.delay.show = argument.delayIn;\n\t\t}\n\t\tif(argument.delayOut) {\n\t\t\toptions.delay.hide = argument.delayOut;\n\t\t}\n\t\tif(argument.html) {\n\t\t\toptions.html = true;\n\t\t}\n\t\tif(argument.fallback) {\n\t\t\toptions.title = argument.fallback;\n\t\t}\n\t\t\/\/ destroy old tooltip in case the title has changed\n\t\tjQuery.fn.tooltip.call(this, 'destroy');\n\t\tjQuery.fn.tooltip.call(this, options);\n\t} else {\n\t\tthis.tooltip(argument);\n\t\tjQuery.fn.tooltip.call(this, argument);\n\t}\n\treturn this;\n};\n\n\n\/**\n * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>\n *\n * This file is licensed under the Affero General Public License version 3\n * or later.\n *\n * See the COPYING-README file.\n *\n *\/\n\n\/**\n * L10N namespace with localization functions.\n *\n * @namespace\n *\/\nOC.L10N = {\n\t\/**\n\t * String bundles with app name as key.\n\t * @type {Object.<String,String>}\n\t *\/\n\t_bundles: {},\n\n\t\/**\n\t * Plural functions, key is app name and value is function.\n\t * @type {Object.<String,Function>}\n\t *\/\n\t_pluralFunctions: {},\n\n\t\/**\n\t * Load an app's translation bundle if not loaded already.\n\t *\n\t * @param {String} appName name of the app\n\t * @param {Function} callback callback to be called when\n\t * the translations are loaded\n\t * @return {Promise} promise\n\t *\/\n\tload: function(appName, callback) {\n\t\t\/\/ already available ?\n\t\tif (this._bundles[appName] || OC.getLocale() === 'en') {\n\t\t\tvar deferred = $.Deferred();\n\t\t\tvar promise = deferred.promise();\n\t\t\tpromise.then(callback);\n\t\t\tdeferred.resolve();\n\t\t\treturn promise;\n\t\t}\n\n\t\tvar self = this;\n\t\tvar url = OC.filePath(appName, 'l10n', OC.getLocale() + '.json');\n\n\t\t\/\/ load JSON translation bundle per AJAX\n\t\treturn $.get(url)\n\t\t\t.then(\n\t\t\t\tfunction(result) {\n\t\t\t\t\tif (result.translations) {\n\t\t\t\t\t\tself.register(appName, result.translations, result.pluralForm);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t.then(callback);\n\t},\n\n\t\/**\n\t * Register an app's translation bundle.\n\t *\n\t * @param {String} appName name of the app\n\t * @param {Object<String,String>} bundle\n\t * @param {Function|String} [pluralForm] optional plural function or plural string\n\t *\/\n\tregister: function(appName, bundle, pluralForm) {\n\t\tvar self = this;\n\t\tif (_.isUndefined(this._bundles[appName])) {\n\t\t\tthis._bundles[appName] = bundle || {};\n\n\t\t\t\/\/ generate plural function based on form\n\t\t\tthis._pluralFunctions[appName] = this._getPlural;\n\t\t} else {\n\t\t\t\/\/ Theme overwriting the default language\n\t\t\t_.extend(self._bundles[appName], bundle);\n\t\t}\n\t},\n\n\t\/**\n\t * Translate a string\n\t * @param {string} app the id of the app for which to translate the string\n\t * @param {string} text the string to translate\n\t * @param [vars] map of placeholder key to value\n\t * @param {number} [count] number to replace %n with\n\t * @param {array} [options] options array\n\t * @param {bool} [options.escape=true] enable\/disable auto escape of placeholders (by default enabled)\n\t * @return {string}\n\t *\/\n\ttranslate: function(app, text, vars, count, options) {\n\t\tvar defaultOptions = {\n\t\t\t\tescape: true\n\t\t\t},\n\t\t\tallOptions = options || {};\n\t\t_.defaults(allOptions, defaultOptions);\n\n\t\t\/\/ TODO: cache this function to avoid inline recreation\n\t\t\/\/ of the same function over and over again in case\n\t\t\/\/ translate() is used in a loop\n\t\tvar _build = function (text, vars, count) {\n\t\t\treturn text.replace(\/%n\/g, count).replace(\/{([^{}]*)}\/g,\n\t\t\t\tfunction (a, b) {\n\t\t\t\t\tvar r = vars[b];\n\t\t\t\t\tif(typeof r === 'string' || typeof r === 'number') {\n\t\t\t\t\t\tif(allOptions.escape) {\n\t\t\t\t\t\t\treturn DOMPurify.sanitize(escapeHTML(r));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn DOMPurify.sanitize(r);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn DOMPurify.sanitize(a);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\t\t};\n\t\tvar translation = text;\n\t\tvar bundle = this._bundles[app] || {};\n\t\tvar value = bundle[text];\n\t\tif( typeof(value) !== 'undefined' ){\n\t\t\ttranslation = value;\n\t\t}\n\n\t\tif(typeof vars === 'object' || count !== undefined ) {\n\t\t\treturn DOMPurify.sanitize(_build(translation, vars, count));\n\t\t} else {\n\t\t\treturn DOMPurify.sanitize(translation);\n\t\t}\n\t},\n\n\t\/**\n\t * Translate a plural string\n\t * @param {string} app the id of the app for which to translate the string\n\t * @param {string} textSingular the string to translate for exactly one object\n\t * @param {string} textPlural the string to translate for n objects\n\t * @param {number} count number to determine whether to use singular or plural\n\t * @param [vars] map of placeholder key to value\n\t * @param {array} [options] options array\n\t * @param {bool} [options.escape=true] enable\/disable auto escape of placeholders (by default enabled)\n\t * @return {string} Translated string\n\t *\/\n\ttranslatePlural: function(app, textSingular, textPlural, count, vars, options) {\n\t\tvar identifier = '_' + textSingular + '_::_' + textPlural + '_';\n\t\tvar bundle = this._bundles[app] || {};\n\t\tvar value = bundle[identifier];\n\t\tif( typeof(value) !== 'undefined' ){\n\t\t\tvar translation = value;\n\t\t\tif ($.isArray(translation)) {\n\t\t\t\tvar plural = this._pluralFunctions[app](count);\n\t\t\t\treturn this.translate(app, translation[plural], vars, count, options);\n\t\t\t}\n\t\t}\n\n\t\tif(count === 1) {\n\t\t\treturn this.translate(app, textSingular, vars, count, options);\n\t\t}\n\t\telse{\n\t\t\treturn this.translate(app, textPlural, vars, count, options);\n\t\t}\n\t},\n\n\t\/**\n\t * The plural function taken from symfony\n\t *\n\t * @param {number} number\n\t * @returns {number}\n\t * @private\n\t *\/\n\t_getPlural: function(number) {\n\t\tvar locale = OC.getLocale();\n\t\tif ('pt_BR' === locale) {\n\t\t\t\/\/ temporary set a locale for brazilian\n\t\t\tlocale = 'xbr';\n\t\t}\n\n\t\tif (typeof locale === 'undefined') {\n\t\t\treturn (1 == number) ? 0 : 1;\n\t\t}\n\n\t\tif (locale.length > 3) {\n\t\t\tlocale = locale.substring(0, locale.lastIndexOf('_'));\n\t\t}\n\n\t\t\/*\n\t\t * The plural rules are derived from code of the Zend Framework (2010-09-25),\n\t\t * which is subject to the new BSD license (http:\/\/framework.zend.com\/license\/new-bsd).\n\t\t * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http:\/\/www.zend.com)\n\t\t *\/\n\t\tswitch (locale) {\n\t\t\tcase 'az':\n\t\t\tcase 'bo':\n\t\t\tcase 'dz':\n\t\t\tcase 'id':\n\t\t\tcase 'ja':\n\t\t\tcase 'jv':\n\t\t\tcase 'ka':\n\t\t\tcase 'km':\n\t\t\tcase 'kn':\n\t\t\tcase 'ko':\n\t\t\tcase 'ms':\n\t\t\tcase 'th':\n\t\t\tcase 'tr':\n\t\t\tcase 'vi':\n\t\t\tcase 'zh':\n\t\t\t\treturn 0;\n\n\t\t\tcase 'af':\n\t\t\tcase 'bn':\n\t\t\tcase 'bg':\n\t\t\tcase 'ca':\n\t\t\tcase 'da':\n\t\t\tcase 'de':\n\t\t\tcase 'el':\n\t\t\tcase 'en':\n\t\t\tcase 'eo':\n\t\t\tcase 'es':\n\t\t\tcase 'et':\n\t\t\tcase 'eu':\n\t\t\tcase 'fa':\n\t\t\tcase 'fi':\n\t\t\tcase 'fo':\n\t\t\tcase 'fur':\n\t\t\tcase 'fy':\n\t\t\tcase 'gl':\n\t\t\tcase 'gu':\n\t\t\tcase 'ha':\n\t\t\tcase 'he':\n\t\t\tcase 'hu':\n\t\t\tcase 'is':\n\t\t\tcase 'it':\n\t\t\tcase 'ku':\n\t\t\tcase 'lb':\n\t\t\tcase 'ml':\n\t\t\tcase 'mn':\n\t\t\tcase 'mr':\n\t\t\tcase 'nah':\n\t\t\tcase 'nb':\n\t\t\tcase 'ne':\n\t\t\tcase 'nl':\n\t\t\tcase 'nn':\n\t\t\tcase 'no':\n\t\t\tcase 'oc':\n\t\t\tcase 'om':\n\t\t\tcase 'or':\n\t\t\tcase 'pa':\n\t\t\tcase 'pap':\n\t\t\tcase 'ps':\n\t\t\tcase 'pt':\n\t\t\tcase 'so':\n\t\t\tcase 'sq':\n\t\t\tcase 'sv':\n\t\t\tcase 'sw':\n\t\t\tcase 'ta':\n\t\t\tcase 'te':\n\t\t\tcase 'tk':\n\t\t\tcase 'ur':\n\t\t\tcase 'zu':\n\t\t\t\treturn (1 == number) ? 0 : 1;\n\n\t\t\tcase 'am':\n\t\t\tcase 'bh':\n\t\t\tcase 'fil':\n\t\t\tcase 'fr':\n\t\t\tcase 'gun':\n\t\t\tcase 'hi':\n\t\t\tcase 'hy':\n\t\t\tcase 'ln':\n\t\t\tcase 'mg':\n\t\t\tcase 'nso':\n\t\t\tcase 'xbr':\n\t\t\tcase 'ti':\n\t\t\tcase 'wa':\n\t\t\t\treturn ((0 == number) || (1 == number)) ? 0 : 1;\n\n\t\t\tcase 'be':\n\t\t\tcase 'bs':\n\t\t\tcase 'hr':\n\t\t\tcase 'ru':\n\t\t\tcase 'sh':\n\t\t\tcase 'sr':\n\t\t\tcase 'uk':\n\t\t\t\treturn ((1 == number % 10) && (11 != number % 100)) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2);\n\n\t\t\tcase 'cs':\n\t\t\tcase 'sk':\n\t\t\t\treturn (1 == number) ? 0 : (((number >= 2) && (number <= 4)) ? 1 : 2);\n\n\t\t\tcase 'ga':\n\t\t\t\treturn (1 == number) ? 0 : ((2 == number) ? 1 : 2);\n\n\t\t\tcase 'lt':\n\t\t\t\treturn ((1 == number % 10) && (11 != number % 100)) ? 0 : (((number % 10 >= 2) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2);\n\n\t\t\tcase 'sl':\n\t\t\t\treturn (1 == number % 100) ? 0 : ((2 == number % 100) ? 1 : (((3 == number % 100) || (4 == number % 100)) ? 2 : 3));\n\n\t\t\tcase 'mk':\n\t\t\t\treturn (1 == number % 10) ? 0 : 1;\n\n\t\t\tcase 'mt':\n\t\t\t\treturn (1 == number) ? 0 : (((0 == number) || ((number % 100 > 1) && (number % 100 < 11))) ? 1 : (((number % 100 > 10) && (number % 100 < 20)) ? 2 : 3));\n\n\t\t\tcase 'lv':\n\t\t\t\treturn (0 == number) ? 0 : (((1 == number % 10) && (11 != number % 100)) ? 1 : 2);\n\n\t\t\tcase 'pl':\n\t\t\t\treturn (1 == number) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 12) || (number % 100 > 14))) ? 1 : 2);\n\n\t\t\tcase 'cy':\n\t\t\t\treturn (1 == number) ? 0 : ((2 == number) ? 1 : (((8 == number) || (11 == number)) ? 2 : 3));\n\n\t\t\tcase 'ro':\n\t\t\t\treturn (1 == number) ? 0 : (((0 == number) || ((number % 100 > 0) && (number % 100 < 20))) ? 1 : 2);\n\n\t\t\tcase 'ar':\n\t\t\t\treturn (0 == number) ? 0 : ((1 == number) ? 1 : ((2 == number) ? 2 : (((number % 100 >= 3) && (number % 100 <= 10)) ? 3 : (((number % 100 >= 11) && (number % 100 <= 99)) ? 4 : 5))));\n\n\t\t\tdefault:\n\t\t\t\treturn 0;\n\t\t}\n\t}\n};\n\n\/**\n * translate a string\n * @param {string} app the id of the app for which to translate the string\n * @param {string} text the string to translate\n * @param [vars] map of placeholder key to value\n * @param {number} [count] number to replace %n with\n * @return {string}\n *\/\nwindow.t = _.bind(OC.L10N.translate, OC.L10N);\n\n\/**\n * translate a string\n * @param {string} app the id of the app for which to translate the string\n * @param {string} text_singular the string to translate for exactly one object\n * @param {string} text_plural the string to translate for n objects\n * @param {number} count number to determine whether to use singular or plural\n * @param [vars] map of placeholder key to value\n * @return {string} Translated string\n *\/\nwindow.n = _.bind(OC.L10N.translatePlural, OC.L10N);\n\nHandlebars.registerHelper('t', function(app, text) {\n\treturn OC.L10N.translate(app, text);\n});\n\n\n\n\/**\n * jQuery plugin for micro templates\n *\n * Strings are automatically escaped, but that can be disabled by setting\n * escapeFunction to null.\n *\n * Usage examples:\n *\n *\tvar htmlStr = '<p>Bake, uncovered, until the {greasystuff} is melted and the {pasta} is heated through, about {min} minutes.<\/p>'\n *\t$(htmlStr).octemplate({greasystuff: 'cheese', pasta: 'macaroni', min: 10});\n *\n * \tvar htmlStr = '<p>Welcome back {user}<\/p>';\n *\t$(htmlStr).octemplate({user: 'John Q. Public'}, {escapeFunction: null});\n *\n * Be aware that the target string must be wrapped in an HTML element for the\n * plugin to work. The following won't work:\n * \n * var textStr = 'Welcome back {user}';\n * $(textStr).octemplate({user: 'John Q. Public'});\n *\n * For anything larger than one-liners, you can use a simple $.get() ajax\n * request to get the template, or you can embed them it the page using the\n * text\/template type:\n *\n * <script id=\"contactListItemTemplate\" type=\"text\/template\">\n *\t<tr class=\"contact\" data-id=\"{id}\">\n *\t\t<td class=\"name\">\n *\t\t\t<input type=\"checkbox\" name=\"id\" value=\"{id}\" \/><span class=\"nametext\">{name}<\/span>\n *\t\t<\/td>\n *\t\t<td class=\"email\">\n *\t\t\t<a href=\"mailto:{email}\">{email}<\/a>\n *\t\t<\/td>\n *\t\t<td class=\"phone\">{phone}<\/td>\n *\t<\/tr>\n * <\/script>\n *\n * var $tmpl = $('#contactListItemTemplate');\n * var contacts = \/\/ fetched in some ajax call\n *\n * $.each(contacts, function(idx, contact) {\n * \t\t$contactList.append(\n * \t\t\t$tmpl.octemplate({\n * \t\t\t\tid: contact.getId(),\n * \t\t\t\tname: contact.getDisplayName(),\n * \t\t\t\temail: contact.getPreferredEmail(),\n * \t\t\t\tphone: contact.getPreferredPhone(),\n * \t\t\t});\n * \t\t);\n * });\n *\/\n(function( $ ) {\n\t\/**\n\t* Object Template\n\t* Inspired by micro templating done by e.g. underscore.js\n\t*\/\n\tvar Template = {\n\t\tinit: function(vars, options, elem) {\n\t\t\t\/\/ Mix in the passed in options with the default options\n\t\t\tthis.vars = vars;\n\t\t\tthis.options = $.extend({},this.options,options);\n\n\t\t\tthis.elem = elem;\n\t\t\tvar self = this;\n\n\t\t\tif(typeof this.options.escapeFunction === 'function') {\n\t\t\t\tvar keys = Object.keys(this.vars);\n\t\t\t\tfor (var key = 0; key < keys.length; key++) {\n\t\t\t\t\tif(typeof this.vars[keys[key]] === 'string') {\n\t\t\t\t\t\tthis.vars[keys[key]] = self.options.escapeFunction(this.vars[keys[key]]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar _html = this._build(this.vars);\n\t\t\treturn $(_html);\n\t\t},\n\t\t\/\/ From stackoverflow.com\/questions\/1408289\/best-way-to-do-variable-interpolation-in-javascript\n\t\t_build: function(o){\n\t\t\tvar data = this.elem.attr('type') === 'text\/template' ? this.elem.html() : this.elem.get(0).outerHTML;\n\t\t\ttry {\n\t\t\t\treturn data.replace(\/{([^{}]*)}\/g,\n\t\t\t\t\tfunction (a, b) {\n\t\t\t\t\t\tvar r = o[b];\n\t\t\t\t\t\treturn typeof r === 'string' || typeof r === 'number' ? r : a;\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t} catch(e) {\n\t\t\t\tconsole.error(e, 'data:', data);\n\t\t\t}\n\t\t},\n\t\toptions: {\n\t\t\tescapeFunction: escapeHTML\n\t\t}\n\t};\n\n\t$.fn.octemplate = function(vars, options) {\n\t\tvars = vars || {};\n\t\tif(this.length) {\n\t\t\tvar _template = Object.create(Template);\n\t\t\treturn _template.init(vars, options, this);\n\t\t}\n\t};\n\n})( jQuery );\n\n\n\n\/**\n * ownCloud\n *\n * @author Robin Appelman\n * @copyright 2012 Robin Appelman icewind1991@gmail.com\n *\n * This library is free software; you can redistribute it and\/or\n * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE\n * License as published by the Free Software Foundation; either\n * version 3 of the License, or any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU AFFERO GENERAL PUBLIC LICENSE for more details.\n *\n * You should have received a copy of the GNU Affero General Public\n * License along with this library. If not, see <http:\/\/www.gnu.org\/licenses\/>.\n *\n *\/\n\n\/**\n * Wrapper for server side events\n * (http:\/\/en.wikipedia.org\/wiki\/Server-sent_events)\n * includes a fallback for older browsers and IE\n *\n * use server side events with caution, too many open requests can hang the\n * server\n *\/\n\n\/* global EventSource *\/\n\n\/**\n * Create a new event source\n * @param {string} src\n * @param {object} [data] to be send as GET\n *\n * @constructs OC.EventSource\n *\/\nOC.EventSource=function(src,data){\n\tvar dataStr='';\n\tvar name;\n\tvar joinChar;\n\tthis.typelessListeners=[];\n\tthis.closed = false;\n\tthis.listeners={};\n\tif(data){\n\t\tfor(name in data){\n\t\t\tdataStr+=name+'='+encodeURIComponent(data[name])+'&';\n\t\t}\n\t}\n\tdataStr+='requesttoken='+encodeURIComponent(oc_requesttoken);\n\tif(!this.useFallBack && typeof EventSource !== 'undefined'){\n\t\tjoinChar = '&';\n\t\tif(src.indexOf('?') === -1) {\n\t\t\tjoinChar = '?';\n\t\t}\n\t\tthis.source= new EventSource(src+joinChar+dataStr);\n\t\tthis.source.onmessage=function(e){\n\t\t\tfor(var i=0;i<this.typelessListeners.length;i++){\n\t\t\t\tthis.typelessListeners[i](JSON.parse(e.data));\n\t\t\t}\n\t\t}.bind(this);\n\t}else{\n\t\tvar iframeId='oc_eventsource_iframe_'+OC.EventSource.iframeCount;\n\t\tOC.EventSource.fallBackSources[OC.EventSource.iframeCount]=this;\n\t\tthis.iframe=$('<iframe\/>');\n\t\tthis.iframe.attr('id',iframeId);\n\t\tthis.iframe.hide();\n\n\t\tjoinChar = '&';\n\t\tif(src.indexOf('?') === -1) {\n\t\t\tjoinChar = '?';\n\t\t}\n\t\tthis.iframe.attr('src',src+joinChar+'fallback=true&fallback_id='+OC.EventSource.iframeCount+'&'+dataStr);\n\t\t$('body').append(this.iframe);\n\t\tthis.useFallBack=true;\n\t\tOC.EventSource.iframeCount++;\n\t}\n\t\/\/add close listener\n\tthis.listen('__internal__',function(data){\n\t\tif(data === 'close'){\n\t\t\tthis.close();\n\t\t}\n\t}.bind(this));\n};\nOC.EventSource.fallBackSources=[];\nOC.EventSource.iframeCount=0;\/\/number of fallback iframes\nOC.EventSource.fallBackCallBack=function(id,type,data){\n\tOC.EventSource.fallBackSources[id].fallBackCallBack(type,data);\n};\nOC.EventSource.prototype={\n\ttypelessListeners:[],\n\tiframe:null,\n\tlisteners:{},\/\/only for fallback\n\tuseFallBack:false,\n\t\/**\n\t * Fallback callback for browsers that don't have the\n\t * native EventSource object.\n\t *\n\t * Calls the registered listeners.\n\t *\n\t * @private\n\t * @param {String} type event type\n\t * @param {Object} data received data\n\t *\/\n\tfallBackCallBack:function(type,data){\n\t\tvar i;\n\t\t\/\/ ignore messages that might appear after closing\n\t\tif (this.closed) {\n\t\t\treturn;\n\t\t}\n\t\tif(type){\n\t\t\tif (typeof this.listeners.done !== 'undefined') {\n\t\t\t\tfor(i=0;i<this.listeners[type].length;i++){\n\t\t\t\t\tthis.listeners[type][i](data);\n\t\t\t\t}\n\t\t\t}\n\t\t}else{\n\t\t\tfor(i=0;i<this.typelessListeners.length;i++){\n\t\t\t\tthis.typelessListeners[i](data);\n\t\t\t}\n\t\t}\n\t},\n\tlastLength:0,\/\/for fallback\n\t\/**\n\t * Listen to a given type of events.\n\t *\n\t * @param {String} type event type\n\t * @param {Function} callback event callback\n\t *\/\n\tlisten:function(type,callback){\n\t\tif(callback && callback.call){\n\n\t\t\tif(type){\n\t\t\t\tif(this.useFallBack){\n\t\t\t\t\tif(!this.listeners[type]){\n\t\t\t\t\t\tthis.listeners[type]=[];\n\t\t\t\t\t}\n\t\t\t\t\tthis.listeners[type].push(callback);\n\t\t\t\t}else{\n\t\t\t\t\tthis.source.addEventListener(type,function(e){\n\t\t\t\t\t\tif (typeof e.data !== 'undefined') {\n\t\t\t\t\t\t\tcallback(JSON.parse(e.data));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcallback('');\n\t\t\t\t\t\t}\n\t\t\t\t\t},false);\n\t\t\t\t}\n\t\t\t}else{\n\t\t\t\tthis.typelessListeners.push(callback);\n\t\t\t}\n\t\t}\n\t},\n\t\/**\n\t * Closes this event source.\n\t *\/\n\tclose:function(){\n\t\tthis.closed = true;\n\t\tif (typeof this.source !== 'undefined') {\n\t\t\tthis.source.close();\n\t\t}\n\t}\n};\n\n\n\/**\n * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>\n *\n * @license GNU AGPL version 3 or any later version\n *\n * This program is free software: you can redistribute it and\/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http:\/\/www.gnu.org\/licenses\/>.\n *\n *\/\n\n\/**\n * @namespace\n * @since 11.0.0\n *\/\nOCP.AppConfig = {\n\t\/**\n\t * @param {string} method\n\t * @param {string} endpoint\n\t * @param {Object} [options]\n\t * @param {Object} [options.data]\n\t * @param {function} [options.success]\n\t * @param {function} [options.error]\n\t * @internal\n\t *\/\n\t_call: function(method, endpoint, options) {\n\t\tif ((method === 'post' || method === 'delete') && OC.PasswordConfirmation.requiresPasswordConfirmation()) {\n\t\t\tOC.PasswordConfirmation.requirePasswordConfirmation(_.bind(this._call, this, method, endpoint, options));\n\t\t\treturn;\n\t\t}\n\n\t\toptions = options || {};\n\t\t$.ajax({\n\t\t\ttype: method.toUpperCase(),\n\t\t\turl: OC.linkToOCS('apps\/provisioning_api\/api\/v1', 2) + 'config\/apps' + endpoint,\n\t\t\tdata: options.data || {},\n\t\t\tsuccess: options.success,\n\t\t\terror: options.error\n\t\t});\n\t},\n\n\t\/**\n\t * @param {Object} [options]\n\t * @param {function} [options.success]\n\t * @since 11.0.0\n\t *\/\n\tgetApps: function(options) {\n\t\tthis._call('get', '', options);\n\t},\n\n\t\/**\n\t * @param {string} app\n\t * @param {Object} [options]\n\t * @param {function} [options.success]\n\t * @param {function} [options.error]\n\t * @since 11.0.0\n\t *\/\n\tgetKeys: function(app, options) {\n\t\tthis._call('get', '\/' + app, options);\n\t},\n\n\t\/**\n\t * @param {string} app\n\t * @param {string} key\n\t * @param {string|function} defaultValue\n\t * @param {Object} [options]\n\t * @param {function} [options.success]\n\t * @param {function} [options.error]\n\t * @since 11.0.0\n\t *\/\n\tgetValue: function(app, key, defaultValue, options) {\n\t\toptions = options || {};\n\t\toptions.data = {\n\t\t\tdefaultValue: defaultValue\n\t\t};\n\n\t\tthis._call('get', '\/' + app + '\/' + key, options);\n\t},\n\n\t\/**\n\t * @param {string} app\n\t * @param {string} key\n\t * @param {string} value\n\t * @param {Object} [options]\n\t * @param {function} [options.success]\n\t * @param {function} [options.error]\n\t * @since 11.0.0\n\t *\/\n\tsetValue: function(app, key, value, options) {\n\t\toptions = options || {};\n\t\toptions.data = {\n\t\t\tvalue: value\n\t\t};\n\n\t\tthis._call('post', '\/' + app + '\/' + key, options);\n\t},\n\n\t\/**\n\t * @param {string} app\n\t * @param {string} key\n\t * @param {Object} [options]\n\t * @param {function} [options.success]\n\t * @param {function} [options.error]\n\t * @since 11.0.0\n\t *\/\n\tdeleteKey: function(app, key, options) {\n\t\tthis._call('delete', '\/' + app + '\/' + key, options);\n\t}\n};\n\n\n\/**\n * @copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>\n *\n * @author Arthur Schiwon <blizzz@arthur-schiwon.de>\n *\n * This file is licensed under the Affero General Public License version 3 or\n * later. See the COPYING file.\n *\/\n\n(function(OCP) {\n\t\"use strict\";\n\n\tOCP.Comments = {\n\n\t\t\/*\n\t\t * Detects links:\n\t\t * Either the http(s) protocol is given or two strings, basically limited to ascii with the last\n\t\t * \tword being at least one digit long,\n\t\t * followed by at least another character\n\t\t *\n\t\t * The downside: anything not ascii is excluded. Not sure how common it is in areas using different\n\t\t * alphabets\u2026 the upside: fake domains with similar looking characters won't be formatted as links\n\t\t *\/\n\t\turlRegex: \/(\\s|^)(https?:\\\/\\\/)?((?:[-A-Z0-9+_]+\\.)+[-A-Z]+(?:\\\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\\s|$)\/ig,\n\n\t\tplainToRich: function(content) {\n\t\t\tcontent = this.formatLinksRich(content);\n\t\t\treturn content;\n\t\t},\n\n\t\trichToPlain: function(content) {\n\t\t\tcontent = this.formatLinksPlain(content);\n\t\t\treturn content;\n\t\t},\n\n\t\tformatLinksRich: function(content) {\n\t\t\treturn content.replace(this.urlRegex, function(_, leadingSpace, protocol, url, trailingSpace) {\n\t\t\t\tvar linkText = url;\n\t\t\t\tif(!protocol) {\n\t\t\t\t\tprotocol = 'https:\/\/';\n\t\t\t\t} else if (protocol === 'http:\/\/'){\n\t\t\t\t\tlinkText = protocol + url;\n\t\t\t\t}\n\n\t\t\t\treturn leadingSpace + '<a class=\"external\" target=\"_blank\" rel=\"noopener noreferrer\" href=\"' + protocol + url + '\">' + linkText + '<\/a>' + trailingSpace;\n\t\t\t});\n\t\t},\n\n\t\tformatLinksPlain: function(content) {\n\t\t\tvar $content = $('<div><\/div>').html(content);\n\t\t\t$content.find('a').each(function () {\n\t\t\t\tvar $this = $(this);\n\t\t\t\t$this.html($this.attr('href'));\n\t\t\t});\n\t\t\treturn $content.html();\n\t\t}\n\n\t};\n})(OCP);\n\n\n\/**\n * @copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>\n *\n * @author Arthur Schiwon <blizzz@arthur-schiwon.de>\n *\n * This file is licensed under the Affero General Public License version 3 or\n * later. See the COPYING file.\n *\/\n\n(function(OCP) {\n\t\"use strict\";\n\n\tOCP.WhatsNew = {\n\n\t\tquery: function(options) {\n\t\t\toptions = options || {};\n\t\t\tvar dismissOptions = options.dismiss || {};\n\t\t\t$.ajax({\n\t\t\t\ttype: 'GET',\n\t\t\t\turl: options.url || OC.linkToOCS('core', 2) + 'whatsnew?format=json',\n\t\t\t\tsuccess: options.success || function(data, statusText, xhr) {\n\t\t\t\t\tOCP.WhatsNew._onQuerySuccess(data, statusText, xhr, dismissOptions);\n\t\t\t\t},\n\t\t\t\terror: options.error || this._onQueryError\n\t\t\t});\n\t\t},\n\n\t\tdismiss: function(version, options) {\n\t\t\toptions = options || {};\n\t\t\t$.ajax({\n\t\t\t\ttype: 'POST',\n\t\t\t\turl: options.url || OC.linkToOCS('core', 2) + 'whatsnew',\n\t\t\t\tdata: {version: encodeURIComponent(version)},\n\t\t\t\tsuccess: options.success || this._onDismissSuccess,\n\t\t\t\terror: options.error || this._onDismissError\n\t\t\t});\n\t\t\t\/\/ remove element immediately\n\t\t\t$('.whatsNewPopover').remove();\n\t\t},\n\n\t\t_onQuerySuccess: function(data, statusText, xhr, dismissOptions) {\n\t\t\tconsole.debug('querying Whats New data was successful: ' + statusText);\n\t\t\tconsole.debug(data);\n\n\t\t\tif(xhr.status !== 200) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar item, menuItem, text, icon;\n\n\t\t\tvar div = document.createElement('div');\n\t\t\tdiv.classList.add('popovermenu', 'open', 'whatsNewPopover', 'menu-left');\n\n\t\t\tvar list = document.createElement('ul');\n\n\t\t\t\/\/ header\n\t\t\titem = document.createElement('li');\n\t\t\tmenuItem = document.createElement('span');\n\t\t\tmenuItem.className = \"menuitem\";\n\n\t\t\ttext = document.createElement('span');\n\t\t\ttext.innerText = t('core', 'New in') + ' ' + data['ocs']['data']['product'];\n\t\t\ttext.className = 'caption';\n\t\t\tmenuItem.appendChild(text);\n\n\t\t\ticon = document.createElement('span');\n\t\t\ticon.className = 'icon-close';\n\t\t\ticon.onclick = function () {\n\t\t\t\tOCP.WhatsNew.dismiss(data['ocs']['data']['version'], dismissOptions);\n\t\t\t};\n\t\t\tmenuItem.appendChild(icon);\n\n\t\t\titem.appendChild(menuItem);\n\t\t\tlist.appendChild(item);\n\n\t\t\t\/\/ Highlights\n\t\t\tfor (var i in data['ocs']['data']['whatsNew']['regular']) {\n\t\t\t\tvar whatsNewTextItem = data['ocs']['data']['whatsNew']['regular'][i];\n\t\t\t\titem = document.createElement('li');\n\n\t\t\t\tmenuItem = document.createElement('span');\n\t\t\t\tmenuItem.className = \"menuitem\";\n\n\t\t\t\ticon = document.createElement('span');\n\t\t\t\ticon.className = 'icon-checkmark';\n\t\t\t\tmenuItem.appendChild(icon);\n\n\t\t\t\ttext = document.createElement('p');\n\t\t\t\ttext.innerHTML = _.escape(whatsNewTextItem);\n\t\t\t\tmenuItem.appendChild(text);\n\n\t\t\t\titem.appendChild(menuItem);\n\t\t\t\tlist.appendChild(item);\n\t\t\t}\n\n\t\t\t\/\/ Changelog URL\n\t\t\tif(!_.isUndefined(data['ocs']['data']['changelogURL'])) {\n\t\t\t\titem = document.createElement('li');\n\n\t\t\t\tmenuItem = document.createElement('a');\n\t\t\t\tmenuItem.href = data['ocs']['data']['changelogURL'];\n\t\t\t\tmenuItem.rel = 'noreferrer noopener';\n\t\t\t\tmenuItem.target = '_blank';\n\n\t\t\t\ticon = document.createElement('span');\n\t\t\t\ticon.className = 'icon-link';\n\t\t\t\tmenuItem.appendChild(icon);\n\n\t\t\t\ttext = document.createElement('span');\n\t\t\t\ttext.innerText = t('core', 'View changelog');\n\t\t\t\tmenuItem.appendChild(text);\n\n\t\t\t\titem.appendChild(menuItem);\n\t\t\t\tlist.appendChild(item);\n\t\t\t}\n\n\t\t\tdiv.appendChild(list);\n\t\t\tdocument.body.appendChild(div);\n\t\t},\n\n\t\t_onQueryError: function (x, t, e) {\n\t\t\tconsole.debug('querying Whats New Data resulted in an error: ' + t + e);\n\t\t\tconsole.debug(x);\n\t\t},\n\n\t\t_onDismissSuccess: function(data) {\n\t\t\t\/\/noop\n\t\t},\n\n\t\t_onDismissError: function (data) {\n\t\t\tconsole.debug('dismissing Whats New data resulted in an error: ' + data);\n\t\t}\n\t};\n})(OCP);\n\n\n\/**\n * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>\n *\n * @license GNU AGPL version 3 or any later version\n *\n * This program is free software: you can redistribute it and\/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http:\/\/www.gnu.org\/licenses\/>.\n *\n *\/\n\n\/**\n * @namespace\n * @deprecated Use OCP.AppConfig instead\n *\/\nOC.AppConfig={\n\t\/**\n\t * @deprecated Use OCP.AppConfig.getValue() instead\n\t *\/\n\tgetValue:function(app,key,defaultValue,callback){\n\t\tOCP.AppConfig.getValue(app, key, defaultValue, {\n\t\t\tsuccess: callback\n\t\t});\n\t},\n\n\t\/**\n\t * @deprecated Use OCP.AppConfig.setValue() instead\n\t *\/\n\tsetValue:function(app,key,value){\n\t\tOCP.AppConfig.setValue(app, key, value);\n\t},\n\n\t\/**\n\t * @deprecated Use OCP.AppConfig.getApps() instead\n\t *\/\n\tgetApps:function(callback){\n\t\tOCP.AppConfig.getApps({\n\t\t\tsuccess: callback\n\t\t});\n\t},\n\n\t\/**\n\t * @deprecated Use OCP.AppConfig.getKeys() instead\n\t *\/\n\tgetKeys:function(app,callback){\n\t\tOCP.AppConfig.getKeys(app, {\n\t\t\tsuccess: callback\n\t\t});\n\t},\n\n\t\/**\n\t * @deprecated Use OCP.AppConfig.deleteKey() instead\n\t *\/\n\tdeleteKey:function(app,key){\n\t\tOCP.AppConfig.deleteKey(app, key);\n\t}\n};\n\n\n$(document).on('ajaxSend',function(elm, xhr, settings) {\n\tif(settings.crossDomain === false) {\n\t\txhr.setRequestHeader('requesttoken', oc_requesttoken);\n\t\txhr.setRequestHeader('OCS-APIREQUEST', 'true');\n\t}\n});\n\n\n\/**\n * ownCloud - core\n *\n * This file is licensed under the Affero General Public License version 3 or\n * later. See the COPYING file.\n *\n * @author Bernhard Posselt <dev@bernhard-posselt.com>\n * @copyright Bernhard Posselt 2014\n *\/\n\n(function (document, $, exports) {\n\n\t'use strict';\n\n\tvar dynamicSlideToggleEnabled = false;\n\n\texports.Apps = {\n\t\tenableDynamicSlideToggle: function () {\n\t\t\tdynamicSlideToggleEnabled = true;\n\t\t}\n\t};\n\n\t\/**\n\t * Shows the #app-sidebar and add .with-app-sidebar to subsequent siblings\n\t *\n\t * @param {Object} [$el] sidebar element to show, defaults to $('#app-sidebar')\n\t *\/\n\texports.Apps.showAppSidebar = function($el) {\n\t\tvar $appSidebar = $el || $('#app-sidebar');\n\t\t$appSidebar.removeClass('disappear')\n\t\t\t.show('slide', { direction: 'right' }, 300);\n\t\t$('#app-content').trigger(new $.Event('appresized'));\n\t};\n\n\t\/**\n\t * Shows the #app-sidebar and removes .with-app-sidebar from subsequent\n\t * siblings\n\t *\n\t * @param {Object} [$el] sidebar element to hide, defaults to $('#app-sidebar')\n\t *\/\n\texports.Apps.hideAppSidebar = function($el) {\n\t\tvar $appSidebar = $el || $('#app-sidebar');\n\t\t$appSidebar.hide('slide', { direction: 'right' }, 300,\n\t\t\tfunction() {\n\t\t\t\t$appSidebar.addClass('disappear');\n\t\t\t});\n\t\t$('#app-content').trigger(new $.Event('appresized'));\n\t};\n\n\t\/**\n\t * Provides a way to slide down a target area through a button and slide it\n\t * up if the user clicks somewhere else. Used for the news app settings and\n\t * add new field.\n\t *\n\t * Usage:\n\t * <button data-apps-slide-toggle=\".slide-area\">slide<\/button>\n\t * <div class=\".slide-area\" class=\"hidden\">I'm sliding up<\/div>\n\t *\/\n\tvar registerAppsSlideToggle = function () {\n\t\tvar buttons = $('[data-apps-slide-toggle]');\n\n\t\tif (buttons.length === 0) {\n\t\t\t$('#app-navigation').addClass('without-app-settings');\n\t\t}\n\n\t\t$(document).click(function (event) {\n\n\t\t\tif (dynamicSlideToggleEnabled) {\n\t\t\t\tbuttons = $('[data-apps-slide-toggle]');\n\t\t\t}\n\n\t\t\tbuttons.each(function (index, button) {\n\n\t\t\t\tvar areaSelector = $(button).data('apps-slide-toggle');\n\t\t\t\tvar area = $(areaSelector);\n\n\t\t\t\tfunction hideArea() {\n\t\t\t\t\tarea.slideUp(OC.menuSpeed*4, function() {\n\t\t\t\t\t\tarea.trigger(new $.Event('hide'));\n\t\t\t\t\t});\n\t\t\t\t\tarea.removeClass('opened');\n\t\t\t\t\t$(button).removeClass('opened');\n\t\t\t\t}\n\t\t\t\tfunction showArea() {\n\t\t\t\t\tarea.slideDown(OC.menuSpeed*4, function() {\n\t\t\t\t\t\tarea.trigger(new $.Event('show'));\n\t\t\t\t\t});\n\t\t\t\t\tarea.addClass('opened');\n\t\t\t\t\t$(button).addClass('opened');\n\t\t\t\t\tvar input = $(areaSelector + ' [autofocus]');\n\t\t\t\t\tif (input.length === 1) {\n\t\t\t\t\t\tinput.focus();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t\/\/ do nothing if the area is animated\n\t\t\t\tif (!area.is(':animated')) {\n\n\t\t\t\t\t\/\/ button toggles the area\n\t\t\t\t\tif ($(button).is($(event.target).closest('[data-apps-slide-toggle]'))) {\n\t\t\t\t\t\tif (area.is(':visible')) {\n\t\t\t\t\t\t\thideArea();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tshowArea();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\/\/ all other areas that have not been clicked but are open\n\t\t\t\t\t\/\/ should be slid up\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar closest = $(event.target).closest(areaSelector);\n\t\t\t\t\t\tif (area.is(':visible') && closest[0] !== area[0]) {\n\t\t\t\t\t\t\thideArea();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t});\n\t};\n\n\n\t$(document).ready(function () {\n\t\tregisterAppsSlideToggle();\n\t});\n\n}(document, jQuery, OC));\n\n\n\/**\n * @author Roeland Jago Douma <roeland@famdouma.nl>\n *\n * @copyright Copyright (c) 2015, ownCloud, Inc.\n * @license AGPL-3.0\n *\n * This code is free software: you can redistribute it and\/or modify\n * it under the terms of the GNU Affero General Public License, version 3,\n * as published by the Free Software Foundation.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License, version 3,\n * along with this program. If not, see <http:\/\/www.gnu.org\/licenses\/>\n *\n *\/\n\n\/**\n * Namespace to hold functions related to convert mimetype to icons\n *\n * @namespace\n *\/\nOC.MimeType = {\n\n\t\/**\n\t * Cache that maps mimeTypes to icon urls\n\t *\/\n\t_mimeTypeIcons: {},\n\n\t\/**\n\t * Return the file icon we want to use for the given mimeType.\n\t * The file needs to be present in the supplied file list\n\t *\n\t * @param {string} mimeType The mimeType we want an icon for\n\t * @param {array} files The available icons in this theme\n\t * @return {string} The icon to use or null if there is no match\n\t *\/\n\t_getFile: function(mimeType, files) {\n\t\tvar icon = mimeType.replace(new RegExp('\/', 'g'), '-');\n\n\t\t\/\/ Generate path\n\t\tif (mimeType === 'dir' && $.inArray('folder', files) !== -1) {\n\t\t\treturn 'folder';\n\t\t} else if (mimeType === 'dir-encrypted' && $.inArray('folder-encrypted', files) !== -1) {\n\t\t\treturn 'folder-encrypted';\n\t\t} else if (mimeType === 'dir-shared' && $.inArray('folder-shared', files) !== -1) {\n\t\t\treturn 'folder-shared';\n\t\t} else if (mimeType === 'dir-public' && $.inArray('folder-public', files) !== -1) {\n\t\t\treturn 'folder-public';\n\t\t} else if (mimeType === 'dir-external' && $.inArray('folder-external', files) !== -1) {\n\t\t\treturn 'folder-external';\n\t\t} else if ($.inArray(icon, files) !== -1) {\n\t\t\treturn icon;\n\t\t} else if ($.inArray(icon.split('-')[0], files) !== -1) {\n\t\t\treturn icon.split('-')[0];\n\t\t} else if ($.inArray('file', files) !== -1) {\n\t\t\treturn 'file';\n\t\t}\n\n\t\treturn null;\n\t},\n\n\t\/**\n\t * Return the url to icon of the given mimeType\n\t *\n\t * @param {string} mimeType The mimeType to get the icon for\n\t * @return {string} Url to the icon for mimeType\n\t *\/\n\tgetIconUrl: function(mimeType) {\n\t\tif (_.isUndefined(mimeType)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\twhile (mimeType in OC.MimeTypeList.aliases) {\n\t\t\tmimeType = OC.MimeTypeList.aliases[mimeType];\n\t\t}\n\t\tif (mimeType in OC.MimeType._mimeTypeIcons) {\n\t\t\treturn OC.MimeType._mimeTypeIcons[mimeType];\n\t\t}\n\n\t\t\/\/ First try to get the correct icon from the current theme\n\t\tvar gotIcon = null;\n\t\tvar path = '';\n\t\tif (OC.theme.folder !== '' && $.isArray(OC.MimeTypeList.themes[OC.theme.folder])) {\n\t\t\tpath = OC.getRootPath() + '\/themes\/' + OC.theme.folder + '\/core\/img\/filetypes\/';\n\t\t\tvar icon = OC.MimeType._getFile(mimeType, OC.MimeTypeList.themes[OC.theme.folder]);\n\n\t\t\tif (icon !== null) {\n\t\t\t\tgotIcon = true;\n\t\t\t\tpath += icon;\n\t\t\t}\n\t\t}\n\t\tif(OCA.Theming && gotIcon === null) {\n\t\t\tpath = OC.generateUrl('\/apps\/theming\/img\/core\/filetypes\/');\n\t\t\tpath += OC.MimeType._getFile(mimeType, OC.MimeTypeList.files);\n\t\t\tgotIcon = true;\n\t\t}\n\n\t\t\/\/ If we do not yet have an icon fall back to the default\n\t\tif (gotIcon === null) {\n\t\t\tpath = OC.getRootPath() + '\/core\/img\/filetypes\/';\n\t\t\tpath += OC.MimeType._getFile(mimeType, OC.MimeTypeList.files);\n\t\t}\n\n\t\tpath += '.svg';\n\n\t\tif(OCA.Theming) {\n\t\t\tpath += \"?v=\" + OCA.Theming.cacheBuster;\n\t\t}\n\n\t\t\/\/ Cache the result\n\t\tOC.MimeType._mimeTypeIcons[mimeType] = path;\n\t\treturn path;\n\t}\n\n};\n\n\n\/**\n* This file is automatically generated\n* DO NOT EDIT MANUALLY!\n*\n* You can update the list of MimeType Aliases in config\/mimetypealiases.json\n* The list of files is fetched from core\/img\/filetypes\n* To regenerate this file run .\/occ maintenance:mimetype:update-js\n*\/\nOC.MimeTypeList={\n\taliases: {\n \"application\/coreldraw\": \"image\",\n \"application\/epub+zip\": \"text\",\n \"application\/font-sfnt\": \"image\",\n \"application\/font-woff\": \"image\",\n \"application\/gpx+xml\": \"location\",\n \"application\/illustrator\": \"image\",\n \"application\/javascript\": \"text\/code\",\n \"application\/json\": \"text\/code\",\n \"application\/msaccess\": \"file\",\n \"application\/msexcel\": \"x-office\/spreadsheet\",\n \"application\/msonenote\": \"x-office\/document\",\n \"application\/mspowerpoint\": \"x-office\/presentation\",\n \"application\/msword\": \"x-office\/document\",\n \"application\/octet-stream\": \"file\",\n \"application\/postscript\": \"image\",\n \"application\/rss+xml\": \"application\/xml\",\n \"application\/vnd.android.package-archive\": \"package\/x-generic\",\n \"application\/vnd.lotus-wordpro\": \"x-office\/document\",\n \"application\/vnd.garmin.tcx+xml\": \"location\",\n \"application\/vnd.google-earth.kml+xml\": \"location\",\n \"application\/vnd.google-earth.kmz\": \"location\",\n \"application\/vnd.ms-excel\": \"x-office\/spreadsheet\",\n \"application\/vnd.ms-excel.addin.macroEnabled.12\": \"x-office\/spreadsheet\",\n \"application\/vnd.ms-excel.sheet.binary.macroEnabled.12\": \"x-office\/spreadsheet\",\n \"application\/vnd.ms-excel.sheet.macroEnabled.12\": \"x-office\/spreadsheet\",\n \"application\/vnd.ms-excel.template.macroEnabled.12\": \"x-office\/spreadsheet\",\n \"application\/vnd.ms-fontobject\": \"image\",\n \"application\/vnd.ms-powerpoint\": \"x-office\/presentation\",\n \"application\/vnd.ms-powerpoint.addin.macroEnabled.12\": \"x-office\/presentation\",\n \"application\/vnd.ms-powerpoint.presentation.macroEnabled.12\": \"x-office\/presentation\",\n \"application\/vnd.ms-powerpoint.slideshow.macroEnabled.12\": \"x-office\/presentation\",\n \"application\/vnd.ms-powerpoint.template.macroEnabled.12\": \"x-office\/presentation\",\n \"application\/vnd.ms-visio.drawing.macroEnabled.12\": \"application\/vnd.visio\",\n \"application\/vnd.ms-visio.drawing\": \"application\/vnd.visio\",\n \"application\/vnd.ms-visio.stencil.macroEnabled.12\": \"application\/vnd.visio\",\n \"application\/vnd.ms-visio.stencil\": \"application\/vnd.visio\",\n \"application\/vnd.ms-visio.template.macroEnabled.12\": \"application\/vnd.visio\",\n \"application\/vnd.ms-visio.template\": \"application\/vnd.visio\",\n \"application\/vnd.ms-word.document.macroEnabled.12\": \"x-office\/document\",\n \"application\/vnd.ms-word.template.macroEnabled.12\": \"x-office\/document\",\n \"application\/vnd.oasis.opendocument.presentation\": \"x-office\/presentation\",\n \"application\/vnd.oasis.opendocument.presentation-template\": \"x-office\/presentation\",\n \"application\/vnd.oasis.opendocument.spreadsheet\": \"x-office\/spreadsheet\",\n \"application\/vnd.oasis.opendocument.spreadsheet-template\": \"x-office\/spreadsheet\",\n \"application\/vnd.oasis.opendocument.text\": \"x-office\/document\",\n \"application\/vnd.oasis.opendocument.text-master\": \"x-office\/document\",\n \"application\/vnd.oasis.opendocument.text-template\": \"x-office\/document\",\n \"application\/vnd.oasis.opendocument.text-web\": \"x-office\/document\",\n \"application\/vnd.openxmlformats-officedocument.presentationml.presentation\": \"x-office\/presentation\",\n \"application\/vnd.openxmlformats-officedocument.presentationml.slideshow\": \"x-office\/presentation\",\n \"application\/vnd.openxmlformats-officedocument.presentationml.template\": \"x-office\/presentation\",\n \"application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet\": \"x-office\/spreadsheet\",\n \"application\/vnd.openxmlformats-officedocument.spreadsheetml.template\": \"x-office\/spreadsheet\",\n \"application\/vnd.openxmlformats-officedocument.wordprocessingml.document\": \"x-office\/document\",\n \"application\/vnd.openxmlformats-officedocument.wordprocessingml.template\": \"x-office\/document\",\n \"application\/vnd.visio\": \"x-office\/document\",\n \"application\/vnd.wordperfect\": \"x-office\/document\",\n \"application\/x-7z-compressed\": \"package\/x-generic\",\n \"application\/x-bzip2\": \"package\/x-generic\",\n \"application\/x-cbr\": \"text\",\n \"application\/x-compressed\": \"package\/x-generic\",\n \"application\/x-dcraw\": \"image\",\n \"application\/x-deb\": \"package\/x-generic\",\n \"application\/x-fictionbook+xml\": \"text\",\n \"application\/x-font\": \"image\",\n \"application\/x-gimp\": \"image\",\n \"application\/x-gzip\": \"package\/x-generic\",\n \"application\/x-iwork-keynote-sffkey\": \"x-office\/presentation\",\n \"application\/x-iwork-numbers-sffnumbers\": \"x-office\/spreadsheet\",\n \"application\/x-iwork-pages-sffpages\": \"x-office\/document\",\n \"application\/x-mobipocket-ebook\": \"text\",\n \"application\/x-perl\": \"text\/code\",\n \"application\/x-photoshop\": \"image\",\n \"application\/x-php\": \"text\/code\",\n \"application\/x-rar-compressed\": \"package\/x-generic\",\n \"application\/x-tar\": \"package\/x-generic\",\n \"application\/x-tex\": \"text\",\n \"application\/xml\": \"text\/html\",\n \"application\/yaml\": \"text\/code\",\n \"application\/zip\": \"package\/x-generic\",\n \"database\": \"file\",\n \"httpd\/unix-directory\": \"dir\",\n \"text\/css\": \"text\/code\",\n \"text\/csv\": \"x-office\/spreadsheet\",\n \"text\/html\": \"text\/code\",\n \"text\/x-c\": \"text\/code\",\n \"text\/x-c++src\": \"text\/code\",\n \"text\/x-h\": \"text\/code\",\n \"text\/x-java-source\": \"text\/code\",\n \"text\/x-ldif\": \"text\/code\",\n \"text\/x-python\": \"text\/code\",\n \"text\/x-shellscript\": \"text\/code\",\n \"web\": \"text\/code\",\n \"application\/internet-shortcut\": \"link\"\n},\n\tfiles: [\n \"application\",\n \"application-pdf\",\n \"audio\",\n \"file\",\n \"folder\",\n \"folder-drag-accept\",\n \"folder-encrypted\",\n \"folder-external\",\n \"folder-public\",\n \"folder-shared\",\n \"folder-starred\",\n \"image\",\n \"link\",\n \"location\",\n \"package-x-generic\",\n \"text\",\n \"text-calendar\",\n \"text-code\",\n \"text-vcard\",\n \"video\",\n \"x-office-document\",\n \"x-office-presentation\",\n \"x-office-spreadsheet\"\n],\n\tthemes: []\n};\n\n\n\/*\n * Copyright (c) 2015\n *\n * This file is licensed under the Affero General Public License version 3\n * or later.\n *\n * See the COPYING-README file.\n *\n *\/\n\n\/* global Backbone *\/\nif(!_.isUndefined(Backbone)) {\n\tOC.Backbone = Backbone.noConflict();\n}\n\n\n\/*\n * Copyright (c) 2015\n *\n * This file is licensed under the Affero General Public License version 3\n * or later.\n *\n * See the COPYING-README file.\n *\n *\/\n\n\/* global Select2 *\/\n\n\/**\n * Select2 extension for toggling values in a multi-select dropdown\n *\/\n(function(Select2) {\n\n\tvar Select2FindHighlightableChoices = Select2.class.multi.prototype.findHighlightableChoices;\n\tSelect2.class.multi.prototype.findHighlightableChoices = function () {\n\t\tif (this.opts.toggleSelect) {\n\t\t\treturn this.results.find('.select2-result-selectable:not(.select2-disabled)');\n\t\t}\n\t\treturn Select2FindHighlightableChoices.apply(this, arguments);\n\t};\n\n\tvar Select2TriggerSelect = Select2.class.multi.prototype.triggerSelect;\n\tSelect2.class.multi.prototype.triggerSelect = function (data) {\n\t\tif (this.opts.toggleSelect && this.val().indexOf(this.id(data)) !== -1) {\n\t\t\tvar self = this;\n\t\t\tvar val = this.id(data);\n\n\t\t\tvar selectionEls = this.container.find('.select2-search-choice').filter(function() {\n\t\t\t\treturn (self.id($(this).data('select2-data')) === val);\n\t\t\t});\n\n\t\t\tif (this.unselect(selectionEls)) {\n\t\t\t\t\/\/ also unselect in dropdown\n\t\t\t\tthis.results.find('.select2-result.select2-selected').each(function () {\n\t\t\t\t\tvar $this = $(this);\n\t\t\t\t\tif (self.id($this.data('select2-data')) === val) {\n\t\t\t\t\t\t$this.removeClass('select2-selected');\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tthis.clearSearch();\n\t\t\t}\n\n\t\t\treturn false;\n\t\t} else {\n\t\t\treturn Select2TriggerSelect.apply(this, arguments);\n\t\t}\n\t};\n\n})(Select2);\n\n\n\n\/**\n * ownCloud\n *\n * @author John Molakvo\u00e6\n * @copyright 2016-2018 John Molakvo\u00e6 <skjnldsv@protonmail.com>\n * @author Morris Jobke\n * @copyright 2013 Morris Jobke <morris.jobke@gmail.com>\n *\n * This library is free software; you can redistribute it and\/or\n * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE\n * License as published by the Free Software Foundation; either\n * version 3 of the License, or any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU AFFERO GENERAL PUBLIC LICENSE for more details.\n *\n * You should have received a copy of the GNU Affero General Public\n * License along with this library. If not, see <http:\/\/www.gnu.org\/licenses\/>.\n *\n *\/\n\n\/*\n * Adds a background color to the element called on and adds the first character\n * of the passed in string. This string is also the seed for the generation of\n * the background color.\n *\n * You have following HTML:\n *\n * <div id=\"albumart\"><\/div>\n *\n * And call this from Javascript:\n *\n * $('#albumart').imageplaceholder('The Album Title');\n *\n * Which will result in:\n *\n * <div id=\"albumart\" style=\"background-color: rgb(121, 90, 171); ... \">T<\/div>\n *\n * You may also call it like this, to have a different background, than the seed:\n *\n * $('#albumart').imageplaceholder('The Album Title', 'Album Title');\n *\n * Resulting in:\n *\n * <div id=\"albumart\" style=\"background-color: rgb(121, 90, 171); ... \">A<\/div>\n *\n *\/\n\n \/*\n * Alternatively, you can use the prototype function to convert your string to rgb colors:\n *\n * \"a6741a86aded5611a8e46ce16f2ad646\".toRgb()\n *\n * Will return the rgb parameters within the following object:\n *\n * Color\u00a0{r: 208, g: 158, b: 109}\n *\n *\/\n\n(function ($) {\n\n\tString.prototype.toRgb = function() {\n\t\t\/\/ Normalize hash\t\t\n\t\tvar hash = this.toLowerCase();\n\n\t\t\/\/ Already a md5 hash?\n\t\tif( hash.match(\/^([0-9a-f]{4}-?){8}$\/) === null ) {\n\t\t\thash = md5(hash);\n\t\t}\n\n\t\thash = hash.replace(\/[^0-9a-f]\/g, '');\n\n\t\tfunction Color(r,g,b) {\n\t\t\tthis.r = r;\n\t\t\tthis.g = g;\n\t\t\tthis.b = b;\n\t\t}\n\n\t\tfunction stepCalc(steps, ends) {\n\t\t\tvar step = new Array(3);\n\t\t\tstep[0] = (ends[1].r - ends[0].r) \/ steps;\n\t\t\tstep[1] = (ends[1].g - ends[0].g) \/ steps;\n\t\t\tstep[2] = (ends[1].b - ends[0].b) \/ steps;\n\t\t\treturn step;\n\t\t}\n\n\t\tfunction mixPalette(steps, color1, color2) {\n\t\t\tvar count = steps + 1;\n\t\t\tvar palette = new Array();\n\t\t\tpalette.push(color1);\n\t\t\tvar step = stepCalc(steps, [color1, color2])\n\t\t\tfor (var i = 1; i < steps; i++) {\n\t\t\t\tvar r = parseInt(color1.r + (step[0] * i));\n\t\t\t\tvar g = parseInt(color1.g + (step[1] * i));\n\t\t\t\tvar b = parseInt(color1.b + (step[2] * i));\n\t\t\t\t\tpalette.push(new Color(r,g,b));\n\t\t\t}\n\t\t\treturn palette;\n\t\t}\n\n\t\tvar red = new Color(182, 70, 157);\n\t\tvar yellow = new Color(221, 203, 85);\n\t\tvar blue = new Color(0, 130, 201); \/\/ Nextcloud blue\n\t\t\/\/ Number of steps to go from a color to another\n\t\t\/\/ 3 colors * 6 will result in 18 generated colors\n\t\tvar steps = 6;\n\n\t\tvar palette1 = mixPalette(steps, red, yellow);\n\t\tvar palette2 = mixPalette(steps, yellow, blue);\n\t\tvar palette3 = mixPalette(steps, blue, red);\n\n\t\tvar finalPalette = palette1.concat(palette2).concat(palette3);\n\n\t\t\/\/ Convert a string to an integer evenly\n\t\tfunction hashToInt(hash, maximum) {\n\t\t\tvar finalInt = 0;\n\t\t\tvar result = Array();\n\n\t\t\t\/\/ Splitting evenly the string\n\t\t\tfor (var i=0; i<hash.length; i++) {\n\t\t\t\t\/\/ chars in md5 goes up to f, hex:16\n\t\t\t\tresult.push(parseInt(hash.charAt(i), 16) % 16);\n\t\t\t}\n\t\t\t\/\/ Adds up all results\n\t\t\tfor (var j in result) {\n\t\t\t\tfinalInt += result[j];\n\t\t\t}\n\t\t\t\/\/ chars in md5 goes up to f, hex:16\n\t\t\t\/\/ make sure we're always using int in our operation\n\t\t\treturn parseInt(parseInt(finalInt) % maximum);\n\t\t}\n\t\treturn finalPalette[hashToInt(hash, steps * 3 )];\n\t};\n\n\t$.fn.imageplaceholder = function(seed, text, size) {\n\t\ttext = text || seed;\n\n\t\t\/\/ Compute the hash\n\t\tvar rgb = seed.toRgb();\n\t\tthis.css('background-color', 'rgb('+rgb.r+', '+rgb.g+', '+rgb.b+')');\n\n\t\t\/\/ Placeholders are square\n\t\tvar height = this.height() || size || 32;\n\t\tthis.height(height);\n\t\tthis.width(height);\n\n\t\t\/\/ CSS rules\n\t\tthis.css('color', '#fff');\n\t\tthis.css('font-weight', 'normal');\n\t\tthis.css('text-align', 'center');\n\n\t\t\/\/ calculate the height\n\t\tthis.css('line-height', height + 'px');\n\t\tthis.css('font-size', (height * 0.55) + 'px');\n\n\t\tif(seed !== null && seed.length) {\n\t\t\tthis.html(text[0].toUpperCase());\n\t\t}\n\t};\n\n\t$.fn.clearimageplaceholder = function() {\n\t\tthis.css('background-color', '');\n\t\tthis.css('color', '');\n\t\tthis.css('font-weight', '');\n\t\tthis.css('text-align', '');\n\t\tthis.css('line-height', '');\n\t\tthis.css('font-size', '');\n\t\tthis.html('');\n\t\tthis.removeClass('icon-loading');\n\t\tthis.removeClass('icon-loading-small');\n\t};\n}(jQuery));\n\n\n\/**\n * Copyright (c) 2013 Christopher Sch\u00e4pers <christopher@schaepers.it>\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING-README file.\n *\/\n\n\/**\n * This plugin inserts the right avatar for the user, depending on, whether a\n * custom avatar is uploaded - which it uses then - or not, and display a\n * placeholder with the first letter of the users name instead.\n * For this it queries the core_avatar_get route, thus this plugin is fit very\n * tightly for owncloud, and it may not work anywhere else.\n *\n * You may use this on any <div><\/div>\n * Here I'm using <div class=\"avatardiv\"><\/div> as an example.\n *\n * There are 5 ways to call this:\n *\n * 1. $('.avatardiv').avatar('jdoe', 128);\n * This will make the div to jdoe's fitting avatar, with a size of 128px.\n *\n * 2. $('.avatardiv').avatar('jdoe');\n * This will make the div to jdoe's fitting avatar. If the div already has a\n * height, it will be used for the avatars size. Otherwise this plugin will\n * search for 'size' DOM data, to use for avatar size. If neither are available\n * it will default to 64px.\n *\n * 3. $('.avatardiv').avatar();\n * This will search the DOM for 'user' data, to use as the username. If there\n * is no username available it will default to a placeholder with the value of\n * \"?\". The size will be determined the same way, as the second example.\n *\n * 4. $('.avatardiv').avatar('jdoe', 128, true);\n * This will behave like the first example, except it will also append random\n * hashes to the custom avatar images, to force image reloading in IE8.\n *\n * 5. $('.avatardiv').avatar('jdoe', 128, undefined, true);\n * This will behave like the first example, but it will hide the avatardiv, if\n * it will display the default placeholder. undefined is the ie8fix from\n * example 4 and can be either true, or false\/undefined, to be ignored.\n *\n * 6. $('.avatardiv').avatar('jdoe', 128, undefined, true, callback);\n * This will behave like the above example, but it will call the function\n * defined in callback after the avatar is placed into the DOM.\n *\n *\/\n\n(function ($) {\n\t$.fn.avatar = function(user, size, ie8fix, hidedefault, callback, displayname) {\n\t\tvar setAvatarForUnknownUser = function(target) {\n\t\t\ttarget.imageplaceholder('?');\n\t\t\ttarget.css('background-color', '#b9b9b9');\n\t\t};\n\n\t\tif (typeof(user) !== 'undefined') {\n\t\t\tuser = String(user);\n\t\t}\n\t\tif (typeof(displayname) !== 'undefined') {\n\t\t\tdisplayname = String(displayname);\n\t\t}\n\n\t\tif (typeof(size) === 'undefined') {\n\t\t\tif (this.height() > 0) {\n\t\t\t\tsize = this.height();\n\t\t\t} else if (this.data('size') > 0) {\n\t\t\t\tsize = this.data('size');\n\t\t\t} else {\n\t\t\t\tsize = 64;\n\t\t\t}\n\t\t}\n\n\t\tthis.height(size);\n\t\tthis.width(size);\n\n\t\tif (typeof(user) === 'undefined') {\n\t\t\tif (typeof(this.data('user')) !== 'undefined') {\n\t\t\t\tuser = this.data('user');\n\t\t\t} else {\n\t\t\t\tsetAvatarForUnknownUser(this);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t\/\/ sanitize\n\t\tuser = String(user).replace(\/\\\/\/g,'');\n\n\t\tvar $div = this;\n\t\tvar url;\n\n\t\t\/\/ If this is our own avatar we have to use the version attribute\n\t\tif (user === OC.getCurrentUser().uid) {\n\t\t\turl = OC.generateUrl(\n\t\t\t\t'\/avatar\/{user}\/{size}?v={version}',\n\t\t\t\t{\n\t\t\t\t\tuser: user,\n\t\t\t\t\tsize: Math.ceil(size * window.devicePixelRatio),\n\t\t\t\t\tversion: oc_userconfig.avatar.version\n\t\t\t\t});\n\t\t} else {\n\t\t\turl = OC.generateUrl(\n\t\t\t\t'\/avatar\/{user}\/{size}',\n\t\t\t\t{\n\t\t\t\t\tuser: user,\n\t\t\t\t\tsize: Math.ceil(size * window.devicePixelRatio)\n\t\t\t\t});\n\t\t}\n\n\t\tvar img = new Image();\n\n\t\t\/\/ If the new image loads successfully set it.\n\t\timg.onload = function() {\n\t\t\t$div.clearimageplaceholder();\n\t\t\t$div.append(img);\n\n\t\t\tif(typeof callback === 'function') {\n\t\t\t\tcallback();\n\t\t\t}\n\t\t};\n\t\t\/\/ Fallback when avatar loading fails:\n\t\t\/\/ Use old placeholder when a displayname attribute is defined,\n\t\t\/\/ otherwise show the unknown user placeholder.\n\t\timg.onerror = function () {\n\t\t\t$div.clearimageplaceholder();\n\t\t\tif (typeof(displayname) !== 'undefined') {\n\t\t\t\t$div.imageplaceholder(user, displayname);\n\t\t\t} else {\n\t\t\t\tsetAvatarForUnknownUser($div);\n\t\t\t}\n\n\t\t\tif(typeof callback === 'function') {\n\t\t\t\tcallback();\n\t\t\t}\n\t\t};\n\n\t\tif (size < 32) {\n\t\t\t$div.addClass('icon-loading-small');\n\t\t} else {\n\t\t\t$div.addClass('icon-loading');\n\t\t}\n\t\timg.width = size;\n\t\timg.height = size;\n\t\timg.src = url;\n\t\timg.alt = '';\n\t};\n}(jQuery));\n\n\n\/**\n * Copyright (c) 2017 Georg Ehrke <oc.list@georgehrke.com>\n * This file is licensed under the Affero General Public License version 3 or\n * later.\n * See the COPYING-README file.\n *\/\n\n(function ($) {\n\n\tvar LIST = ''\n\t\t+ '<div class=\"menu popovermenu menu-left hidden contactsmenu-popover\">'\n\t\t+ ' <ul>'\n\t\t+ ' <li>'\n\t\t+ ' <a>'\n\t\t+ ' <span class=\"icon-loading-small\"><\/span>'\n\t\t+ ' <\/a>'\n\t\t+ ' <\/li>'\n\t\t+ ' <\/ul>'\n\t\t+ '<\/div>';\n\n\t$.fn.contactsMenu = function(shareWith, shareType, appendTo) {\n\t\t\/\/ 0 - user, 4 - email, 6 - remote\n\t\tvar allowedTypes = [0, 4, 6];\n\t\tif (allowedTypes.indexOf(shareType) === -1) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar $div = this;\n\t\tappendTo.append(LIST);\n\t\tvar $list = appendTo.find('div.contactsmenu-popover');\n\n\t\t$div.click(function() {\n\t\t\tif (!$list.hasClass('hidden')) {\n\t\t\t\t$list.addClass('hidden');\n\t\t\t\t$list.hide();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t$list.removeClass('hidden');\n\t\t\t$list.show();\n\n\t\t\tif ($list.hasClass('loaded')) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t$list.addClass('loaded');\n\t\t\t$.ajax(OC.generateUrl('\/contactsmenu\/findOne'), {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tdata: {\n\t\t\t\t\tshareType: shareType,\n\t\t\t\t\tshareWith: shareWith\n\t\t\t\t}\n\t\t\t}).then(function(data) {\n\t\t\t\t$list.find('ul').find('li').addClass('hidden');\n\n\t\t\t\tvar actions;\n\t\t\t\tif (!data.topAction) {\n\t\t\t\t\tactions = [{\n\t\t\t\t\t\thyperlink: '#',\n\t\t\t\t\t\ttitle: t('core', 'No action available')\n\t\t\t\t\t}];\n\t\t\t\t} else {\n\t\t\t\t\tactions = [data.topAction].concat(data.actions);\n\t\t\t\t}\n\n\t\t\t\tactions.forEach(function(action) {\n\t\t\t\t\tvar template = OC.ContactsMenu.Templates['jquery_entry'];\n\t\t\t\t\t$list.find('ul').append(template(action));\n\t\t\t\t});\n\n\t\t\t\tif (actions.length === 0) {\n\n\t\t\t\t}\n\t\t\t}, function(jqXHR) {\n\t\t\t\t$list.find('ul').find('li').addClass('hidden');\n\n\t\t\t\tvar title;\n\t\t\t\tif (jqXHR.status === 404) {\n\t\t\t\t\ttitle = t('core', 'No action available');\n\t\t\t\t} else {\n\t\t\t\t\ttitle = t('core', 'Error fetching contact actions');\n\t\t\t\t}\n\n\t\t\t\tvar template = OC.ContactsMenu.Templates['jquery_entry'];\n\t\t\t\t$list.find('ul').append(template({\n\t\t\t\t\thyperlink: '#',\n\t\t\t\t\ttitle: title\n\t\t\t\t}));\n\t\t\t});\n\t\t});\n\n\t\t$(document).click(function(event) {\n\t\t\tvar clickedList = ($list.has(event.target).length > 0);\n\t\t\tvar clickedTarget = ($div.has(event.target).length > 0);\n\n\t\t\t$div.each(function() {\n\t\t\t\tif ($(this).is(event.target)) {\n\t\t\t\t\tclickedTarget = true;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (clickedList || clickedTarget) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t$list.addClass('hidden');\n\t\t\t$list.hide();\n\t\t});\n\t};\n}(jQuery));\n\n\n"]},{"file":"\/var\/www\/nextcloud\/lib\/private\/Template\/JSCombiner.php","line":103,"function":"cache","class":"OC\\Template\\JSCombiner","type":"->","args":["\/var\/www\/nextcloud\/core\/js","merged-template-prepend.js",{"__class__":"OC\\Files\\SimpleFS\\SimpleFolder"}]},{"file":"\/var\/www\/nextcloud\/lib\/private\/Template\/JSResourceLocator.php","line":114,"function":"process","class":"OC\\Template\\JSCombiner","type":"->","args":["\/var\/www\/nextcloud","core\/js\/merged-template-prepend.json","core"]},{"file":"\/var\/www\/nextcloud\/lib\/private\/Template\/JSResourceLocator.php","line":70,"function":"cacheAndAppendCombineJsonIfExist","class":"OC\\Template\\JSResourceLocator","type":"->","args":["\/var\/www\/nextcloud","core\/js\/merged-template-prepend.json"]},{"file":"\/var\/www\/nextcloud\/lib\/private\/Template\/ResourceLocator.php","line":78,"function":"doFind","class":"OC\\Template\\JSResourceLocator","type":"->","args":["js\/merged-template-prepend"]},{"file":"\/var\/www\/nextcloud\/lib\/private\/TemplateLayout.php","line":328,"function":"find","class":"OC\\Template\\ResourceLocator","type":"->","args":[["vendor\/core","js\/merged-template-prepend","search\/js\/search","core\/l10n\/de_DE","search\/l10n\/de_DE","search\/js\/searchprovider","js\/jquery-ui-fixes","js\/files\/fileinfo","js\/files\/client","js\/contactsmenu","js\/contactsmenu_templates"]]},{"file":"\/var\/www\/nextcloud\/lib\/private\/TemplateLayout.php","line":160,"function":"findJavascriptFiles","class":"OC\\TemplateLayout","type":"::","args":[["vendor\/core","js\/merged-template-prepend","search\/js\/search","core\/l10n\/de_DE","search\/l10n\/de_DE","search\/js\/searchprovider","js\/jquery-ui-fixes","js\/files\/fileinfo","js\/files\/client","js\/contactsmenu","js\/contactsmenu_templates"]]},{"file":"\/var\/www\/nextcloud\/lib\/private\/legacy\/template.php","line":211,"function":"__construct","class":"OC\\TemplateLayout","type":"->","args":["guest",""]},{"file":"\/var\/www\/nextcloud\/lib\/private\/Template\/Base.php","line":132,"function":"fetchPage","class":"OC_Template","type":"->","args":[]},{"file":"\/var\/www\/nextcloud\/lib\/private\/legacy\/template.php","line":301,"function":"printPage","class":"OC\\Template\\Base","type":"->","args":[]},{"file":"\/var\/www\/nextcloud\/lib\/base.php","line":690,"function":"printGuestPage","class":"OC_Template","type":"::","args":["","error",{"errors":[{"error":"Dein Datenverzeichnis ist ung\u00fcltig.","hint":"Stellen Sie sicher, dass eine Datei \".ocdata\" im Wurzelverzeichnis des data-Verzeichnisses existiert."}]}]},{"file":"\/var\/www\/nextcloud\/lib\/base.php","line":1068,"function":"init","class":"OC","type":"::","args":[]},{"file":"\/var\/www\/nextcloud\/status.php","line":36,"args":["\/var\/www\/nextcloud\/lib\/base.php"],"function":"require_once"}],"File":"\/var\/www\/nextcloud\/lib\/private\/Files\/Node\/File.php","Line":67,"CustomMessage":"--"},"userAgent":"Mozilla\/5.0 (Windows) mirall\/2.6.2stable-Win64 (build 20191224) (Nextcloud)","version":"15.0.5.3"}
  128. {"reqId":"TtRuEnKOay3mdELO1z1M","level":2,"time":"2020-01-10T14:57:53+00:00","remoteAddr":"","user":"--","app":"no app in context","method":"","url":"--","message":"Temporary directory \/media\/USBdrive\/ncdata\/tmp is not present or writable","userAgent":"--","version":"15.0.5.3"}
  129. {"reqId":"TtRuEnKOay3mdELO1z1M","level":3,"time":"2020-01-10T14:57:54+00:00","remoteAddr":"","user":"--","app":"PHP","method":"","url":"--","message":"file_get_contents(\/media\/USBdrive\/ncdata\/news\/config\/config.ini): failed to open stream: No such file or directory at \/var\/www\/nextcloud\/lib\/private\/Files\/Storage\/Local.php#216","userAgent":"--","version":"15.0.5.3"}
  130. {"reqId":"TtRuEnKOay3mdELO1z1M","level":2,"time":"2020-01-10T14:57:54+00:00","remoteAddr":"","user":"--","app":"news","method":"","url":"--","message":"Configuration invalid. Ignoring values.","userAgent":"--","version":"15.0.5.3"}
  131. {"reqId":"7hTnWrQ84GRhbsjPr3oB","level":2,"time":"2020-01-10T14:57:55+00:00","remoteAddr":"","user":"--","app":"no app in context","method":"","url":"--","message":"Temporary directory \/media\/USBdrive\/ncdata\/tmp is not present or writable","userAgent":"--","version":"15.0.5.3"}
  132. {"reqId":"7hTnWrQ84GRhbsjPr3oB","level":3,"time":"2020-01-10T14:57:56+00:00","remoteAddr":"","user":"--","app":"PHP","method":"","url":"--","message":"file_get_contents(\/media\/USBdrive\/ncdata\/news\/config\/config.ini): failed to open stream: No such file or directory at \/var\/www\/nextcloud\/lib\/private\/Files\/Storage\/Local.php#216","userAgent":"--","version":"15.0.5.3"}
  133. {"reqId":"7hTnWrQ84GRhbsjPr3oB","level":2,"time":"2020-01-10T14:57:56+00:00","remoteAddr":"","user":"--","app":"news","method":"","url":"--","message":"Configuration invalid. Ignoring values.","userAgent":"--","version":"15.0.5.3"}
  134. {"reqId":"0QHjITfAh9DkXlNfjH58","level":2,"time":"2020-01-10T14:57:57+00:00","remoteAddr":"","user":"--","app":"no app in context","method":"","url":"--","message":"Temporary directory \/media\/USBdrive\/ncdata\/tmp is not present or writable","userAgent":"--","version":"15.0.5.3"}
  135. {"reqId":"0QHjITfAh9DkXlNfjH58","level":3,"time":"2020-01-10T14:57:58+00:00","remoteAddr":"","user":"--","app":"PHP","method":"","url":"--","message":"file_get_contents(\/media\/USBdrive\/ncdata\/news\/config\/config.ini): failed to open stream: No such file or directory at \/var\/www\/nextcloud\/lib\/private\/Files\/Storage\/Local.php#216","userAgent":"--","version":"15.0.5.3"}
  136. {"reqId":"0QHjITfAh9DkXlNfjH58","level":2,"time":"2020-01-10T14:57:58+00:00","remoteAddr":"","user":"--","app":"news","method":"","url":"--","message":"Configuration invalid. Ignoring values.","userAgent":"--","version":"15.0.5.3"}
  137. {"reqId":"Sz0r5wfbNrbNGoIVY7ZM","level":2,"time":"2020-01-10T14:58:03+00:00","remoteAddr":"","user":"--","app":"no app in context","method":"","url":"--","message":"Temporary directory \/media\/USBdrive\/ncdata\/tmp is not present or writable","userAgent":"--","version":"15.0.5.3"}
  138. {"reqId":"IAsKukvctlcKlGMjvJiJ","level":2,"time":"2020-01-10T14:58:03+00:00","remoteAddr":"","user":"--","app":"no app in context","method":"","url":"--","message":"Temporary directory \/media\/USBdrive\/ncdata\/tmp is not present or writable","userAgent":"--","version":"15.0.5.3"}
  139. {"reqId":"Sz0r5wfbNrbNGoIVY7ZM","level":3,"time":"2020-01-10T14:58:04+00:00","remoteAddr":"","user":"--","app":"PHP","method":"","url":"--","message":"file_get_contents(\/media\/USBdrive\/ncdata\/news\/config\/config.ini): failed to open stream: No such file or directory at \/var\/www\/nextcloud\/lib\/private\/Files\/Storage\/Local.php#216","userAgent":"--","version":"15.0.5.3"}
  140. {"reqId":"Sz0r5wfbNrbNGoIVY7ZM","level":2,"time":"2020-01-10T14:58:04+00:00","remoteAddr":"","user":"--","app":"news","method":"","url":"--","message":"Configuration invalid. Ignoring values.","userAgent":"--","version":"15.0.5.3"}
  141. {"reqId":"IAsKukvctlcKlGMjvJiJ","level":3,"time":"2020-01-10T14:58:04+00:00","remoteAddr":"","user":"--","app":"PHP","method":"","url":"--","message":"file_get_contents(\/media\/USBdrive\/ncdata\/news\/config\/config.ini): failed to open stream: No such file or directory at \/var\/www\/nextcloud\/lib\/private\/Files\/Storage\/Local.php#216","userAgent":"--","version":"15.0.5.3"}
  142. {"reqId":"IAsKukvctlcKlGMjvJiJ","level":2,"time":"2020-01-10T14:58:04+00:00","remoteAddr":"","user":"--","app":"news","method":"","url":"--","message":"Configuration invalid. Ignoring values.","userAgent":"--","version":"15.0.5.3"}
  143.  
  144. ```
  145. </details>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement