OTRS API Reference JavaScript

Source: Core.UI.InputFields.js

  1. // --
  2. // Copyright (C) 2001-2020 OTRS AG, https://otrs.com/
  3. // --
  4. // This software comes with ABSOLUTELY NO WARRANTY. For details, see
  5. // the enclosed file COPYING for license information (GPL). If you
  6. // did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
  7. // --
  8. "use strict";
  9. var Core = Core || {};
  10. Core.UI = Core.UI || {};
  11. /**
  12. * @namespace Core.UI.InputFields
  13. * @memberof Core.UI
  14. * @author OTRS AG
  15. * @description
  16. * Unified input fields.
  17. */
  18. Core.UI.InputFields = (function (TargetNS) {
  19. /**
  20. * @private
  21. * @name Config
  22. * @memberof Core.UI.InputFields
  23. * @member {Object}
  24. * @description
  25. * Configuration object.
  26. */
  27. var Config = {
  28. InputFieldPadding: 3,
  29. SelectionBoxOffsetLeft: 5,
  30. SelectionBoxOffsetRight: 5,
  31. ErrorClass: 'Error',
  32. ServerErrorClass: 'ServerError',
  33. FadeDuration: 150,
  34. ResizeEvent: 'onorientationchange' in window ? 'orientationchange' : 'resize',
  35. ResizeTimeout: 0,
  36. SafeMargin: 30,
  37. MaxNumberOfOptions: 1000,
  38. MinQueryLength: 4,
  39. Diacritics: {
  40. "\u24B6":"A", "\uFF21":"A", "\u00C0":"A", "\u00C1":"A", "\u00C2":"A", "\u1EA6":"A",
  41. "\u1EA4":"A", "\u1EAA":"A", "\u1EA8":"A", "\u00C3":"A", "\u0100":"A", "\u0102":"A",
  42. "\u1EB0":"A", "\u1EAE":"A", "\u1EB4":"A", "\u1EB2":"A", "\u0226":"A", "\u01E0":"A",
  43. "\u00C4":"A", "\u01DE":"A", "\u1EA2":"A", "\u00C5":"A", "\u01FA":"A", "\u01CD":"A",
  44. "\u0200":"A", "\u0202":"A", "\u1EA0":"A", "\u1EAC":"A", "\u1EB6":"A", "\u1E00":"A",
  45. "\u0104":"A", "\u023A":"A", "\u2C6F":"A", "\uA732":"AA", "\u00C6":"AE", "\u01FC":"AE",
  46. "\u01E2":"AE", "\uA734":"AO", "\uA736":"AU", "\uA738":"AV", "\uA73A":"AV", "\uA73C":"AY",
  47. "\u24B7":"B", "\uFF22":"B", "\u1E02":"B", "\u1E04":"B", "\u1E06":"B", "\u0243":"B",
  48. "\u0182":"B", "\u0181":"B", "\u24B8":"C", "\uFF23":"C", "\u0106":"C", "\u0108":"C",
  49. "\u010A":"C", "\u010C":"C", "\u00C7":"C", "\u1E08":"C", "\u0187":"C", "\u023B":"C",
  50. "\uA73E":"C", "\u24B9":"D", "\uFF24":"D", "\u1E0A":"D", "\u010E":"D", "\u1E0C":"D",
  51. "\u1E10":"D", "\u1E12":"D", "\u1E0E":"D", "\u0110":"D", "\u018B":"D", "\u018A":"D",
  52. "\u0189":"D", "\uA779":"D", "\u01F1":"DZ", "\u01C4":"DZ", "\u01F2":"Dz", "\u01C5":"Dz",
  53. "\u24BA":"E", "\uFF25":"E", "\u00C8":"E", "\u00C9":"E", "\u00CA":"E", "\u1EC0":"E",
  54. "\u1EBE":"E", "\u1EC4":"E", "\u1EC2":"E", "\u1EBC":"E", "\u0112":"E", "\u1E14":"E",
  55. "\u1E16":"E", "\u0114":"E", "\u0116":"E", "\u00CB":"E", "\u1EBA":"E", "\u011A":"E",
  56. "\u0204":"E", "\u0206":"E", "\u1EB8":"E", "\u1EC6":"E", "\u0228":"E", "\u1E1C":"E",
  57. "\u0118":"E", "\u1E18":"E", "\u1E1A":"E", "\u0190":"E", "\u018E":"E", "\u24BB":"F",
  58. "\uFF26":"F", "\u1E1E":"F", "\u0191":"F", "\uA77B":"F", "\u24BC":"G", "\uFF27":"G",
  59. "\u01F4":"G", "\u011C":"G", "\u1E20":"G", "\u011E":"G", "\u0120":"G", "\u01E6":"G",
  60. "\u0122":"G", "\u01E4":"G", "\u0193":"G", "\uA7A0":"G", "\uA77D":"G", "\uA77E":"G",
  61. "\u24BD":"H", "\uFF28":"H", "\u0124":"H", "\u1E22":"H", "\u1E26":"H", "\u021E":"H",
  62. "\u1E24":"H", "\u1E28":"H", "\u1E2A":"H", "\u0126":"H", "\u2C67":"H", "\u2C75":"H",
  63. "\uA78D":"H", "\u24BE":"I", "\uFF29":"I", "\u00CC":"I", "\u00CD":"I", "\u00CE":"I",
  64. "\u0128":"I", "\u012A":"I", "\u012C":"I", "\u0130":"I", "\u00CF":"I", "\u1E2E":"I",
  65. "\u1EC8":"I", "\u01CF":"I", "\u0208":"I", "\u020A":"I", "\u1ECA":"I", "\u012E":"I",
  66. "\u1E2C":"I", "\u0197":"I", "\u24BF":"J", "\uFF2A":"J", "\u0134":"J", "\u0248":"J",
  67. "\u24C0":"K", "\uFF2B":"K", "\u1E30":"K", "\u01E8":"K", "\u1E32":"K", "\u0136":"K",
  68. "\u1E34":"K", "\u0198":"K", "\u2C69":"K", "\uA740":"K", "\uA742":"K", "\uA744":"K",
  69. "\uA7A2":"K", "\u24C1":"L", "\uFF2C":"L", "\u013F":"L", "\u0139":"L", "\u013D":"L",
  70. "\u1E36":"L", "\u1E38":"L", "\u013B":"L", "\u1E3C":"L", "\u1E3A":"L", "\u0141":"L",
  71. "\u023D":"L", "\u2C62":"L", "\u2C60":"L", "\uA748":"L", "\uA746":"L", "\uA780":"L",
  72. "\u01C7":"LJ", "\u01C8":"Lj", "\u24C2":"M", "\uFF2D":"M", "\u1E3E":"M", "\u1E40":"M",
  73. "\u1E42":"M", "\u2C6E":"M", "\u019C":"M", "\u24C3":"N", "\uFF2E":"N", "\u01F8":"N",
  74. "\u0143":"N", "\u00D1":"N", "\u1E44":"N", "\u0147":"N", "\u1E46":"N", "\u0145":"N",
  75. "\u1E4A":"N", "\u1E48":"N", "\u0220":"N", "\u019D":"N", "\uA790":"N", "\uA7A4":"N",
  76. "\u01CA":"NJ", "\u01CB":"Nj", "\u24C4":"O", "\uFF2F":"O", "\u00D2":"O", "\u00D3":"O",
  77. "\u00D4":"O", "\u1ED2":"O", "\u1ED0":"O", "\u1ED6":"O", "\u1ED4":"O", "\u00D5":"O",
  78. "\u1E4C":"O", "\u022C":"O", "\u1E4E":"O", "\u014C":"O", "\u1E50":"O", "\u1E52":"O",
  79. "\u014E":"O", "\u022E":"O", "\u0230":"O", "\u00D6":"O", "\u022A":"O", "\u1ECE":"O",
  80. "\u0150":"O", "\u01D1":"O", "\u020C":"O", "\u020E":"O", "\u01A0":"O", "\u1EDC":"O",
  81. "\u1EDA":"O", "\u1EE0":"O", "\u1EDE":"O", "\u1EE2":"O", "\u1ECC":"O", "\u1ED8":"O",
  82. "\u01EA":"O", "\u01EC":"O", "\u00D8":"O", "\u01FE":"O", "\u0186":"O", "\u019F":"O",
  83. "\uA74A":"O", "\uA74C":"O", "\u0152":"OE", "\u01A2":"OI", "\uA74E":"OO", "\u0222":"OU",
  84. "\u24C5":"P", "\uFF30":"P", "\u1E54":"P", "\u1E56":"P", "\u01A4":"P", "\u2C63":"P",
  85. "\uA750":"P", "\uA752":"P", "\uA754":"P", "\u24C6":"Q", "\uFF31":"Q", "\uA756":"Q",
  86. "\uA758":"Q", "\u024A":"Q", "\u24C7":"R", "\uFF32":"R", "\u0154":"R", "\u1E58":"R",
  87. "\u0158":"R", "\u0210":"R", "\u0212":"R", "\u1E5A":"R", "\u1E5C":"R", "\u0156":"R",
  88. "\u1E5E":"R", "\u024C":"R", "\u2C64":"R", "\uA75A":"R", "\uA7A6":"R", "\uA782":"R",
  89. "\u24C8":"S", "\uFF33":"S", "\u015A":"S", "\u1E64":"S", "\u015C":"S", "\u1E60":"S",
  90. "\u0160":"S", "\u1E66":"S", "\u1E62":"S", "\u1E68":"S", "\u0218":"S", "\u015E":"S",
  91. "\u2C7E":"S", "\uA7A8":"S", "\uA784":"S", "\u1E9E":"SS", "\u24C9":"T", "\uFF34":"T",
  92. "\u1E6A":"T", "\u0164":"T", "\u1E6C":"T", "\u021A":"T", "\u0162":"T", "\u1E70":"T",
  93. "\u1E6E":"T", "\u0166":"T", "\u01AC":"T", "\u01AE":"T", "\u023E":"T", "\uA786":"T",
  94. "\uA728":"TZ", "\u24CA":"U", "\uFF35":"U", "\u00D9":"U", "\u00DA":"U", "\u00DB":"U",
  95. "\u0168":"U", "\u1E78":"U", "\u016A":"U", "\u1E7A":"U", "\u016C":"U", "\u00DC":"U",
  96. "\u01DB":"U", "\u01D7":"U", "\u01D5":"U", "\u01D9":"U", "\u1EE6":"U", "\u016E":"U",
  97. "\u0170":"U", "\u01D3":"U", "\u0214":"U", "\u0216":"U", "\u01AF":"U", "\u1EEA":"U",
  98. "\u1EE8":"U", "\u1EEE":"U", "\u1EEC":"U", "\u1EF0":"U", "\u1EE4":"U", "\u1E72":"U",
  99. "\u0172":"U", "\u1E76":"U", "\u1E74":"U", "\u0244":"U", "\u24CB":"V", "\uFF36":"V",
  100. "\u1E7C":"V", "\u1E7E":"V", "\u01B2":"V", "\uA75E":"V", "\u0245":"V", "\uA760":"VY",
  101. "\u24CC":"W", "\uFF37":"W", "\u1E80":"W", "\u1E82":"W", "\u0174":"W", "\u1E86":"W",
  102. "\u1E84":"W", "\u1E88":"W", "\u2C72":"W", "\u24CD":"X", "\uFF38":"X", "\u1E8A":"X",
  103. "\u1E8C":"X", "\u24CE":"Y", "\uFF39":"Y", "\u1EF2":"Y", "\u00DD":"Y", "\u0176":"Y",
  104. "\u1EF8":"Y", "\u0232":"Y", "\u1E8E":"Y", "\u0178":"Y", "\u1EF6":"Y", "\u1EF4":"Y",
  105. "\u01B3":"Y", "\u024E":"Y", "\u1EFE":"Y", "\u24CF":"Z", "\uFF3A":"Z", "\u0179":"Z",
  106. "\u1E90":"Z", "\u017B":"Z", "\u017D":"Z", "\u1E92":"Z", "\u1E94":"Z", "\u01B5":"Z",
  107. "\u0224":"Z", "\u2C7F":"Z", "\u2C6B":"Z", "\uA762":"Z", "\u24D0":"a", "\uFF41":"a",
  108. "\u1E9A":"a", "\u00E0":"a", "\u00E1":"a", "\u00E2":"a", "\u1EA7":"a", "\u1EA5":"a",
  109. "\u1EAB":"a", "\u1EA9":"a", "\u00E3":"a", "\u0101":"a", "\u0103":"a", "\u1EB1":"a",
  110. "\u1EAF":"a", "\u1EB5":"a", "\u1EB3":"a", "\u0227":"a", "\u01E1":"a", "\u00E4":"a",
  111. "\u01DF":"a", "\u1EA3":"a", "\u00E5":"a", "\u01FB":"a", "\u01CE":"a", "\u0201":"a",
  112. "\u0203":"a", "\u1EA1":"a", "\u1EAD":"a", "\u1EB7":"a", "\u1E01":"a", "\u0105":"a",
  113. "\u2C65":"a", "\u0250":"a", "\uA733":"aa", "\u00E6":"ae", "\u01FD":"ae", "\u01E3":"ae",
  114. "\uA735":"ao", "\uA737":"au", "\uA739":"av", "\uA73B":"av", "\uA73D":"ay", "\u24D1":"b",
  115. "\uFF42":"b", "\u1E03":"b", "\u1E05":"b", "\u1E07":"b", "\u0180":"b", "\u0183":"b",
  116. "\u0253":"b", "\u24D2":"c", "\uFF43":"c", "\u0107":"c", "\u0109":"c", "\u010B":"c",
  117. "\u010D":"c", "\u00E7":"c", "\u1E09":"c", "\u0188":"c", "\u023C":"c", "\uA73F":"c",
  118. "\u2184":"c", "\u24D3":"d", "\uFF44":"d", "\u1E0B":"d", "\u010F":"d", "\u1E0D":"d",
  119. "\u1E11":"d", "\u1E13":"d", "\u1E0F":"d", "\u0111":"d", "\u018C":"d", "\u0256":"d",
  120. "\u0257":"d", "\uA77A":"d", "\u01F3":"dz", "\u01C6":"dz", "\u24D4":"e", "\uFF45":"e",
  121. "\u00E8":"e", "\u00E9":"e", "\u00EA":"e", "\u1EC1":"e", "\u1EBF":"e", "\u1EC5":"e",
  122. "\u1EC3":"e", "\u1EBD":"e", "\u0113":"e", "\u1E15":"e", "\u1E17":"e", "\u0115":"e",
  123. "\u0117":"e", "\u00EB":"e", "\u1EBB":"e", "\u011B":"e", "\u0205":"e", "\u0207":"e",
  124. "\u1EB9":"e", "\u1EC7":"e", "\u0229":"e", "\u1E1D":"e", "\u0119":"e", "\u1E19":"e",
  125. "\u1E1B":"e", "\u0247":"e", "\u025B":"e", "\u01DD":"e", "\u24D5":"f", "\uFF46":"f",
  126. "\u1E1F":"f", "\u0192":"f", "\uA77C":"f", "\u24D6":"g", "\uFF47":"g", "\u01F5":"g",
  127. "\u011D":"g", "\u1E21":"g", "\u011F":"g", "\u0121":"g", "\u01E7":"g", "\u0123":"g",
  128. "\u01E5":"g", "\u0260":"g", "\uA7A1":"g", "\u1D79":"g", "\uA77F":"g", "\u24D7":"h",
  129. "\uFF48":"h", "\u0125":"h", "\u1E23":"h", "\u1E27":"h", "\u021F":"h", "\u1E25":"h",
  130. "\u1E29":"h", "\u1E2B":"h", "\u1E96":"h", "\u0127":"h", "\u2C68":"h", "\u2C76":"h",
  131. "\u0265":"h", "\u0195":"hv", "\u24D8":"i", "\uFF49":"i", "\u00EC":"i", "\u00ED":"i",
  132. "\u00EE":"i", "\u0129":"i", "\u012B":"i", "\u012D":"i", "\u00EF":"i", "\u1E2F":"i",
  133. "\u1EC9":"i", "\u01D0":"i", "\u0209":"i", "\u020B":"i", "\u1ECB":"i", "\u012F":"i",
  134. "\u1E2D":"i", "\u0268":"i", "\u0131":"i", "\u24D9":"j", "\uFF4A":"j", "\u0135":"j",
  135. "\u01F0":"j", "\u0249":"j", "\u24DA":"k", "\uFF4B":"k", "\u1E31":"k", "\u01E9":"k",
  136. "\u1E33":"k", "\u0137":"k", "\u1E35":"k", "\u0199":"k", "\u2C6A":"k", "\uA741":"k",
  137. "\uA743":"k", "\uA745":"k", "\uA7A3":"k", "\u24DB":"l", "\uFF4C":"l", "\u0140":"l",
  138. "\u013A":"l", "\u013E":"l", "\u1E37":"l", "\u1E39":"l", "\u013C":"l", "\u1E3D":"l",
  139. "\u1E3B":"l", "\u0142":"l", "\u019A":"l", "\u026B":"l", "\u2C61":"l", "\uA749":"l",
  140. "\uA781":"l", "\uA747":"l", "\u01C9":"lj", "\u24DC":"m", "\uFF4D":"m", "\u1E3F":"m",
  141. "\u1E41":"m", "\u1E43":"m", "\u0271":"m", "\u026F":"m", "\u24DD":"n", "\uFF4E":"n",
  142. "\u01F9":"n", "\u0144":"n", "\u00F1":"n", "\u1E45":"n", "\u0148":"n", "\u1E47":"n",
  143. "\u0146":"n", "\u1E4B":"n", "\u1E49":"n", "\u019E":"n", "\u0272":"n", "\u0149":"n",
  144. "\uA791":"n", "\uA7A5":"n", "\u01CC":"nj", "\u24DE":"o", "\uFF4F":"o", "\u00F2":"o",
  145. "\u00F3":"o", "\u00F4":"o", "\u1ED3":"o", "\u1ED1":"o", "\u1ED7":"o", "\u1ED5":"o",
  146. "\u00F5":"o", "\u1E4D":"o", "\u022D":"o", "\u1E4F":"o", "\u014D":"o", "\u1E51":"o",
  147. "\u1E53":"o", "\u014F":"o", "\u022F":"o", "\u0231":"o", "\u00F6":"o", "\u022B":"o",
  148. "\u1ECF":"o", "\u0151":"o", "\u01D2":"o", "\u020D":"o", "\u020F":"o", "\u01A1":"o",
  149. "\u1EDD":"o", "\u1EDB":"o", "\u1EE1":"o", "\u1EDF":"o", "\u1EE3":"o", "\u1ECD":"o",
  150. "\u1ED9":"o", "\u01EB":"o", "\u01ED":"o", "\u00F8":"o", "\u01FF":"o", "\u0254":"o",
  151. "\uA74B":"o", "\uA74D":"o", "\u0275":"o", "\u0153":"oe", "\u0276":"oe", "\u01A3":"oi",
  152. "\u0223":"ou", "\uA74F":"oo", "\u24DF":"p", "\uFF50":"p", "\u1E55":"p", "\u1E57":"p",
  153. "\u01A5":"p", "\u1D7D":"p", "\uA751":"p", "\uA753":"p", "\uA755":"p", "\u24E0":"q",
  154. "\uFF51":"q", "\u024B":"q", "\uA757":"q", "\uA759":"q", "\u24E1":"r", "\uFF52":"r",
  155. "\u0155":"r", "\u1E59":"r", "\u0159":"r", "\u0211":"r", "\u0213":"r", "\u1E5B":"r",
  156. "\u1E5D":"r", "\u0157":"r", "\u1E5F":"r", "\u024D":"r", "\u027D":"r", "\uA75B":"r",
  157. "\uA7A7":"r", "\uA783":"r", "\u24E2":"s", "\uFF53":"s", "\u015B":"s", "\u1E65":"s",
  158. "\u015D":"s", "\u1E61":"s", "\u0161":"s", "\u1E67":"s", "\u1E63":"s", "\u1E69":"s",
  159. "\u0219":"s", "\u015F":"s", "\u023F":"s", "\uA7A9":"s", "\uA785":"s", "\u017F":"s",
  160. "\u1E9B":"s", "\u00DF":"ss", "\u24E3":"t", "\uFF54":"t", "\u1E6B":"t", "\u1E97":"t",
  161. "\u0165":"t", "\u1E6D":"t", "\u021B":"t", "\u0163":"t", "\u1E71":"t", "\u1E6F":"t",
  162. "\u0167":"t", "\u01AD":"t", "\u0288":"t", "\u2C66":"t", "\uA787":"t", "\uA729":"tz",
  163. "\u24E4":"u", "\uFF55":"u", "\u00F9":"u", "\u00FA":"u", "\u00FB":"u", "\u0169":"u",
  164. "\u1E79":"u", "\u016B":"u", "\u1E7B":"u", "\u016D":"u", "\u00FC":"u", "\u01DC":"u",
  165. "\u01D8":"u", "\u01D6":"u", "\u01DA":"u", "\u1EE7":"u", "\u016F":"u", "\u0171":"u",
  166. "\u01D4":"u", "\u0215":"u", "\u0217":"u", "\u01B0":"u", "\u1EEB":"u", "\u1EE9":"u",
  167. "\u1EEF":"u", "\u1EED":"u", "\u1EF1":"u", "\u1EE5":"u", "\u1E73":"u", "\u0173":"u",
  168. "\u1E77":"u", "\u1E75":"u", "\u0289":"u", "\u24E5":"v", "\uFF56":"v", "\u1E7D":"v",
  169. "\u1E7F":"v", "\u028B":"v", "\uA75F":"v", "\u028C":"v", "\uA761":"vy", "\u24E6":"w",
  170. "\uFF57":"w", "\u1E81":"w", "\u1E83":"w", "\u0175":"w", "\u1E87":"w", "\u1E85":"w",
  171. "\u1E98":"w", "\u1E89":"w", "\u2C73":"w", "\u24E7":"x", "\uFF58":"x", "\u1E8B":"x",
  172. "\u1E8D":"x", "\u24E8":"y", "\uFF59":"y", "\u1EF3":"y", "\u00FD":"y", "\u0177":"y",
  173. "\u1EF9":"y", "\u0233":"y", "\u1E8F":"y", "\u00FF":"y", "\u1EF7":"y", "\u1E99":"y",
  174. "\u1EF5":"y", "\u01B4":"y", "\u024F":"y", "\u1EFF":"y", "\u24E9":"z", "\uFF5A":"z",
  175. "\u017A":"z", "\u1E91":"z", "\u017C":"z", "\u017E":"z", "\u1E93":"z", "\u1E95":"z",
  176. "\u01B6":"z", "\u0225":"z", "\u0240":"z", "\u2C6C":"z", "\uA763":"z", "\uFF10":"0",
  177. "\u2080":"0", "\u24EA":"0", "\u2070":"0", "\u00B9":"1", "\u2474":"1", "\u2081":"1",
  178. "\u2776":"1", "\u24F5":"1", "\u2488":"1", "\u2460":"1", "\uFF11":"1", "\u00B2":"2",
  179. "\u2777":"2", "\u2475":"2", "\uFF12":"2", "\u2082":"2", "\u24F6":"2", "\u2461":"2",
  180. "\u2489":"2", "\u00B3":"3", "\uFF13":"3", "\u248A":"3", "\u2476":"3", "\u2083":"3",
  181. "\u2778":"3", "\u24F7":"3", "\u2462":"3", "\u24F8":"4", "\u2463":"4", "\u248B":"4",
  182. "\uFF14":"4", "\u2074":"4", "\u2084":"4", "\u2779":"4", "\u2477":"4", "\u248C":"5",
  183. "\u2085":"5", "\u24F9":"5", "\u2478":"5", "\u277A":"5", "\u2464":"5", "\uFF15":"5",
  184. "\u2075":"5", "\u2479":"6", "\u2076":"6", "\uFF16":"6", "\u277B":"6", "\u2086":"6",
  185. "\u2465":"6", "\u24FA":"6", "\u248D":"6", "\uFF17":"7", "\u2077":"7", "\u277C":"7",
  186. "\u24FB":"7", "\u248E":"7", "\u2087":"7", "\u247A":"7", "\u2466":"7", "\u2467":"8",
  187. "\u248F":"8", "\u24FC":"8", "\u247B":"8", "\u2078":"8", "\uFF18":"8", "\u277D":"8",
  188. "\u2088":"8", "\u24FD":"9", "\uFF19":"9", "\u2490":"9", "\u277E":"9", "\u247C":"9",
  189. "\u2089":"9", "\u2468":"9", "\u2079":"9"
  190. }
  191. };
  192. /**
  193. * @name Activate
  194. * @memberof Core.UI.InputFields
  195. * @param {jQueryObject} [$Context] - jQuery object for context (optional)
  196. * @description
  197. * Activate the feature on all applicable fields in supplied context.
  198. */
  199. TargetNS.Activate = function ($Context) {
  200. // Check SysConfig
  201. if (parseInt(Core.Config.Get('InputFieldsActivated'), 10) === 1) {
  202. // Initialize select fields on all applicable fields
  203. TargetNS.InitSelect($('select.Modernize', $Context));
  204. }
  205. };
  206. /**
  207. * @name Deactivate
  208. * @memberof Core.UI.InputFields
  209. * @param {jQueryObject} [$Context] - jQuery object for context (optional)
  210. * @description
  211. * Deactivate the feature on all applicable fields in supplied context
  212. * and restore original fields.
  213. */
  214. TargetNS.Deactivate = function ($Context) {
  215. // Restore select fields
  216. $('select.Modernize', $Context).each(function (Index, SelectObj) {
  217. var $SelectObj = $(SelectObj),
  218. $ShowTreeObj = $SelectObj.next('.ShowTreeSelection');
  219. if ($SelectObj.data('modernized')) {
  220. $('#' + Core.App.EscapeSelector($SelectObj.data('modernized'))).parents('.InputField_Container')
  221. .blur()
  222. .remove();
  223. $SelectObj.show()
  224. .removeData('modernized');
  225. $ShowTreeObj.show();
  226. }
  227. });
  228. };
  229. /**
  230. * @private
  231. * @name InitCallback
  232. * @memberof Core.UI.InputFields
  233. * @function
  234. * @description
  235. * Initialization callback function.
  236. */
  237. function InitCallback() {
  238. // Activate the feature
  239. TargetNS.Activate();
  240. }
  241. /**
  242. * @name Init
  243. * @memberof Core.UI.InputFields
  244. * @function
  245. * @description
  246. * This function initializes all input field types.
  247. */
  248. TargetNS.Init = function () {
  249. InitCallback();
  250. Core.App.Subscribe('Event.UI.ToggleWidget', InitCallback);
  251. };
  252. /**
  253. * @private
  254. * @name CheckAvailability
  255. * @memberof Core.UI.InputFields
  256. * @param {jQueryObject} $SelectObj - Original select field
  257. * @param {jQueryObject} $SearchObj - Search input field
  258. * @param {jQueryObject} $InputContainerObj - Input container element
  259. * @description
  260. * Checks if there are available options for selection in the supplied field
  261. * and disabled the field if that is not the case.
  262. */
  263. function CheckAvailability($SelectObj, $SearchObj, $InputContainerObj) {
  264. // Handle form <select> elements that are disabled and elements that were
  265. // disabled with Core.Form.DisableForm();
  266. if ($SelectObj.attr('disabled') || $SearchObj.data('form-disabled')) {
  267. $SearchObj.attr('disabled', 'disabled');
  268. // Make background grey and elements white.
  269. $SearchObj.attr('readonly', 'readonly');
  270. $InputContainerObj.addClass('AlreadyDisabled');
  271. return;
  272. }
  273. $SearchObj.prop('readonly', false);
  274. $InputContainerObj.removeClass('AlreadyDisabled');
  275. // Check if there are only empty and disabled options
  276. if ($SelectObj.find('option')
  277. .not("[value='']")
  278. .not("[value='-||']")
  279. .not('[disabled]')
  280. .length === 0
  281. )
  282. {
  283. // Disable the field, add the tooltip and dash string
  284. $SearchObj
  285. .attr('readonly', 'readonly')
  286. .attr('title', Core.Language.Translate('Not available'));
  287. // when the original field does no longer provide any valid options,
  288. // we also want to remove existing selections
  289. $InputContainerObj.find('.InputField_Selection').remove();
  290. $InputContainerObj.find('.InputField_More').remove();
  291. }
  292. else {
  293. // Enable the field, remove the tooltip and dash string
  294. $SearchObj
  295. .removeAttr('title')
  296. .prop('readonly', false)
  297. .val('');
  298. }
  299. }
  300. /**
  301. * @private
  302. * @name ValidateFormElement
  303. * @memberof Core.UI.InputFields
  304. * @param {jQueryObject} $SelectObj - Select field to validate
  305. * @description
  306. * Trigger supplied select field validation if part of appropriate form.
  307. */
  308. function ValidateFormElement($SelectObj) {
  309. // Get form object
  310. var $FormObj = $SelectObj.closest('form');
  311. // Check if form supports validation
  312. if ($FormObj.hasClass('Validate')) {
  313. Core.Form.Validate.ValidateElement($SelectObj);
  314. }
  315. }
  316. /**
  317. * @private
  318. * @name CloseOpenSelections
  319. * @memberof Core.UI.InputFields
  320. * @param {jQueryObject} [$Context] - jQuery object for context (optional)
  321. * @description
  322. * Triggers blur on all modern input fields in order to close them.
  323. */
  324. function CloseOpenSelections($Context) {
  325. // Step through all select fields on the page or only those in supplied context.
  326. // If the dropdown of the field is opened, close it (by calling blur event)
  327. // TODO: nicer way to find opened select elements
  328. $('select.Modernize', $Context).each(function () {
  329. var Selector = Core.App.EscapeSelector($(this).data('modernized'));
  330. if (!Selector) {
  331. return;
  332. }
  333. $('#' + Selector).filter('[aria-expanded=true]').trigger('blur');
  334. });
  335. }
  336. /**
  337. * @private
  338. * @name ShowSelectionBoxes
  339. * @memberof Core.UI.InputFields
  340. * @param {jQueryObject} $SelectObj - Original select field
  341. * @param {JQueryObject} $InputContainerObj - Container for associated input field
  342. * @description
  343. * Creates and displays selection boxes in available width,
  344. * and lists number of additional selected values.
  345. */
  346. function ShowSelectionBoxes($SelectObj, $InputContainerObj) {
  347. var Selection,
  348. SelectionLength,
  349. $SelectionFilterObj,
  350. i = 0,
  351. OffsetLeft = 0,
  352. OffsetRight = Config.SelectionBoxOffsetRight,
  353. MoreBox = false,
  354. Multiple = ($SelectObj.attr('multiple') !== '' && $SelectObj.attr('multiple') !== undefined) ? true : false,
  355. PossibleNone = false,
  356. MaxWidth,
  357. $SearchObj = $InputContainerObj.find('.InputField_Search'),
  358. $TempMoreObj;
  359. // Give up if field is expanded
  360. if ($SearchObj.attr('aria-expanded')) return;
  361. // Give up is field is disabled
  362. if ($SearchObj.attr('readonly')) return;
  363. // Remove any existing boxes in supplied container
  364. $InputContainerObj.find('.InputField_Selection').remove();
  365. $InputContainerObj.find('.InputField_SelectionFilter').remove();
  366. $InputContainerObj.find('.InputField_More').remove();
  367. // Check for empty values (allow field clearing).
  368. $SelectObj.find('option').each(function (Index, Option) {
  369. if ($(Option).attr('value') === '' || $(Option).attr('value') === '||-') {
  370. PossibleNone = true;
  371. return true;
  372. }
  373. });
  374. // Also allow field clearing if the select has a size attribute > 1. In this case
  375. // most browsers allow unselecting options.
  376. if ($SelectObj.attr('size') && parseInt($SelectObj.attr('size'), 10) > 1) {
  377. PossibleNone = true;
  378. }
  379. // Check if we have a selection at all
  380. if ($SelectObj.val()) {
  381. // Maximum available width for boxes
  382. MaxWidth = $SearchObj.width() - Config.SelectionBoxOffsetRight - Config.InputFieldPadding;
  383. // Check which kind of selection we are dealing with
  384. if ($.isArray($SelectObj.val())) {
  385. Selection = $.unique($SelectObj.val());
  386. SelectionLength = Selection.length;
  387. } else {
  388. Selection = [ $SelectObj.val() ];
  389. SelectionLength = 1;
  390. }
  391. // Calculate width for hypothetical more string
  392. if (SelectionLength > 1) {
  393. $TempMoreObj = $('<div />').hide()
  394. .addClass('InputField_More')
  395. .text(Core.Language.Translate("and %s more...").replace(/%s/, '##'))
  396. .appendTo($InputContainerObj);
  397. // Save place for string
  398. MaxWidth -= $TempMoreObj.outerWidth();
  399. // Remove temporary more string
  400. $TempMoreObj.remove();
  401. }
  402. // Iterate through all selected values
  403. $.each(Selection, function (Index, Value) {
  404. var $SelectionObj,
  405. Text,
  406. $TextObj,
  407. $RemoveObj;
  408. // Skip empty value
  409. if (Value === '' || Value === '||-') {
  410. return true;
  411. }
  412. // Selection box container
  413. $SelectionObj = $('<div />').appendTo($InputContainerObj);
  414. $SelectionObj.addClass('InputField_Selection')
  415. .data('value', Value);
  416. // Textual representation of selected value
  417. Text = $SelectObj.find('option[value="' + Core.App.EscapeSelector(Value) + '"]').first().text().trim();
  418. $TextObj = $('<div />').appendTo($SelectionObj);
  419. $TextObj.addClass('Text')
  420. .text(Text)
  421. .off('click.InputField').on('click.InputField', function () {
  422. $SearchObj.trigger('focus');
  423. });
  424. // Remove button
  425. if (PossibleNone || Multiple) {
  426. $RemoveObj = $('<div />').appendTo($SelectionObj);
  427. $RemoveObj.addClass('Remove')
  428. .append(
  429. $('<a />').attr('href', '#')
  430. .attr('title', Core.Language.Translate('Remove selection'))
  431. .text('x')
  432. .attr('role', 'button')
  433. .attr('tabindex', '-1')
  434. .attr(
  435. 'aria-label',
  436. Core.Language.Translate('Remove selection') + ': ' + Text
  437. )
  438. .off('click.InputField').on('click.InputField', function () {
  439. var HasEmptyElement = $SelectObj.find('option[value=""]').length === 0 ? false : true,
  440. SelectedValue = $(this).parents('.InputField_Selection')
  441. .data('value');
  442. Selection.splice(Selection.indexOf(SelectedValue), 1);
  443. if (HasEmptyElement && Selection.length === 0) {
  444. $SelectObj.val('');
  445. }
  446. else {
  447. $SelectObj.val(Selection);
  448. }
  449. ShowSelectionBoxes($SelectObj, $InputContainerObj);
  450. setTimeout(function () {
  451. $SelectObj.trigger('change');
  452. ValidateFormElement($SelectObj);
  453. }, 0);
  454. if (Multiple) {
  455. RefreshSelectionFilter($SelectObj, null);
  456. }
  457. return false;
  458. })
  459. );
  460. }
  461. // Indent first box from the left
  462. if (OffsetLeft === 0) {
  463. OffsetLeft = Config.SelectionBoxOffsetLeft;
  464. }
  465. // Check if we exceed available width of the container
  466. if (OffsetLeft + $SelectionObj.outerWidth() < MaxWidth) {
  467. // Offset the box and show it
  468. if ($('body').hasClass('RTL')) {
  469. $SelectionObj.css('right', OffsetLeft + 'px')
  470. .show();
  471. }
  472. else {
  473. $SelectionObj.css('left', OffsetLeft + 'px')
  474. .show();
  475. }
  476. } else {
  477. // If first selection, we must shorten it in order to display it
  478. if (i === 0) {
  479. while (MaxWidth > 0 && OffsetLeft + $SelectionObj.outerWidth() >= MaxWidth) {
  480. $TextObj.text(
  481. $TextObj.text().substring(0, $TextObj.text().length - 4)
  482. + '...'
  483. );
  484. }
  485. // Offset the box and show it
  486. if ($('body').hasClass('RTL')) {
  487. $SelectionObj.css('right', OffsetLeft + 'px')
  488. .show();
  489. }
  490. else {
  491. $SelectionObj.css('left', OffsetLeft + 'px')
  492. .show();
  493. }
  494. }
  495. else {
  496. // Check if we already displayed more box
  497. if (!MoreBox) {
  498. $SelectionObj.after(
  499. $('<div />').addClass('InputField_More')
  500. .css(
  501. ($('body').hasClass('RTL') ? 'right' : 'left'),
  502. OffsetLeft + 'px'
  503. )
  504. .text(
  505. Core.Language.Translate("and %s more...").replace(/%s/, SelectionLength - i)
  506. )
  507. .on('click.InputField', function () {
  508. $SearchObj.trigger('focus');
  509. })
  510. );
  511. MoreBox = true;
  512. }
  513. // Remove superfluous box
  514. $SelectionObj.remove();
  515. // Break each loop
  516. return false;
  517. }
  518. }
  519. // Increment the offset with the width of box and right margin
  520. OffsetLeft += $SelectionObj.outerWidth() + OffsetRight;
  521. i++;
  522. });
  523. if (Multiple && MoreBox) {
  524. $SelectionFilterObj = $('<a />').appendTo($InputContainerObj);
  525. $SelectionFilterObj.addClass('InputField_Action InputField_SelectionFilter')
  526. .attr('href', '#')
  527. .attr('title', Core.Language.Translate('Show current selection'))
  528. .css(($('body').hasClass('RTL') ? 'left' : 'right'), Config.SelectionBoxOffsetRight + 'px')
  529. .append($('<i />').addClass('fa fa-eye'))
  530. .attr('role', 'button')
  531. .attr('tabindex', '-1')
  532. .attr('aria-label', Core.Language.Translate('Current selection'))
  533. .off('click.InputField').on('click.InputField', function () {
  534. if ($SelectObj.data('filters') !== '' && $SelectObj.data('filters') !== undefined) {
  535. // See if we already have a selection filter
  536. $.each($SelectObj.data('filters').Filters, function(Index, Filter) {
  537. if (Filter.Name === Core.Language.Translate('Current selection')) {
  538. $SelectObj.data('filtered', Index + 1);
  539. $SelectObj.data('expand-filters', true);
  540. $SelectObj.data('selection-filter', true);
  541. ApplyFilter($SelectObj, null);
  542. // Refresh the field and get focus
  543. $SearchObj = $('#' + Core.App.EscapeSelector($SelectObj.data('modernized')));
  544. $SearchObj.width($SelectObj.outerWidth())
  545. .trigger('blur');
  546. CheckAvailability($SelectObj, $SearchObj, $InputContainerObj);
  547. setTimeout(function () {
  548. $SearchObj.focus();
  549. }, 0);
  550. return true;
  551. }
  552. });
  553. }
  554. return false;
  555. });
  556. }
  557. }
  558. }
  559. /**
  560. * @private
  561. * @name HideSelectList
  562. * @memberof Core.UI.InputFields
  563. * @param {jQueryObject} $SelectObj - Original select field
  564. * @param {JQueryObject} $InputContainerObj - Container for associated input field
  565. * @param {JQueryObject} $SearchObj - Search input field
  566. * @param {JQueryObject} $ListContainerObj - List container
  567. * @param {JQueryObject} $TreeContainerObj - Container for jsTree list
  568. * @description
  569. * Remove complete jsTree list and action buttons.
  570. */
  571. function HideSelectList($SelectObj, $InputContainerObj, $SearchObj, $ListContainerObj, $TreeContainerObj) {
  572. // Remove jsTree if it exists
  573. if ($ListContainerObj && $TreeContainerObj) {
  574. $ListContainerObj.fadeOut(Config.FadeDuration, function () {
  575. $TreeContainerObj.find('.jstree')
  576. .jstree('destroy')
  577. .remove();
  578. $(this).remove();
  579. Core.App.Publish('Event.UI.InputFields.Closed', $SearchObj);
  580. });
  581. $InputContainerObj.find('.InputField_ClearSearch')
  582. .remove();
  583. $SearchObj.removeAttr('aria-expanded');
  584. }
  585. // Clear search field
  586. if (!$SearchObj.attr('readonly')) {
  587. $SearchObj.val('');
  588. }
  589. // Show selection boxes
  590. ShowSelectionBoxes($SelectObj, $InputContainerObj);
  591. // Trigger change event on original field (see bug#11419)
  592. if ($SelectObj.data('changed')) {
  593. $SelectObj.removeData('changed');
  594. setTimeout(function () {
  595. $SelectObj.trigger('change');
  596. ValidateFormElement($SelectObj);
  597. }, 0);
  598. }
  599. }
  600. /**
  601. * @private
  602. * @name RegisterActionEvent
  603. * @memberof Core.UI.InputFields
  604. * @param {jQueryObject} $TreeObj - Tree view object
  605. * @param {jQueryObject} $ActionObj - Action link object
  606. * @param {String} ActionType - Type of the action
  607. * @description
  608. * Register click handler for supplied action.
  609. */
  610. function RegisterActionEvent($TreeObj, $ActionObj, ActionType) {
  611. switch (ActionType) {
  612. case 'SelectAll':
  613. $ActionObj.off('click.InputField').on('click.InputField', function () {
  614. // Make sure subtrees of all nodes are expanded
  615. $TreeObj.jstree('open_all');
  616. // Select all nodes
  617. $TreeObj.find('li')
  618. .not('.jstree-clicked,.Disabled')
  619. .each(function () {
  620. $TreeObj.jstree('select_node', this);
  621. });
  622. return false;
  623. // Prevent clicks on action to steal focus from search field
  624. }).on('mousedown.InputField', function () {
  625. return false;
  626. });
  627. break;
  628. case 'SelectAll_Search':
  629. $ActionObj.off('click.InputField').on('click.InputField', function () {
  630. // Select only matched values
  631. $TreeObj.find('li:visible .jstree-search')
  632. .not('.jstree-clicked,.Disabled')
  633. .each(function () {
  634. $TreeObj.jstree('select_node', this);
  635. });
  636. return false;
  637. });
  638. break;
  639. case 'ClearAll':
  640. $ActionObj.off('click.InputField').on('click.InputField', function () {
  641. // Clear selection
  642. $TreeObj.jstree('deselect_node', $TreeObj.jstree('get_selected'));
  643. return false;
  644. // Prevent clicks on action to steal focus from search field
  645. }).on('mousedown.InputField', function () {
  646. return false;
  647. });
  648. break;
  649. case 'ClearAll_Search':
  650. $ActionObj.off('click.InputField').on('click.InputField', function () {
  651. // Deselect only matched values
  652. var SelectedNodesIDs = $TreeObj.jstree('get_selected');
  653. $.each(SelectedNodesIDs, function () {
  654. var $Node = $('#' + this);
  655. if ($Node.is(':visible')) {
  656. $TreeObj.jstree('deselect_node', this);
  657. }
  658. });
  659. return false;
  660. });
  661. break;
  662. case 'Confirm':
  663. $ActionObj.off('click.InputField').on('click.InputField', function () {
  664. // Hide the list
  665. $TreeObj.blur();
  666. return false;
  667. });
  668. break;
  669. }
  670. }
  671. /**
  672. * @private
  673. * @name ApplyFilter
  674. * @memberof Core.UI.InputFields
  675. * @param {jQueryObject} $SelectObj - Select object
  676. * @param {jQueryObject} $ToolbarContainerObj - Container for toolbar actions
  677. * @description
  678. * Apply active filter on select field.
  679. */
  680. function ApplyFilter($SelectObj, $ToolbarContainerObj) {
  681. var Selection,
  682. FilterIndex;
  683. // Save selection
  684. if ($SelectObj.val()) {
  685. // Check which kind of selection we are dealing with
  686. if ($.isArray($SelectObj.val())) {
  687. Selection = $SelectObj.val();
  688. } else {
  689. Selection = [ $SelectObj.val() ];
  690. }
  691. $SelectObj.data('selection', Selection);
  692. }
  693. $SelectObj.empty();
  694. if ($SelectObj.data('filtered') && $SelectObj.data('filtered') !== '0') {
  695. FilterIndex = parseInt($SelectObj.data('filtered'), 10) - 1;
  696. // Insert filtered data
  697. $.each($SelectObj.data('filters').Filters[FilterIndex].Data, function (Index, Option) {
  698. var $OptionObj = $('<option />');
  699. $OptionObj.attr('value', Option.Key)
  700. .text(Option.Value);
  701. if (Option.Disabled) {
  702. $OptionObj.attr('disabled', true);
  703. }
  704. if (Option.Selected) {
  705. $OptionObj.attr('selected', true);
  706. }
  707. $SelectObj.append($OptionObj);
  708. });
  709. // Add class
  710. if ($ToolbarContainerObj) {
  711. if (
  712. !$ToolbarContainerObj.find('.InputField_Filters')
  713. .hasClass('Active')
  714. )
  715. {
  716. $ToolbarContainerObj.find('.InputField_Filters')
  717. .addClass('Active')
  718. .prepend('<i class="fa fa-filter" /> ');
  719. }
  720. }
  721. }
  722. else {
  723. // Remove class
  724. if ($ToolbarContainerObj) {
  725. $ToolbarContainerObj.find('.InputField_Filters')
  726. .removeClass('Active')
  727. .find('.fa.fa-filter')
  728. .remove();
  729. }
  730. // Restore original data
  731. $SelectObj.append($SelectObj.data('original'));
  732. $SelectObj.data('expand-filters', false);
  733. }
  734. // Restore selection
  735. if ($SelectObj.data('selection')) {
  736. $SelectObj.val($SelectObj.data('selection'));
  737. $SelectObj.removeData('selection');
  738. }
  739. }
  740. /**
  741. * @private
  742. * @name RegisterFilterEvent
  743. * @memberof Core.UI.InputFields
  744. * @param {jQueryObject} $SelectObj - Select object
  745. * @param {jQueryObject} $InputContainerObj - Container object for associated input field
  746. * @param {jQueryObject} $ToolbarContainerObj - Container for toolbar actions
  747. * @param {jQueryObject} $FilterObj - Filter object
  748. * @param {String} ActionType - Type of the action
  749. * @description
  750. * Register click handler for supplied action.
  751. */
  752. function RegisterFilterEvent($SelectObj, $InputContainerObj, $ToolbarContainerObj, $FilterObj, ActionType) {
  753. var $SearchObj;
  754. switch (ActionType) {
  755. case 'ShowFilters':
  756. $FilterObj.off('click.InputField').on('click.InputField', function () {
  757. var $FiltersListObj = $ToolbarContainerObj.find('.InputField_FiltersList');
  758. // Hide filter list
  759. if ($FiltersListObj.is(':visible')) {
  760. $FiltersListObj.hide();
  761. }
  762. // Show filter list
  763. else {
  764. $FiltersListObj.show();
  765. }
  766. return false;
  767. // Prevent clicks on action to steal focus from search field
  768. }).on('mousedown.InputField', function () {
  769. return false;
  770. });
  771. break;
  772. case 'Filter':
  773. $FilterObj.off('click.InputField').on('click.InputField', function (Event) {
  774. // Allow selection of only one filter
  775. $FilterObj.siblings('input').each(function (Index, Filter) {
  776. if ($(Filter).attr('id') !== $FilterObj.attr('id')) {
  777. $(Filter).attr('checked', false);
  778. }
  779. });
  780. Event.stopPropagation();
  781. })
  782. // Handle checkbox change
  783. .off('change.InputField').on('change.InputField', function () {
  784. // Set filter
  785. if (this.checked) {
  786. $SelectObj.data('filtered', $FilterObj.data('index'));
  787. }
  788. // Clear filter
  789. else {
  790. $SelectObj.data('filtered', '0');
  791. }
  792. // Apply filter
  793. ApplyFilter($SelectObj, $ToolbarContainerObj);
  794. // Refresh the field and get focus
  795. $SearchObj = $('#' + Core.App.EscapeSelector($SelectObj.data('modernized')));
  796. $SearchObj.width($SelectObj.outerWidth())
  797. .trigger('blur');
  798. CheckAvailability($SelectObj, $SearchObj, $InputContainerObj);
  799. setTimeout(function () {
  800. $SearchObj.focus();
  801. }, 0);
  802. });
  803. break;
  804. }
  805. }
  806. /**
  807. * @private
  808. * @name FocusNextElement
  809. * @memberof Core.UI.InputFields
  810. * @param {jQueryObject} $Element - Form element
  811. * @description
  812. * Focus next element in form.
  813. */
  814. function FocusNextElement($Element) {
  815. // Get all tabbable and visible elements in the same form
  816. // remove the elements within the input field container of the element
  817. // and re-add the element that has actually focus again
  818. var $TabbableElements = $Element.closest('form')
  819. .find(':tabbable:visible')
  820. .not($Element.closest('.InputField_Container').find(':tabbable:visible'))
  821. .add($Element);
  822. // Advance index for one element and trigger focus
  823. setTimeout(function () {
  824. $TabbableElements.eq($TabbableElements.index($Element) + 1)
  825. .focus();
  826. }, 0);
  827. }
  828. /**
  829. * @private
  830. * @name FocusPreviousElement
  831. * @memberof Core.UI.InputFields
  832. * @param {jQueryObject} $Element - Form element
  833. * @description
  834. * Focus previous element in form.
  835. */
  836. function FocusPreviousElement($Element) {
  837. // Get all tabbable and visible elements in the same form
  838. // remove the elements within the input field container of the element
  839. // and re-add the element that has actually focus again
  840. var $TabbableElements = $Element.closest('form')
  841. .find(':tabbable:visible')
  842. .not($Element.closest('.InputField_Container').find(':tabbable:visible'))
  843. .add($Element);
  844. // Advance index for one element and trigger focus
  845. setTimeout(function () {
  846. $TabbableElements.eq($TabbableElements.index($Element) - 1)
  847. .focus();
  848. }, 0);
  849. }
  850. /**
  851. * @private
  852. * @name BuildSelectionFilter
  853. * @memberof Core.UI.InputFields
  854. * @param {jQueryObject} $SelectObj - Original select field
  855. * @description
  856. * Build current selection filter.
  857. */
  858. function BuildSelectionFilter($SelectObj) {
  859. var Filters = new Object(),
  860. SelectionFilterIndex,
  861. SelectionFilter,
  862. Elements;
  863. if ($SelectObj.data('filters') !== '' && $SelectObj.data('filters') !== undefined) {
  864. Filters.Filters = $SelectObj.data('filters').Filters;
  865. // See if we already have a selection filter
  866. $.each(Filters.Filters, function(Index, Filter) {
  867. if (Filter.Name === Core.Language.Translate('Current selection')) {
  868. SelectionFilterIndex = Index;
  869. SelectionFilter = Filter;
  870. Filters.Filters.splice(SelectionFilterIndex, 1);
  871. SelectionFilter.Data = new Array();
  872. return true;
  873. }
  874. });
  875. } else {
  876. Filters.Filters = new Array();
  877. }
  878. if (Filters.Filters.length === 0 || !SelectionFilterIndex) {
  879. SelectionFilter = new Object();
  880. SelectionFilter.Name = Core.Language.Translate('Current selection');
  881. SelectionFilter.Data = new Array();
  882. }
  883. // Generate JSON structure based on select field options
  884. // Sort the list by default if tree view is active
  885. Elements = Core.UI.TreeSelection.BuildElementsArray($SelectObj, $SelectObj.data('tree', true));
  886. SelectionFilter.Data = GetSelectionFilterData(Elements);
  887. function GetSelectionFilterData(Elements, Data) {
  888. if (Data === undefined) {
  889. Data = new Array();
  890. }
  891. $.each(Elements, function (Index, Element) {
  892. if (typeof Element === 'object') {
  893. if (Element.state) {
  894. if (Element.state.selected) {
  895. Data.push({
  896. 'Key': Element.ID,
  897. 'Value': Element.Name
  898. });
  899. }
  900. if (Element.children.length > 0) {
  901. return GetSelectionFilterData(Element.children, Data);
  902. }
  903. }
  904. }
  905. });
  906. return Data;
  907. }
  908. if (SelectionFilter.Data.length == 0) {
  909. SelectionFilter.Data.push({
  910. 'Key': '',
  911. 'Value': ''
  912. });
  913. }
  914. Filters.Filters.push(SelectionFilter);
  915. $SelectObj.data('filters', Filters);
  916. }
  917. /**
  918. * @private
  919. * @name RefreshSelectionFilter
  920. * @memberof Core.UI.InputFields
  921. * @param {jQueryObject} $SelectObj - Original select field
  922. * @param {jQueryObject} $ToolbarContainerObj - Container for toolbar actions
  923. * @description
  924. * Refresh current selection filter.
  925. */
  926. function RefreshSelectionFilter($SelectObj, $ToolbarContainerObj) {
  927. var Filters = new Object(),
  928. SelectionFilterIndex,
  929. $FilterObj;
  930. // Rebuild the selection filter
  931. BuildSelectionFilter($SelectObj);
  932. // Get index
  933. if ($SelectObj.data('filters') !== '' && $SelectObj.data('filters') !== undefined) {
  934. Filters.Filters = $SelectObj.data('filters').Filters;
  935. $.each(Filters.Filters, function(Index, Filter) {
  936. if (Filter.Name === Core.Language.Translate('Current selection')) {
  937. SelectionFilterIndex = Index;
  938. if ($ToolbarContainerObj) {
  939. $FilterObj = $ToolbarContainerObj.find('input[type="checkbox"]').filter(function () {
  940. return $(this).data('index') === SelectionFilterIndex + 1;
  941. });
  942. }
  943. return true;
  944. }
  945. });
  946. }
  947. // Check if the selection is empty.
  948. if ($SelectObj.val() !== null && $SelectObj.val().length === 0) {
  949. $SelectObj.data('filtered', '0');
  950. $SelectObj.data('selection', []);
  951. ApplyFilter($SelectObj, $ToolbarContainerObj);
  952. if ($FilterObj) {
  953. $FilterObj.attr('disabled', true)
  954. .attr('checked', false);
  955. }
  956. } else {
  957. if (SelectionFilterIndex === parseInt($SelectObj.data('filtered'), 10) - 1) {
  958. ApplyFilter($SelectObj, $ToolbarContainerObj);
  959. }
  960. if ($FilterObj) {
  961. $FilterObj.attr('disabled', false);
  962. }
  963. }
  964. }
  965. /**
  966. * @name RemoveDiacritics
  967. * @memberof Core.UI.InputFields
  968. * @function
  969. * @returns {String} String with removed diacritic characters
  970. * @param {String} Str - String from which to remove diacritic characters
  971. * @description
  972. * Remove all diacritic characters from supplied string (accent folding).
  973. * Taken from https://gist.github.com/instanceofme/1731620
  974. */
  975. TargetNS.RemoveDiacritics = function (Str) {
  976. var Chars = Str.split(''),
  977. i = Chars.length - 1,
  978. Alter = false,
  979. Ch;
  980. for (; i >= 0; i--) {
  981. Ch = Chars[i];
  982. if (Config.Diacritics.hasOwnProperty(Ch)) {
  983. Chars[i] = Config.Diacritics[Ch];
  984. Alter = true;
  985. }
  986. }
  987. if (Alter) {
  988. Str = Chars.join('');
  989. }
  990. return Str;
  991. }
  992. /**
  993. * @name InitSelect
  994. * @memberof Core.UI.InputFields
  995. * @function
  996. * @returns {Boolean} Returns true if successfull, false otherwise
  997. * @param {jQueryObject} $SelectFields - Fields to initialize.
  998. * @description
  999. * This function initializes select input fields, based on supplied CSS selector.
  1000. */
  1001. TargetNS.InitSelect = function ($SelectFields) {
  1002. // Give up if no select fields are found
  1003. if (!$SelectFields.length) {
  1004. return false;
  1005. }
  1006. // Iterate over all found fields
  1007. $SelectFields.each(function (Index, SelectObj) {
  1008. // Global variables
  1009. var $ToolbarContainerObj,
  1010. $InputContainerObj,
  1011. $TreeContainerObj,
  1012. $ListContainerObj,
  1013. $ContainerObj,
  1014. $ToolbarObj,
  1015. $SearchObj,
  1016. $SelectObj,
  1017. $LabelObj,
  1018. Multiple,
  1019. TreeView,
  1020. Focused,
  1021. TabFocus,
  1022. SearchID,
  1023. SkipFocus,
  1024. Searching,
  1025. Filterable,
  1026. SelectWidth,
  1027. SearchLabel,
  1028. $FiltersObj,
  1029. $ShowTreeObj,
  1030. $FiltersListObj,
  1031. WholeRowClicked,
  1032. ScrollEventListener;
  1033. // For performance reasons:
  1034. // Do not initialize modern inputfields on selects with many entries
  1035. if ($(SelectObj).children('option').length > Config.MaxNumberOfOptions) {
  1036. return;
  1037. }
  1038. // Only initialize new elements if original field is valid and visible
  1039. if ($(SelectObj).is(':visible')) {
  1040. // Initialize variables
  1041. $SelectObj = $(SelectObj);
  1042. Multiple = ($SelectObj.attr('multiple') !== '' && $SelectObj.attr('multiple') !== undefined) ? true : false;
  1043. Filterable = ($SelectObj.data('filters') !== '' && $SelectObj.data('filters') !== undefined) ? true : false;
  1044. TreeView = false;
  1045. SkipFocus = false;
  1046. TabFocus = false;
  1047. Searching = false;
  1048. Focused = null;
  1049. // Get width now, since we will hide the element
  1050. SelectWidth = $SelectObj.outerWidth();
  1051. // Hide original field
  1052. $SelectObj.hide();
  1053. // Check to see if we are dealing with tree view
  1054. $ShowTreeObj = $SelectObj.next('.ShowTreeSelection');
  1055. if ($SelectObj.data('tree') || $ShowTreeObj.length) {
  1056. if ($ShowTreeObj.length) {
  1057. $ShowTreeObj.hide();
  1058. }
  1059. $SelectObj.data('tree', true);
  1060. TreeView = true;
  1061. }
  1062. // Create main container
  1063. $ContainerObj = $('<div />').insertBefore($SelectObj);
  1064. $ContainerObj.addClass('InputField_Container')
  1065. .attr('tabindex', '-1');
  1066. // Container for input field
  1067. $InputContainerObj = $('<div />').appendTo($ContainerObj);
  1068. $InputContainerObj.addClass('InputField_InputContainer');
  1069. // Deduce ID of original field
  1070. SearchID = $SelectObj.attr('id');
  1071. // If invalid, create generic one
  1072. if (!SearchID) {
  1073. SearchID = Core.UI.GetID($SelectObj);
  1074. }
  1075. // Make ID unique
  1076. SearchID += '_Search';
  1077. // Flag the element as modernized
  1078. $SelectObj.data('modernized', SearchID);
  1079. // Create new input field to substitute original one
  1080. $SearchObj = $('<input />').appendTo($InputContainerObj);
  1081. $SearchObj.attr('id', SearchID)
  1082. .addClass('InputField_Search')
  1083. .attr('type', 'text')
  1084. .attr('role', 'search')
  1085. .attr('autocomplete', 'off');
  1086. // If original field has class small, add it to the input field, too
  1087. if ($SelectObj.hasClass('Small')) {
  1088. $SearchObj.addClass('Small');
  1089. }
  1090. // Set width of search field to that of the select field
  1091. $SearchObj.width(SelectWidth);
  1092. // Subscribe on window resize event
  1093. Core.App.Subscribe('Event.UI.InputFields.Resize', function() {
  1094. // Set width of search field to that of the select field
  1095. $SearchObj.blur().hide();
  1096. SelectWidth = $SelectObj.show().outerWidth();
  1097. $SelectObj.hide();
  1098. $SearchObj.width(SelectWidth).show();
  1099. });
  1100. // Handle clicks on related label
  1101. if ($SelectObj.attr('id')) {
  1102. $LabelObj = $('label[for="' + Core.App.EscapeSelector($SelectObj.attr('id')) + '"]');
  1103. if ($LabelObj.length > 0) {
  1104. $LabelObj.on('click.InputField', function () {
  1105. $SearchObj.focus();
  1106. });
  1107. $SearchObj.attr('aria-label', $LabelObj.text());
  1108. }
  1109. }
  1110. // Set the earch field label attribute if there was no label element.
  1111. if (!$LabelObj || $LabelObj.length === 0) {
  1112. if ($SelectObj.attr('aria-label')) {
  1113. SearchLabel = $SelectObj.attr('aria-label');
  1114. }
  1115. else if ($SelectObj.attr('title')) {
  1116. SearchLabel = $SelectObj.attr('title');
  1117. }
  1118. else {
  1119. // Fallback: use sanitized ID to provide at least some information.
  1120. SearchLabel = SearchID.replace(/_/g, ' ');
  1121. }
  1122. $SearchObj.attr('aria-label', SearchLabel);
  1123. }
  1124. // Check error classes
  1125. if ($SelectObj.hasClass(Config.ErrorClass)) {
  1126. $SearchObj.addClass(Config.ErrorClass);
  1127. }
  1128. if ($SelectObj.hasClass(Config.ServerErrorClass)) {
  1129. $SearchObj.addClass(Config.ServerErrorClass);
  1130. }
  1131. // Add selection filter
  1132. if (Multiple) {
  1133. Filterable = true;
  1134. BuildSelectionFilter($SelectObj);
  1135. }
  1136. if (Filterable) {
  1137. // Preserve original data
  1138. $SelectObj.data('original', $SelectObj.children());
  1139. // Apply active filter
  1140. if (
  1141. $SelectObj.data('filtered')
  1142. && $SelectObj.data('filtered') !== '0'
  1143. )
  1144. {
  1145. ApplyFilter($SelectObj, $ToolbarContainerObj);
  1146. }
  1147. }
  1148. // Show selection boxes
  1149. ShowSelectionBoxes($SelectObj, $InputContainerObj);
  1150. // Disable field if no selection available
  1151. CheckAvailability($SelectObj, $SearchObj, $InputContainerObj);
  1152. // Handle form disabling
  1153. Core.App.Subscribe('Event.Form.DisableForm', function ($Form) {
  1154. if ($Form.find($SearchObj).attr('readonly')) {
  1155. $SearchObj.data('form-disabled', true);
  1156. CheckAvailability($SelectObj, $SearchObj, $InputContainerObj);
  1157. }
  1158. });
  1159. // Handle form enabling
  1160. Core.App.Subscribe('Event.Form.EnableForm', function ($Form) {
  1161. if (!$Form.find($SearchObj).attr('readonly')) {
  1162. $SearchObj.removeData('form-disabled');
  1163. CheckAvailability($SelectObj, $SearchObj, $InputContainerObj);
  1164. }
  1165. });
  1166. // Handle dialog dragging
  1167. Core.App.Subscribe('Event.UI.Dialog.ShowDialog.DragStart', function() {
  1168. CloseOpenSelections();
  1169. });
  1170. // Handle dialog opening
  1171. Core.App.Subscribe('Event.UI.Dialog.ShowDialog.BeforeOpen', function() {
  1172. CloseOpenSelections();
  1173. });
  1174. // Handle dialog closing
  1175. Core.App.Subscribe('Event.UI.Dialog.CloseDialog.Close', function() {
  1176. CloseOpenSelections();
  1177. });
  1178. // Handle RTE focus
  1179. Core.App.Subscribe('Event.UI.RichTextEditor.Focus', function() {
  1180. CloseOpenSelections();
  1181. });
  1182. // Handle article navigation in TicketZoom
  1183. Core.App.Subscribe('Event.Agent.TicketZoom.ArticleClick', function() {
  1184. CloseOpenSelections();
  1185. });
  1186. // Handle hiding of inline actions in ticket overviews.
  1187. Core.App.Subscribe('Event.Agent.TicketOverview.InlineActions.Hidden', function ($InlineActionsContainer) {
  1188. setTimeout(function () {
  1189. CloseOpenSelections($InlineActionsContainer);
  1190. }, 0);
  1191. });
  1192. // Register handler for on focus event
  1193. $SearchObj.off('focus.InputField')
  1194. .on('focus.InputField', function () {
  1195. var TreeID,
  1196. $TreeObj,
  1197. SelectedID,
  1198. OldSelectedID,
  1199. Elements,
  1200. SelectedNodes,
  1201. AvailableHeightBottom,
  1202. AvailableHeightTop,
  1203. AvailableMaxHeight,
  1204. PossibleNone,
  1205. $ClearAllObj,
  1206. $SelectAllObj,
  1207. $ConfirmObj,
  1208. ErrorTooltipPosition = 'TongueTop';
  1209. function CalculateListPosition() {
  1210. // calculate available height to bottom of page
  1211. AvailableHeightBottom = parseInt(
  1212. $(window).scrollTop() + $(window).height()
  1213. - (
  1214. $InputContainerObj.offset().top
  1215. + $InputContainerObj.outerHeight()
  1216. + Config.SafeMargin
  1217. ),
  1218. 10
  1219. );
  1220. // calculate available height to top of page
  1221. AvailableHeightTop = parseInt($InputContainerObj.offset().top - $(window).scrollTop() - Config.SafeMargin, 10);
  1222. // set left position
  1223. $ListContainerObj
  1224. .css({
  1225. left: $InputContainerObj.offset().left
  1226. });
  1227. // decide whether list should be positioned on top or at the bottom of the input field
  1228. if (AvailableHeightTop > AvailableHeightBottom) {
  1229. AvailableMaxHeight = AvailableHeightTop;
  1230. // add a class to the searchobj itself to be able to react on it properly
  1231. // e.g. to show the error tooltip
  1232. $SearchObj.removeClass('ExpandToBottom')
  1233. .addClass('ExpandToTop');
  1234. $ListContainerObj
  1235. .removeClass('ExpandToBottom')
  1236. .addClass('ExpandToTop')
  1237. .css({
  1238. top: 'auto',
  1239. bottom: parseInt($('body').height() - $InputContainerObj.offset().top, 10)
  1240. });
  1241. }
  1242. else {
  1243. AvailableMaxHeight = AvailableHeightBottom;
  1244. $SearchObj.removeClass('ExpandToTop')
  1245. .addClass('ExpandToBottom');
  1246. $ListContainerObj
  1247. .removeClass('ExpandToTop')
  1248. .addClass('ExpandToBottom')
  1249. .css({
  1250. top: parseInt($InputContainerObj.offset().top + $InputContainerObj.outerHeight(), 10),
  1251. bottom: 'auto'
  1252. });
  1253. }
  1254. }
  1255. $SelectObj.find('option').each(function (Index, Option) {
  1256. if ($(Option).attr('value') === '' || $(Option).attr('value') === '||-') {
  1257. PossibleNone = true;
  1258. return true;
  1259. }
  1260. });
  1261. function ShowErrorToolTip() {
  1262. // Show error tooltip if needed
  1263. if ($SelectObj.attr('id')) {
  1264. if ($SearchObj.hasClass('ExpandToTop')) {
  1265. ErrorTooltipPosition = 'TongueBottom';
  1266. }
  1267. else {
  1268. ErrorTooltipPosition = 'TongueTop';
  1269. }
  1270. if ($SelectObj.hasClass(Config.ErrorClass)) {
  1271. Core.Form.ErrorTooltips.ShowTooltip(
  1272. $SearchObj, $('#' + Core.App.EscapeSelector($SelectObj.attr('id')) + Config.ErrorClass).html(), ErrorTooltipPosition
  1273. );
  1274. }
  1275. if ($SelectObj.hasClass(Config.ServerErrorClass)) {
  1276. Core.Form.ErrorTooltips.ShowTooltip(
  1277. $SearchObj, $('#' + Core.App.EscapeSelector($SelectObj.attr('id')) + Config.ServerErrorClass).html(), ErrorTooltipPosition
  1278. );
  1279. }
  1280. }
  1281. }
  1282. ShowErrorToolTip();
  1283. // Focus tracking
  1284. Focused = this;
  1285. SkipFocus = false;
  1286. // Do nothing if already expanded
  1287. if ($SearchObj.attr('aria-expanded')) {
  1288. return false;
  1289. }
  1290. // Do nothing if disabled
  1291. if ($SearchObj.attr('readonly')) {
  1292. return false;
  1293. }
  1294. // close all other selections
  1295. CloseOpenSelections();
  1296. // Set ARIA flag if expanded
  1297. $SearchObj.attr('aria-expanded', true);
  1298. // Remove any existing selection boxes in container
  1299. $InputContainerObj.find('.InputField_Selection').remove();
  1300. $InputContainerObj.find('.InputField_SelectionFilter').remove();
  1301. $InputContainerObj.find('.InputField_More').remove();
  1302. // Create list container
  1303. $ListContainerObj = $('<div />')
  1304. .addClass('InputField_ListContainer')
  1305. .attr('tabindex', '-1')
  1306. .appendTo('body');
  1307. // Calculate available height for the list
  1308. CalculateListPosition();
  1309. // define a named function here to use the variables of upper scope
  1310. // (as it was before with anonymous function)
  1311. // needed, to remove the event listener again later in the blur event
  1312. // we cannot use jquery events, because scroll event on document does not bubble up
  1313. ScrollEventListener = function(Event) {
  1314. if (!$ListContainerObj) {
  1315. return;
  1316. }
  1317. CalculateListPosition();
  1318. ShowErrorToolTip();
  1319. // This checks, if an inner element is scrolled (e.g. dialog)
  1320. // we only need to hide the list in this case, because scrolling the main window
  1321. // will hide the dropdown list anyway from viewport
  1322. if (Event.target !== document && !$(Event.target).hasClass('InputField_TreeContainer')) {
  1323. if (
  1324. $InputContainerObj.position().top + $InputContainerObj.outerHeight() - $(Event.target).outerHeight()
  1325. >= 0
  1326. )
  1327. {
  1328. $ListContainerObj.hide();
  1329. } else {
  1330. $ListContainerObj.show();
  1331. }
  1332. }
  1333. };
  1334. // Listen for scroll event in order to move the list
  1335. // Scroll event does not bubble, hence the need for listener
  1336. document.addEventListener('scroll', ScrollEventListener, true);
  1337. // Create container for jsTree code
  1338. $TreeContainerObj = $('<div />').appendTo($ListContainerObj);
  1339. $TreeContainerObj.addClass('InputField_TreeContainer')
  1340. .attr('tabindex', '-1');
  1341. // Subtract approx. filters list height if applicable
  1342. if (Filterable) {
  1343. AvailableMaxHeight -= $SelectObj.data('filters').Filters.length
  1344. * Config.SafeMargin;
  1345. }
  1346. // Ensure the minimum height of the list
  1347. if (AvailableMaxHeight < 90) {
  1348. AvailableMaxHeight = 90;
  1349. }
  1350. // Set maximum height of the list to available space
  1351. $TreeContainerObj.css('max-height', AvailableMaxHeight + 'px');
  1352. // Calculate width for tree container
  1353. $TreeContainerObj.width($SearchObj.width()
  1354. + Config.InputFieldPadding * 2
  1355. );
  1356. // Deduce ID of original field
  1357. TreeID = $SelectObj.attr('id');
  1358. // If invalid, create generic one
  1359. if (!TreeID) {
  1360. TreeID = Core.UI.GetID($SelectObj);
  1361. }
  1362. // Make ID unique
  1363. TreeID += '_Select';
  1364. // jsTree init
  1365. $TreeObj = $('<div id="' + Core.App.EscapeSelector(TreeID) + '"><ul></ul></div>');
  1366. SelectedID = $SelectObj.val();
  1367. Elements = {};
  1368. SelectedNodes = [];
  1369. // Generate JSON structure based on select field options
  1370. // Sort the list by default if tree view is active
  1371. Elements = Core.UI.TreeSelection.BuildElementsArray($SelectObj, TreeView);
  1372. // Force no tree view if structure has only root level
  1373. // but only if field should not contain tree structure (see bug#12017)
  1374. if (Elements.HighestLevel === 0 && !$SelectObj.data('tree')) {
  1375. TreeView = false;
  1376. }
  1377. // Initialize jsTree
  1378. /* eslint-disable camelcase */
  1379. $TreeObj.jstree({
  1380. core: {
  1381. animation: 70,
  1382. data: Elements,
  1383. multiple: Multiple,
  1384. expand_selected_onload: true,
  1385. check_callback: true,
  1386. themes: {
  1387. name: 'InputField',
  1388. variant: (TreeView) ? 'Tree' : 'NoTree',
  1389. icons: false,
  1390. dots: false,
  1391. url: false
  1392. }
  1393. },
  1394. search: {
  1395. show_only_matches: true,
  1396. show_only_matches_children: true,
  1397. search_callback: function (Search, Node) {
  1398. var SearchString = TargetNS.RemoveDiacritics(Search),
  1399. NodeString = TargetNS.RemoveDiacritics(Node.text);
  1400. return (
  1401. // we're doing toLowerCase() AND toUpperCase() because of bug#11548
  1402. (NodeString.toLowerCase().indexOf(SearchString.toLowerCase()) !== -1 || NodeString.toUpperCase().indexOf(SearchString.toUpperCase()) !== -1)
  1403. );
  1404. }
  1405. },
  1406. plugins: [ 'multiselect', 'search', 'wholerow' ]
  1407. })
  1408. // Handle focus event for tree item
  1409. .on('focus.jstree', '.jstree-anchor', function () {
  1410. if (!SkipFocus) {
  1411. Focused = this;
  1412. // In modernize field selection disable 'backspace' key functionality.
  1413. // See bug#14011 (https://bugs.otrs.org/show_bug.cgi?id=14011).
  1414. $('.jstree .jstree-anchor').on('keydown', function (e) {
  1415. if (e.which === 8 && !$(e.target).is('input')) {
  1416. return false;
  1417. }
  1418. });
  1419. } else {
  1420. SkipFocus = false;
  1421. }
  1422. })
  1423. // Handle focus event for tree list
  1424. .on('focus.jstree', function () {
  1425. if (!SkipFocus) {
  1426. Focused = this;
  1427. } else {
  1428. SkipFocus = false;
  1429. }
  1430. // Focus first available tree item
  1431. if (TabFocus) {
  1432. $($TreeObj.find('a.jstree-anchor:visible')
  1433. .not('.jstree-disabled')
  1434. .get(0)
  1435. ).trigger('focus.jstree');
  1436. TabFocus = false;
  1437. }
  1438. })
  1439. .on('mousedown.jstree', function (Event) {
  1440. var $Target = $(Event.target);
  1441. if ($Target.hasClass('jstree-wholerow')) {
  1442. WholeRowClicked = $Target;
  1443. }
  1444. })
  1445. // Handle blur event for tree item
  1446. .on('blur.jstree', '.jstree-anchor', function () {
  1447. setTimeout(function () {
  1448. if (!Focused) {
  1449. HideSelectList($SelectObj, $InputContainerObj, $SearchObj, $ListContainerObj, $TreeContainerObj);
  1450. }
  1451. Focused = null;
  1452. }, 0);
  1453. })
  1454. // Handle blur event for tree list
  1455. .on('blur.jstree', function () {
  1456. setTimeout(function () {
  1457. if (!Focused && !WholeRowClicked) {
  1458. HideSelectList($SelectObj, $InputContainerObj, $SearchObj, $ListContainerObj, $TreeContainerObj);
  1459. }
  1460. Focused = null;
  1461. WholeRowClicked = null;
  1462. }, 0);
  1463. })
  1464. // Handle node selection in tree list
  1465. // Skip eslint check on next line for unused vars (it's actually event)
  1466. .on('select_node.jstree', function (Node, Selected, Event) { //eslint-disable-line no-unused-vars
  1467. var $SelectedNode = $('#' + Selected.node.id),
  1468. SelectedNodesIDs;
  1469. // Do not select disabled nodes
  1470. if ($SelectedNode.hasClass('Disabled') || !$SelectedNode.is(':visible')) {
  1471. $TreeObj.jstree('deselect_node', Selected.node);
  1472. }
  1473. // Reset selected nodes list
  1474. SelectedNodes = [];
  1475. // Get selected nodes
  1476. SelectedNodesIDs = $TreeObj.jstree('get_selected');
  1477. $.each(SelectedNodesIDs, function () {
  1478. var $Node = $('#' + this);
  1479. SelectedNodes.push($Node.data('id'));
  1480. });
  1481. // Set selected nodes as selected in initial select box
  1482. // (which is hidden but is still used for the action)
  1483. $SelectObj.val(SelectedNodes);
  1484. OldSelectedID = SelectedID;
  1485. SelectedID = $SelectObj.val();
  1486. // If single select, lose the focus and hide the list
  1487. if (!Multiple) {
  1488. SkipFocus = true;
  1489. $TreeObj.blur();
  1490. }
  1491. // Refresh selection filter
  1492. RefreshSelectionFilter($SelectObj, $ToolbarContainerObj);
  1493. // Delay triggering change event on original field (see bug#11419)
  1494. $SelectObj.data('changed', true);
  1495. return false;
  1496. })
  1497. // click is also triggered (besides select_node), which
  1498. // could result in a bubbled-up event
  1499. // prevents dialogs from accidently closing
  1500. // jstree triggers a click event for pressing the enter key
  1501. // so we try to handle this here
  1502. .on('click.jstree', function (Event) {
  1503. var $HoveredNode, HoveredValue;
  1504. Event.preventDefault();
  1505. // check for keydown event for enter key
  1506. if (
  1507. typeof Event.originalEvent !== 'undefined'
  1508. && Event.originalEvent.type === 'keydown'
  1509. && Event.originalEvent.which === $.ui.keyCode.ENTER) {
  1510. $HoveredNode = $TreeObj.find('.jstree-hovered');
  1511. HoveredValue = $HoveredNode.closest('li').data('id');
  1512. // at this point, the jstree events have already selected the new value and processed the event
  1513. // but we need to know, if the hovered element was selected before or not to decide whether to
  1514. // select or deselect it now. therefor we check for OldSelectedID
  1515. if (!Multiple) {
  1516. if (HoveredValue !== OldSelectedID) {
  1517. $TreeObj.jstree('deselect_all');
  1518. $TreeObj.jstree('select_node', $HoveredNode.get(0));
  1519. }
  1520. else {
  1521. if (PossibleNone) {
  1522. $TreeObj.jstree('deselect_all');
  1523. $SelectObj.val('');
  1524. }
  1525. }
  1526. FocusNextElement($SearchObj);
  1527. }
  1528. }
  1529. Event.stopPropagation();
  1530. return false;
  1531. })
  1532. // Handle node deselection in tree list
  1533. .on('deselect_node.jstree', function () {
  1534. var SelectedNodesIDs,
  1535. HasEmptyElement = $SelectObj.find('option[value=""]').length === 0 ? false : true;
  1536. if (Multiple) {
  1537. // Reset selected nodes list
  1538. SelectedNodes = [];
  1539. // Get selected nodes
  1540. SelectedNodesIDs = $TreeObj.jstree('get_selected');
  1541. $.each(SelectedNodesIDs, function () {
  1542. var $Node = $('#' + this);
  1543. SelectedNodes.push($Node.data('id'));
  1544. });
  1545. // Set selected nodes as selected in initial select box
  1546. // (which is hidden but is still used for the action)
  1547. if (HasEmptyElement && SelectedNodes.length === 0) {
  1548. $SelectObj.val('');
  1549. }
  1550. else {
  1551. $SelectObj.val(SelectedNodes);
  1552. }
  1553. OldSelectedID = SelectedID;
  1554. SelectedID = $SelectObj.val();
  1555. // Update selection filter
  1556. RefreshSelectionFilter($SelectObj, $ToolbarContainerObj);
  1557. // Delay triggering change event on original field (see bug#11419)
  1558. $SelectObj.data('changed', true);
  1559. }
  1560. })
  1561. // Handle double clicks on node rows in tree list
  1562. .on('dblclick.jstree', '.jstree-wholerow', function (Event) {
  1563. var Node;
  1564. // Expand node if we are in tree view
  1565. if (TreeView) {
  1566. Node = $(Event.target).closest('li');
  1567. $TreeObj.jstree('toggle_node', Node);
  1568. }
  1569. })
  1570. // Keydown handler for tree list
  1571. .keydown(function (Event) {
  1572. var $HoveredNode;
  1573. switch (Event.which) {
  1574. // Tab
  1575. // Find correct input, if element is selected in dropdown and tab key is used
  1576. case $.ui.keyCode.TAB:
  1577. // On pressing tab the active element will be selected and the field will be left
  1578. $HoveredNode = $TreeObj.find('.jstree-hovered');
  1579. if (!Multiple) {
  1580. $TreeObj.jstree('deselect_all');
  1581. }
  1582. $TreeObj.jstree('select_node', $HoveredNode.get(0));
  1583. if (Event.shiftKey) {
  1584. FocusPreviousElement($SearchObj);
  1585. }
  1586. else {
  1587. FocusNextElement($SearchObj);
  1588. }
  1589. break;
  1590. // Escape
  1591. case $.ui.keyCode.ESCAPE:
  1592. Event.preventDefault();
  1593. $TreeObj.blur();
  1594. break;
  1595. // Space
  1596. case $.ui.keyCode.SPACE:
  1597. Event.preventDefault();
  1598. $HoveredNode = $TreeObj.find('.jstree-hovered');
  1599. if (!Multiple) {
  1600. if (!$HoveredNode.hasClass('jstree-clicked')) {
  1601. $TreeObj.jstree('deselect_all');
  1602. $TreeObj.jstree('select_node', $HoveredNode.get(0));
  1603. }
  1604. else {
  1605. if (PossibleNone) {
  1606. $TreeObj.jstree('deselect_all');
  1607. $SelectObj.val('');
  1608. setTimeout(function () {
  1609. $SelectObj.trigger('change');
  1610. ValidateFormElement($SelectObj);
  1611. }, 0);
  1612. }
  1613. }
  1614. FocusNextElement($SearchObj);
  1615. }
  1616. else {
  1617. if ($HoveredNode.hasClass('jstree-clicked')) {
  1618. $TreeObj.jstree('deselect_node', $HoveredNode.get(0));
  1619. }
  1620. else {
  1621. $TreeObj.jstree('select_node', $HoveredNode.get(0));
  1622. }
  1623. }
  1624. break;
  1625. // Ctrl (Cmd) + A
  1626. case 65:
  1627. if (Event.ctrlKey || Event.metaKey) {
  1628. Event.preventDefault();
  1629. $ListContainerObj.find('.InputField_SelectAll')
  1630. .click();
  1631. }
  1632. break;
  1633. // Ctrl (Cmd) + D
  1634. case 68:
  1635. if (Event.ctrlKey || Event.metaKey) {
  1636. Event.preventDefault();
  1637. $ListContainerObj.find('.InputField_ClearAll')
  1638. .click();
  1639. }
  1640. break;
  1641. // Ctrl (Cmd) + F
  1642. case 70:
  1643. if (Event.ctrlKey || Event.metaKey) {
  1644. Event.preventDefault();
  1645. $ListContainerObj.find('.InputField_Filters')
  1646. .click();
  1647. $ListContainerObj.find('.InputField_FiltersList').children('input').first().focus();
  1648. }
  1649. break;
  1650. }
  1651. })
  1652. // Initialize existing selection
  1653. .on('loaded.jstree', function () {
  1654. if (SelectedID) {
  1655. if (typeof SelectedID === 'object') {
  1656. $.each(SelectedID, function (NodeIndex, Data) {
  1657. $TreeObj.jstree('select_node', $TreeObj.find('li[data-id="' + Core.App.EscapeSelector(Data) + '"]'));
  1658. });
  1659. }
  1660. else {
  1661. $TreeObj.jstree('select_node', $TreeObj.find('li[data-id="' + Core.App.EscapeSelector(SelectedID) + '"]'));
  1662. }
  1663. }
  1664. Core.App.Publish('Event.UI.InputFields.Expanded', $SearchObj);
  1665. });
  1666. // Prevent loss of focus when using scrollbar
  1667. $TreeContainerObj.on('focus.InputField', function () {
  1668. if (!SkipFocus) {
  1669. Focused = this;
  1670. } else {
  1671. SkipFocus = false;
  1672. }
  1673. }).on('blur.jstree', function () {
  1674. setTimeout(function () {
  1675. if (!Focused) {
  1676. HideSelectList($SelectObj, $InputContainerObj, $SearchObj, $ListContainerObj, $TreeContainerObj);
  1677. }
  1678. Focused = null;
  1679. }, 0);
  1680. });
  1681. // Append tree code to the container and show it
  1682. $TreeObj
  1683. .appendTo($TreeContainerObj)
  1684. .show();
  1685. $ToolbarContainerObj = $('<div />').appendTo($ListContainerObj);
  1686. $ToolbarContainerObj.addClass('InputField_ToolbarContainer')
  1687. .attr('tabindex', '-1')
  1688. .width($TreeContainerObj.width());
  1689. $ToolbarObj = $('<ul />').appendTo($ToolbarContainerObj)
  1690. .attr('tabindex', '-1')
  1691. .on('focus.InputField', function () {
  1692. if (!SkipFocus) {
  1693. Focused = this;
  1694. } else {
  1695. SkipFocus = false;
  1696. }
  1697. }).on('blur.InputField', function () {
  1698. setTimeout(function () {
  1699. if (!Focused) {
  1700. HideSelectList($SelectObj, $InputContainerObj, $SearchObj, $ListContainerObj, $TreeContainerObj);
  1701. }
  1702. Focused = null;
  1703. }, 0);
  1704. });
  1705. if (Multiple) {
  1706. // Select all action selects all values in tree
  1707. $SelectAllObj = $('<a />').addClass('InputField_SelectAll')
  1708. .attr('href', '#')
  1709. .attr('role', 'button')
  1710. .attr('tabindex', '-1')
  1711. .text(Core.Language.Translate('Select all'))
  1712. .attr('aria-label', Core.Language.Translate('Select all'))
  1713. .appendTo($ToolbarObj)
  1714. .wrap('<li />');
  1715. RegisterActionEvent($TreeObj, $SelectAllObj, 'SelectAll');
  1716. // Clear all action deselects all selected values in tree
  1717. $ClearAllObj = $('<a />').addClass('InputField_ClearAll')
  1718. .attr('href', '#')
  1719. .attr('role', 'button')
  1720. .attr('tabindex', '-1')
  1721. .text(Core.Language.Translate('Clear all'))
  1722. .attr('aria-label', Core.Language.Translate('Clear all'))
  1723. .appendTo($ToolbarObj)
  1724. .wrap('<li />');
  1725. RegisterActionEvent($TreeObj, $ClearAllObj, 'ClearAll');
  1726. }
  1727. if (Filterable) {
  1728. // Filters action button
  1729. $FiltersObj = $('<a />').addClass('InputField_Filters')
  1730. .attr('href', '#')
  1731. .attr('role', 'button')
  1732. .attr('tabindex', '-1')
  1733. .text(Core.Language.Translate('Filters'))
  1734. .attr('aria-label', Core.Language.Translate('Filters'))
  1735. .appendTo($ToolbarObj)
  1736. .wrap('<li />');
  1737. RegisterFilterEvent($SelectObj, $InputContainerObj, $ToolbarContainerObj, $FiltersObj, 'ShowFilters');
  1738. if (!$SelectObj.data('filtered')) {
  1739. $SelectObj.data('filtered', '0');
  1740. } else if ($SelectObj.data('filtered') !== '0') {
  1741. $FiltersObj.addClass('Active')
  1742. .prepend('<i class="fa fa-filter" /> ');
  1743. }
  1744. // Filters list
  1745. $FiltersListObj = $('<div />').appendTo($ToolbarContainerObj);
  1746. $FiltersListObj.addClass('InputField_FiltersList')
  1747. .attr('tabindex', '-1');
  1748. // Hide the filters list if no parameter is supplied
  1749. if (
  1750. !$SelectObj.data('expand-filters')
  1751. && $SelectObj.data('expand-filters') !== '0'
  1752. )
  1753. {
  1754. $FiltersListObj.hide();
  1755. }
  1756. // Filters checkboxes
  1757. $.each($SelectObj.data('filters').Filters, function (FilterIndex, Filter) {
  1758. var $FilterObj = $('<input />').appendTo($FiltersListObj),
  1759. $SpanObj = $('<span />').appendTo($FiltersListObj);
  1760. $FilterObj.attr('type', 'checkbox')
  1761. .attr('tabindex', '-1')
  1762. .addClass('InputField_FilterCheckbox')
  1763. .data('index', FilterIndex + 1);
  1764. if (
  1765. $SelectObj.data('filtered')
  1766. && parseInt($SelectObj.data('filtered'), 10) === FilterIndex + 1
  1767. )
  1768. {
  1769. $FilterObj.attr('checked', true);
  1770. }
  1771. if (
  1772. Filter.Data.length === 1
  1773. && (Filter.Data[0].Key === '' || Filter.Data[0].Key === '||-')
  1774. )
  1775. {
  1776. $FilterObj.attr('disabled', true);
  1777. }
  1778. Core.UI.GetID($FilterObj);
  1779. $SpanObj.text(Filter.Name);
  1780. $SpanObj.on('click', function (Event) {
  1781. $FilterObj.click();
  1782. Event.stopPropagation();
  1783. });
  1784. $('<br />').appendTo($FiltersListObj);
  1785. RegisterFilterEvent($SelectObj, $InputContainerObj, $ToolbarContainerObj, $FilterObj, 'Filter');
  1786. });
  1787. $FiltersListObj
  1788. .on('focus', 'input', function () {
  1789. Focused = this;
  1790. })
  1791. .on('keydown', 'input', function (Event) {
  1792. var $FilterElements,
  1793. FilterElementIndex;
  1794. switch (Event.which) {
  1795. // Tab
  1796. case $.ui.keyCode.TAB:
  1797. $FilterElements = $FiltersListObj.find('input');
  1798. FilterElementIndex = $FilterElements.index(Event.target);
  1799. // if not shift key and on the last element of the list, jump to top again
  1800. if (!Event.shiftKey) {
  1801. Event.preventDefault();
  1802. Event.stopPropagation();
  1803. if (FilterElementIndex + 1 === $FilterElements.length) {
  1804. $FilterElements.first().focus();
  1805. }
  1806. else {
  1807. $FilterElements.eq(FilterElementIndex + 1).focus();
  1808. }
  1809. }
  1810. // first element and Shift+Tab, jump to last element in list
  1811. else {
  1812. Event.preventDefault();
  1813. Event.stopPropagation();
  1814. if (FilterElementIndex === 0) {
  1815. $FilterElements.last().focus();
  1816. }
  1817. else {
  1818. $FilterElements.eq(FilterElementIndex - 1).focus();
  1819. }
  1820. }
  1821. break;
  1822. // Escape
  1823. case $.ui.keyCode.ESCAPE:
  1824. $ListContainerObj.find('.InputField_Filters')
  1825. .click();
  1826. $SearchObj.focus();
  1827. break;
  1828. // Ctrl (Cmd) + F (closing filters, if this event triggers, the filters are always expanded)
  1829. case 70:
  1830. if (Event.ctrlKey || Event.metaKey) {
  1831. Event.preventDefault();
  1832. $ListContainerObj.find('.InputField_Filters')
  1833. .click();
  1834. $SearchObj.focus();
  1835. }
  1836. break;
  1837. }
  1838. });
  1839. }
  1840. if (Multiple) {
  1841. // Confirm action exits the field
  1842. $ConfirmObj = $('<a />').addClass('InputField_Confirm')
  1843. .attr('href', '#')
  1844. .attr('role', 'button')
  1845. .attr('tabindex', '-1')
  1846. .text(Core.Language.Translate('Confirm'))
  1847. .attr('aria-label', Core.Language.Translate('Confirm'))
  1848. .appendTo($ToolbarObj)
  1849. .prepend('<i class="fa fa-check-square-o" /> ')
  1850. .wrap('<li />');
  1851. RegisterActionEvent($TreeObj, $ConfirmObj, 'Confirm');
  1852. }
  1853. if ($ToolbarObj.children().length === 0) {
  1854. $ToolbarContainerObj.hide();
  1855. }
  1856. // Set up jsTree search function for input search field
  1857. $SearchObj.off('keyup.InputField').on('keyup.InputField', function () {
  1858. var SearchValue = $SearchObj.val().trim(),
  1859. NoMatchNodeJSON,
  1860. $ClearSearchObj,
  1861. SearchTimeout;
  1862. // Clear search timeout
  1863. window.clearTimeout(SearchTimeout);
  1864. SearchTimeout = window.setTimeout(function () {
  1865. // Abandon search if empty string
  1866. if (SearchValue === '') {
  1867. $TreeObj.jstree('delete_node', $TreeObj.find('.jstree-no-match'));
  1868. $TreeObj.jstree('clear_search');
  1869. Searching = false;
  1870. $SearchObj.siblings('.InputField_ClearSearch')
  1871. .remove();
  1872. if (Multiple) {
  1873. // Reset select all and clear all functions to original behavior
  1874. $SelectAllObj.off('click.InputField').on('click.InputField', function () {
  1875. // Make sure subtrees of all nodes are expanded
  1876. $TreeObj.jstree('open_all');
  1877. // Select all nodes
  1878. $TreeObj.find('li')
  1879. .not('.jstree-clicked,.Disabled')
  1880. .each(function () {
  1881. $TreeObj.jstree('select_node', this);
  1882. });
  1883. return false;
  1884. });
  1885. $ClearAllObj.off('click.InputField').on('click.InputField', function () {
  1886. // Clear selection
  1887. $TreeObj.jstree('deselect_node', $TreeObj.jstree('get_selected'));
  1888. return false;
  1889. });
  1890. }
  1891. return false;
  1892. }
  1893. // Remove no match entry if existing from previous search
  1894. $TreeObj.jstree('delete_node', $TreeObj.find('.jstree-no-match'));
  1895. // Start jsTree search
  1896. $TreeObj.jstree('search', Core.App.EscapeHTML(SearchValue));
  1897. Searching = true;
  1898. if (Multiple) {
  1899. // Change select all action to select only matched values
  1900. RegisterActionEvent($TreeObj, $SelectAllObj, 'SelectAll_Search');
  1901. // Change clear all action to deselect only matched values
  1902. RegisterActionEvent($TreeObj, $ClearAllObj, 'ClearAll_Search');
  1903. }
  1904. // No match
  1905. if ($TreeObj.find('.jstree-search').length === 0) {
  1906. // Add no match node
  1907. NoMatchNodeJSON = {
  1908. text: Core.Language.Translate('No matches found.'),
  1909. state: {
  1910. disabled: true
  1911. },
  1912. 'li_attr': {
  1913. class: 'Disabled jstree-no-match'
  1914. }
  1915. };
  1916. $TreeObj.jstree('create_node', $TreeObj, NoMatchNodeJSON);
  1917. // Hide all other nodes
  1918. $TreeObj.find('li:visible')
  1919. .not('.jstree-no-match')
  1920. .hide();
  1921. }
  1922. // Check if we are searching for something
  1923. if ($SearchObj.siblings('.InputField_ClearSearch').length === 0) {
  1924. // Clear search action stops search
  1925. $ClearSearchObj = $('<a />').insertAfter($SearchObj);
  1926. $ClearSearchObj.addClass('InputField_Action InputField_ClearSearch')
  1927. .attr('href', '#')
  1928. .attr('title', Core.Language.Translate('Clear search'))
  1929. .css(($('body').hasClass('RTL') ? 'left' : 'right'), Config.SelectionBoxOffsetRight + 'px')
  1930. .append($('<i />').addClass('fa fa-times-circle'))
  1931. .attr('role', 'button')
  1932. .attr('tabindex', '-1')
  1933. .attr('aria-label', Core.Language.Translate('Clear search'))
  1934. .off('click.InputField').on('click.InputField', function () {
  1935. // Reset the search field
  1936. $SearchObj.val('');
  1937. // Clear search from jsTree and remove no match node
  1938. $TreeObj.jstree('delete_node', $TreeObj.find('.jstree-no-match'));
  1939. $TreeObj.jstree('clear_search');
  1940. Searching = false;
  1941. if (Multiple) {
  1942. // Reset select all and clear all functions to original behavior
  1943. RegisterActionEvent($TreeObj, $SelectAllObj, 'SelectAll');
  1944. RegisterActionEvent($TreeObj, $ClearAllObj, 'ClearAll');
  1945. }
  1946. // Remove the action icon
  1947. $(this).remove();
  1948. return false;
  1949. // Prevent clicks on action to steal focus from search field
  1950. }).on('mousedown.InputField', function () {
  1951. return false;
  1952. });
  1953. }
  1954. }, 250);
  1955. });
  1956. // Show list container
  1957. // (if field is completely visible)
  1958. if (
  1959. !$SearchObj.parents('.Dialog').length ||
  1960. $SearchObj.parents('.Dialog').length &&
  1961. ($InputContainerObj.position().top + $InputContainerObj.outerHeight() - $SearchObj.parents('.Dialog').find('.InnerContent').outerHeight() < 0)
  1962. ) {
  1963. $ListContainerObj.fadeIn(Config.FadeDuration);
  1964. }
  1965. })
  1966. // Out of focus handler removes complete jsTree and action buttons
  1967. .off('blur.InputField').on('blur.InputField', function () {
  1968. document.removeEventListener('scroll', ScrollEventListener, true);
  1969. setTimeout(function () {
  1970. if (!Focused) {
  1971. HideSelectList($SelectObj, $InputContainerObj, $SearchObj, $ListContainerObj, $TreeContainerObj);
  1972. }
  1973. Focused = null;
  1974. }, 0);
  1975. Core.Form.ErrorTooltips.HideTooltip();
  1976. })
  1977. // Keydown handler provides keyboard shortcuts for navigating the tree
  1978. .keydown(function (Event) {
  1979. var $TreeObj = $TreeContainerObj.find('.jstree');
  1980. switch (Event.which) {
  1981. // Return (do not submit form on pressing enter key in search field)
  1982. case $.ui.keyCode.ENTER:
  1983. Event.preventDefault();
  1984. Event.stopPropagation();
  1985. break;
  1986. // Tab
  1987. case $.ui.keyCode.TAB:
  1988. if (!Event.shiftKey) {
  1989. TabFocus = true;
  1990. }
  1991. Focused = null;
  1992. break;
  1993. // Escape
  1994. case $.ui.keyCode.ESCAPE:
  1995. Event.preventDefault();
  1996. $TreeObj.blur();
  1997. $SearchObj.blur();
  1998. break;
  1999. // ArrowUp
  2000. case $.ui.keyCode.UP:
  2001. Event.preventDefault();
  2002. $($TreeObj.find('a.jstree-anchor:visible')
  2003. .not('.jstree-disabled')
  2004. .last()
  2005. .get(0)).trigger('focus.jstree');
  2006. break;
  2007. // ArrowDown
  2008. case $.ui.keyCode.DOWN:
  2009. Event.preventDefault();
  2010. $($TreeObj.find('a.jstree-anchor:visible')
  2011. .not('.jstree-disabled')
  2012. .get(0)).trigger('focus.jstree');
  2013. break;
  2014. // Ctrl (Cmd) + A
  2015. case 65:
  2016. if (Event.ctrlKey || Event.metaKey) {
  2017. if (!Searching) {
  2018. Event.preventDefault();
  2019. $ListContainerObj.find('.InputField_SelectAll')
  2020. .click();
  2021. }
  2022. }
  2023. break;
  2024. // Ctrl (Cmd) + D
  2025. case 68:
  2026. if (Event.ctrlKey || Event.metaKey) {
  2027. Event.preventDefault();
  2028. $ListContainerObj.find('.InputField_ClearAll')
  2029. .click();
  2030. }
  2031. break;
  2032. // Ctrl (Cmd) + F
  2033. case 70:
  2034. if (Event.ctrlKey || Event.metaKey) {
  2035. Event.preventDefault();
  2036. $ListContainerObj.find('.InputField_Filters')
  2037. .click();
  2038. $ListContainerObj.find('.InputField_FiltersList').children('input').first().focus();
  2039. }
  2040. break;
  2041. }
  2042. })
  2043. // Close dropdown if search field has been removed from DOM (bug#12243)
  2044. .off('remove.InputField').on('remove.InputField', function () {
  2045. CloseOpenSelections();
  2046. });
  2047. // Handle custom redraw event on original select field
  2048. // to update values when changed via AJAX calls
  2049. $SelectObj.off('redraw.InputField').on('redraw.InputField', function () {
  2050. // redraw event is critical on hidden elements because
  2051. // e.g. chrome can't calculate the width of hidden elements correctly
  2052. // so we skip it for hidden elements
  2053. if (!$SearchObj.is(':visible')) return;
  2054. CloseOpenSelections();
  2055. if (Filterable) {
  2056. $SelectObj.data('original', $SelectObj.children());
  2057. if (
  2058. $SelectObj.data('filtered')
  2059. && $SelectObj.data('filtered') !== '0'
  2060. )
  2061. {
  2062. ApplyFilter($SelectObj, $ToolbarContainerObj);
  2063. }
  2064. }
  2065. CheckAvailability($SelectObj, $SearchObj, $InputContainerObj);
  2066. $SearchObj.width($SelectObj.outerWidth());
  2067. ShowSelectionBoxes($SelectObj, $InputContainerObj);
  2068. })
  2069. // Handle custom error event on original select field
  2070. // to add error classes to search field if needed
  2071. .off('error.InputField').on('error.InputField', function () {
  2072. if ($SelectObj.hasClass(Config.ErrorClass)) {
  2073. $SearchObj.addClass(Config.ErrorClass);
  2074. }
  2075. else {
  2076. $SearchObj.removeClass(Config.ErrorClass);
  2077. }
  2078. if ($SelectObj.hasClass(Config.ServerErrorClass)) {
  2079. $SearchObj.addClass(Config.ServerErrorClass);
  2080. }
  2081. else {
  2082. $SearchObj.removeClass(Config.ServerErrorClass);
  2083. }
  2084. });
  2085. }
  2086. });
  2087. // Workaround to close dropdown after blur event by clicking the mouse out of the search field
  2088. $('body').off('click.InputField').on('click.InputField', function () {
  2089. if (
  2090. $('.InputField_ListContainer').length
  2091. &&
  2092. (
  2093. !$(document.activeElement).parents('.InputField_Container').length
  2094. || document.activeElement.tagName.toUpperCase() !== 'INPUT'
  2095. )
  2096. ) {
  2097. CloseOpenSelections();
  2098. }
  2099. });
  2100. return true;
  2101. };
  2102. /**
  2103. * @name IsEnabled
  2104. * @memberof Core.UI.InputFields
  2105. * @function
  2106. * @returns {Boolean} True/false value depending whether the Input Field has been initialized.
  2107. * @param {jQueryObject} $Element - The jQuery object of the element that is being tested.
  2108. * @description
  2109. * This function check if Input Field is initialized for the supplied element,
  2110. * and returns appropriate boolean value.
  2111. */
  2112. TargetNS.IsEnabled = function ($Element) {
  2113. if ($Element.data('modernized') && $Element.data('modernized') !== '') {
  2114. return true;
  2115. }
  2116. return false;
  2117. };
  2118. // jsTree plugin for multi selection without modifier key
  2119. // Skip ESLint check below for no camelcase property, we are overriding an existing one!
  2120. $.jstree.defaults.multiselect = {};
  2121. $.jstree.plugins.multiselect = function (options, parent) {
  2122. this.activate_node = function (obj, e) { //eslint-disable-line camelcase
  2123. e.ctrlKey = true;
  2124. parent.activate_node.call(this, obj, e);
  2125. };
  2126. };
  2127. // Handle window resize event
  2128. $(window).on(Config.ResizeEvent + '.InputField', function () {
  2129. clearTimeout(Config.ResizeTimeout);
  2130. Config.ResizeTimeout = setTimeout(function () {
  2131. Core.App.Publish('Event.UI.InputFields.Resize');
  2132. }, 100);
  2133. });
  2134. Core.Init.RegisterNamespace(TargetNS, 'APP_GLOBAL');
  2135. return TargetNS;
  2136. }(Core.UI.InputFields || {}));

^ Use Elevator