defaults.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. define([
  2. 'jquery',
  3. './results',
  4. './selection/single',
  5. './selection/multiple',
  6. './selection/placeholder',
  7. './selection/allowClear',
  8. './selection/search',
  9. './selection/selectionCss',
  10. './selection/eventRelay',
  11. './utils',
  12. './translation',
  13. './diacritics',
  14. './data/select',
  15. './data/array',
  16. './data/ajax',
  17. './data/tags',
  18. './data/tokenizer',
  19. './data/minimumInputLength',
  20. './data/maximumInputLength',
  21. './data/maximumSelectionLength',
  22. './dropdown',
  23. './dropdown/search',
  24. './dropdown/hidePlaceholder',
  25. './dropdown/infiniteScroll',
  26. './dropdown/attachBody',
  27. './dropdown/minimumResultsForSearch',
  28. './dropdown/selectOnClose',
  29. './dropdown/closeOnSelect',
  30. './dropdown/dropdownCss',
  31. './i18n/en'
  32. ], function ($,
  33. ResultsList,
  34. SingleSelection, MultipleSelection, Placeholder, AllowClear,
  35. SelectionSearch, SelectionCSS, EventRelay,
  36. Utils, Translation, DIACRITICS,
  37. SelectData, ArrayData, AjaxData, Tags, Tokenizer,
  38. MinimumInputLength, MaximumInputLength, MaximumSelectionLength,
  39. Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
  40. AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect,
  41. DropdownCSS,
  42. EnglishTranslation) {
  43. function Defaults () {
  44. this.reset();
  45. }
  46. Defaults.prototype.apply = function (options) {
  47. options = $.extend(true, {}, this.defaults, options);
  48. if (options.dataAdapter == null) {
  49. if (options.ajax != null) {
  50. options.dataAdapter = AjaxData;
  51. } else if (options.data != null) {
  52. options.dataAdapter = ArrayData;
  53. } else {
  54. options.dataAdapter = SelectData;
  55. }
  56. if (options.minimumInputLength > 0) {
  57. options.dataAdapter = Utils.Decorate(
  58. options.dataAdapter,
  59. MinimumInputLength
  60. );
  61. }
  62. if (options.maximumInputLength > 0) {
  63. options.dataAdapter = Utils.Decorate(
  64. options.dataAdapter,
  65. MaximumInputLength
  66. );
  67. }
  68. if (options.maximumSelectionLength > 0) {
  69. options.dataAdapter = Utils.Decorate(
  70. options.dataAdapter,
  71. MaximumSelectionLength
  72. );
  73. }
  74. if (options.tags) {
  75. options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
  76. }
  77. if (options.tokenSeparators != null || options.tokenizer != null) {
  78. options.dataAdapter = Utils.Decorate(
  79. options.dataAdapter,
  80. Tokenizer
  81. );
  82. }
  83. }
  84. if (options.resultsAdapter == null) {
  85. options.resultsAdapter = ResultsList;
  86. if (options.ajax != null) {
  87. options.resultsAdapter = Utils.Decorate(
  88. options.resultsAdapter,
  89. InfiniteScroll
  90. );
  91. }
  92. if (options.placeholder != null) {
  93. options.resultsAdapter = Utils.Decorate(
  94. options.resultsAdapter,
  95. HidePlaceholder
  96. );
  97. }
  98. if (options.selectOnClose) {
  99. options.resultsAdapter = Utils.Decorate(
  100. options.resultsAdapter,
  101. SelectOnClose
  102. );
  103. }
  104. }
  105. if (options.dropdownAdapter == null) {
  106. if (options.multiple) {
  107. options.dropdownAdapter = Dropdown;
  108. } else {
  109. var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);
  110. options.dropdownAdapter = SearchableDropdown;
  111. }
  112. if (options.minimumResultsForSearch !== 0) {
  113. options.dropdownAdapter = Utils.Decorate(
  114. options.dropdownAdapter,
  115. MinimumResultsForSearch
  116. );
  117. }
  118. if (options.closeOnSelect) {
  119. options.dropdownAdapter = Utils.Decorate(
  120. options.dropdownAdapter,
  121. CloseOnSelect
  122. );
  123. }
  124. if (
  125. options.dropdownCssClass != null ||
  126. options.dropdownCss != null ||
  127. options.adaptDropdownCssClass != null
  128. ) {
  129. options.dropdownAdapter = Utils.Decorate(
  130. options.dropdownAdapter,
  131. DropdownCSS
  132. );
  133. }
  134. options.dropdownAdapter = Utils.Decorate(
  135. options.dropdownAdapter,
  136. AttachBody
  137. );
  138. }
  139. if (options.selectionAdapter == null) {
  140. if (options.multiple) {
  141. options.selectionAdapter = MultipleSelection;
  142. } else {
  143. options.selectionAdapter = SingleSelection;
  144. }
  145. // Add the placeholder mixin if a placeholder was specified
  146. if (options.placeholder != null) {
  147. options.selectionAdapter = Utils.Decorate(
  148. options.selectionAdapter,
  149. Placeholder
  150. );
  151. }
  152. if (options.allowClear) {
  153. options.selectionAdapter = Utils.Decorate(
  154. options.selectionAdapter,
  155. AllowClear
  156. );
  157. }
  158. if (options.multiple) {
  159. options.selectionAdapter = Utils.Decorate(
  160. options.selectionAdapter,
  161. SelectionSearch
  162. );
  163. }
  164. if (
  165. options.containerCssClass != null ||
  166. options.containerCss != null ||
  167. options.adaptContainerCssClass != null
  168. ) {
  169. options.selectionAdapter = Utils.Decorate(
  170. options.selectionAdapter,
  171. SelectionCSS
  172. );
  173. }
  174. options.selectionAdapter = Utils.Decorate(
  175. options.selectionAdapter,
  176. EventRelay
  177. );
  178. }
  179. // If the defaults were not previously applied from an element, it is
  180. // possible for the language option to have not been resolved
  181. options.language = this._resolveLanguage(options.language);
  182. // Always fall back to English since it will always be complete
  183. options.language.push('en');
  184. var uniqueLanguages = [];
  185. for (var l = 0; l < options.language.length; l++) {
  186. var language = options.language[l];
  187. if (uniqueLanguages.indexOf(language) === -1) {
  188. uniqueLanguages.push(language);
  189. }
  190. }
  191. options.language = uniqueLanguages;
  192. options.translations = this._processTranslations(
  193. options.language,
  194. options.debug
  195. );
  196. return options;
  197. };
  198. Defaults.prototype.reset = function () {
  199. function stripDiacritics (text) {
  200. // Used 'uni range + named function' from http://jsperf.com/diacritics/18
  201. function match(a) {
  202. return DIACRITICS[a] || a;
  203. }
  204. return text.replace(/[^\u0000-\u007E]/g, match);
  205. }
  206. function matcher (params, data) {
  207. // Always return the object if there is nothing to compare
  208. if (params.term == null || params.term.trim() === '') {
  209. return data;
  210. }
  211. // Do a recursive check for options with children
  212. if (data.children && data.children.length > 0) {
  213. // Clone the data object if there are children
  214. // This is required as we modify the object to remove any non-matches
  215. var match = $.extend(true, {}, data);
  216. // Check each child of the option
  217. for (var c = data.children.length - 1; c >= 0; c--) {
  218. var child = data.children[c];
  219. var matches = matcher(params, child);
  220. // If there wasn't a match, remove the object in the array
  221. if (matches == null) {
  222. match.children.splice(c, 1);
  223. }
  224. }
  225. // If any children matched, return the new object
  226. if (match.children.length > 0) {
  227. return match;
  228. }
  229. // If there were no matching children, check just the plain object
  230. return matcher(params, match);
  231. }
  232. var original = stripDiacritics(data.text).toUpperCase();
  233. var term = stripDiacritics(params.term).toUpperCase();
  234. // Check if the text contains the term
  235. if (original.indexOf(term) > -1) {
  236. return data;
  237. }
  238. // If it doesn't contain the term, don't return anything
  239. return null;
  240. }
  241. this.defaults = {
  242. amdLanguageBase: './i18n/',
  243. closeOnSelect: true,
  244. debug: false,
  245. dropdownAutoWidth: false,
  246. escapeMarkup: Utils.escapeMarkup,
  247. language: {},
  248. matcher: matcher,
  249. minimumInputLength: 0,
  250. maximumInputLength: 0,
  251. maximumSelectionLength: 0,
  252. minimumResultsForSearch: 0,
  253. selectOnClose: false,
  254. scrollAfterSelect: false,
  255. sorter: function (data) {
  256. return data;
  257. },
  258. templateResult: function (result) {
  259. return result.text;
  260. },
  261. templateSelection: function (selection) {
  262. return selection.text;
  263. },
  264. theme: 'default',
  265. width: 'resolve'
  266. };
  267. };
  268. Defaults.prototype.applyFromElement = function (options, $element) {
  269. var optionLanguage = options.language;
  270. var defaultLanguage = this.defaults.language;
  271. var elementLanguage = $element.prop('lang');
  272. var parentLanguage = $element.closest('[lang]').prop('lang');
  273. var languages = Array.prototype.concat.call(
  274. this._resolveLanguage(elementLanguage),
  275. this._resolveLanguage(optionLanguage),
  276. this._resolveLanguage(defaultLanguage),
  277. this._resolveLanguage(parentLanguage)
  278. );
  279. options.language = languages;
  280. return options;
  281. };
  282. Defaults.prototype._resolveLanguage = function (language) {
  283. if (!language) {
  284. return [];
  285. }
  286. if ($.isEmptyObject(language)) {
  287. return [];
  288. }
  289. if ($.isPlainObject(language)) {
  290. return [language];
  291. }
  292. var languages;
  293. if (!Array.isArray(language)) {
  294. languages = [language];
  295. } else {
  296. languages = language;
  297. }
  298. var resolvedLanguages = [];
  299. for (var l = 0; l < languages.length; l++) {
  300. resolvedLanguages.push(languages[l]);
  301. if (typeof languages[l] === 'string' && languages[l].indexOf('-') > 0) {
  302. // Extract the region information if it is included
  303. var languageParts = languages[l].split('-');
  304. var baseLanguage = languageParts[0];
  305. resolvedLanguages.push(baseLanguage);
  306. }
  307. }
  308. return resolvedLanguages;
  309. };
  310. Defaults.prototype._processTranslations = function (languages, debug) {
  311. var translations = new Translation();
  312. for (var l = 0; l < languages.length; l++) {
  313. var languageData = new Translation();
  314. var language = languages[l];
  315. if (typeof language === 'string') {
  316. try {
  317. // Try to load it with the original name
  318. languageData = Translation.loadPath(language);
  319. } catch (e) {
  320. try {
  321. // If we couldn't load it, check if it wasn't the full path
  322. language = this.defaults.amdLanguageBase + language;
  323. languageData = Translation.loadPath(language);
  324. } catch (ex) {
  325. // The translation could not be loaded at all. Sometimes this is
  326. // because of a configuration problem, other times this can be
  327. // because of how Select2 helps load all possible translation files
  328. if (debug && window.console && console.warn) {
  329. console.warn(
  330. 'Select2: The language file for "' + language + '" could ' +
  331. 'not be automatically loaded. A fallback will be used instead.'
  332. );
  333. }
  334. }
  335. }
  336. } else if ($.isPlainObject(language)) {
  337. languageData = new Translation(language);
  338. } else {
  339. languageData = language;
  340. }
  341. translations.extend(languageData);
  342. }
  343. return translations;
  344. };
  345. Defaults.prototype.set = function (key, value) {
  346. var camelKey = $.camelCase(key);
  347. var data = {};
  348. data[camelKey] = value;
  349. var convertedData = Utils._convertData(data);
  350. $.extend(true, this.defaults, convertedData);
  351. };
  352. var defaults = new Defaults();
  353. return defaults;
  354. });