jquery.combobox.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708
  1. /**
  2. * jQuery EasyUI 1.5.2
  3. *
  4. * Copyright (c) 2009-2017 www.jeasyui.com. All rights reserved.
  5. *
  6. * Licensed under the freeware license: http://www.jeasyui.com/license_freeware.php
  7. * To use it on other terms please contact us: info@jeasyui.com
  8. *
  9. */
  10. /**
  11. * combobox - jQuery EasyUI
  12. *
  13. * Dependencies:
  14. * combo
  15. *
  16. */
  17. (function($){
  18. function getRowIndex(target, value){
  19. var state = $.data(target, 'combobox');
  20. return $.easyui.indexOfArray(state.data, state.options.valueField, value);
  21. }
  22. /**
  23. * scroll panel to display the specified item
  24. */
  25. function scrollTo(target, value){
  26. var opts = $.data(target, 'combobox').options;
  27. var panel = $(target).combo('panel');
  28. var item = opts.finder.getEl(target, value);
  29. if (item.length){
  30. if (item.position().top <= 0){
  31. var h = panel.scrollTop() + item.position().top;
  32. panel.scrollTop(h);
  33. } else if (item.position().top + item.outerHeight() > panel.height()){
  34. var h = panel.scrollTop() + item.position().top + item.outerHeight() - panel.height();
  35. panel.scrollTop(h);
  36. }
  37. }
  38. panel.triggerHandler('scroll'); // trigger the group sticking
  39. }
  40. function nav(target, dir){
  41. var opts = $.data(target, 'combobox').options;
  42. var panel = $(target).combobox('panel');
  43. var item = panel.children('div.combobox-item-hover');
  44. if (!item.length){
  45. item = panel.children('div.combobox-item-selected');
  46. }
  47. item.removeClass('combobox-item-hover');
  48. var firstSelector = 'div.combobox-item:visible:not(.combobox-item-disabled):first';
  49. var lastSelector = 'div.combobox-item:visible:not(.combobox-item-disabled):last';
  50. if (!item.length){
  51. item = panel.children(dir=='next' ? firstSelector : lastSelector);
  52. } else {
  53. if (dir == 'next'){
  54. item = item.nextAll(firstSelector);
  55. if (!item.length){
  56. item = panel.children(firstSelector);
  57. }
  58. } else {
  59. item = item.prevAll(firstSelector);
  60. if (!item.length){
  61. item = panel.children(lastSelector);
  62. }
  63. }
  64. }
  65. if (item.length){
  66. item.addClass('combobox-item-hover');
  67. var row = opts.finder.getRow(target, item);
  68. if (row){
  69. $(target).combobox('scrollTo', row[opts.valueField]);
  70. if (opts.selectOnNavigation){
  71. select(target, row[opts.valueField]);
  72. }
  73. }
  74. }
  75. }
  76. /**
  77. * select the specified value
  78. */
  79. function select(target, value, remainText){
  80. var opts = $.data(target, 'combobox').options;
  81. var values = $(target).combo('getValues');
  82. if ($.inArray(value+'', values) == -1){
  83. if (opts.multiple){
  84. values.push(value);
  85. } else {
  86. values = [value];
  87. }
  88. setValues(target, values, remainText);
  89. }
  90. }
  91. /**
  92. * unselect the specified value
  93. */
  94. function unselect(target, value){
  95. var opts = $.data(target, 'combobox').options;
  96. var values = $(target).combo('getValues');
  97. var index = $.inArray(value+'', values);
  98. if (index >= 0){
  99. values.splice(index, 1);
  100. setValues(target, values);
  101. }
  102. }
  103. /**
  104. * set values
  105. */
  106. function setValues(target, values, remainText){
  107. var opts = $.data(target, 'combobox').options;
  108. var panel = $(target).combo('panel');
  109. if (!$.isArray(values)){
  110. values = values.split(opts.separator);
  111. }
  112. if (!opts.multiple){
  113. values = values.length ? [values[0]] : [''];
  114. }
  115. // unselect the old rows
  116. var oldValues = $(target).combo('getValues');
  117. if (panel.is(':visible')){
  118. panel.find('.combobox-item-selected').each(function(){
  119. var row = opts.finder.getRow(target, $(this));
  120. if (row){
  121. if ($.easyui.indexOfArray(oldValues, row[opts.valueField]) == -1){
  122. $(this).removeClass('combobox-item-selected');
  123. }
  124. }
  125. });
  126. }
  127. $.map(oldValues, function(v){
  128. if ($.easyui.indexOfArray(values, v) == -1){
  129. var el = opts.finder.getEl(target, v);
  130. if (el.hasClass('combobox-item-selected')){
  131. el.removeClass('combobox-item-selected');
  132. opts.onUnselect.call(target, opts.finder.getRow(target, v));
  133. }
  134. }
  135. });
  136. var theRow = null;
  137. var vv = [], ss = [];
  138. for(var i=0; i<values.length; i++){
  139. var v = values[i];
  140. var s = v;
  141. var row = opts.finder.getRow(target, v);
  142. if (row){
  143. s = row[opts.textField];
  144. theRow = row;
  145. var el = opts.finder.getEl(target, v);
  146. if (!el.hasClass('combobox-item-selected')){
  147. el.addClass('combobox-item-selected');
  148. opts.onSelect.call(target, row);
  149. }
  150. }
  151. vv.push(v);
  152. ss.push(s);
  153. }
  154. if (!remainText){
  155. $(target).combo('setText', ss.join(opts.separator));
  156. }
  157. if (opts.showItemIcon){
  158. var tb = $(target).combobox('textbox');
  159. tb.removeClass('textbox-bgicon ' + opts.textboxIconCls);
  160. if (theRow && theRow.iconCls){
  161. tb.addClass('textbox-bgicon ' + theRow.iconCls);
  162. opts.textboxIconCls = theRow.iconCls;
  163. }
  164. }
  165. $(target).combo('setValues', vv);
  166. panel.triggerHandler('scroll'); // trigger the group sticking
  167. }
  168. /**
  169. * load data, the old list items will be removed.
  170. */
  171. function loadData(target, data, remainText){
  172. var state = $.data(target, 'combobox');
  173. var opts = state.options;
  174. state.data = opts.loadFilter.call(target, data);
  175. opts.view.render.call(opts.view, target, $(target).combo('panel'), state.data);
  176. var vv = $(target).combobox('getValues');
  177. $.easyui.forEach(state.data, false, function(row){
  178. if (row['selected']){
  179. $.easyui.addArrayItem(vv, row[opts.valueField]+'');
  180. }
  181. });
  182. if (opts.multiple){
  183. setValues(target, vv, remainText);
  184. } else {
  185. setValues(target, vv.length ? [vv[vv.length-1]] : [], remainText);
  186. }
  187. opts.onLoadSuccess.call(target, data);
  188. }
  189. /**
  190. * request remote data if the url property is setted.
  191. */
  192. function request(target, url, param, remainText){
  193. var opts = $.data(target, 'combobox').options;
  194. if (url){
  195. opts.url = url;
  196. }
  197. param = $.extend({}, opts.queryParams, param||{});
  198. // param = param || {};
  199. if (opts.onBeforeLoad.call(target, param) == false) return;
  200. opts.loader.call(target, param, function(data){
  201. loadData(target, data, remainText);
  202. }, function(){
  203. opts.onLoadError.apply(this, arguments);
  204. });
  205. }
  206. /**
  207. * do the query action
  208. */
  209. function doQuery(target, q){
  210. var state = $.data(target, 'combobox');
  211. var opts = state.options;
  212. var highlightItem = $();
  213. var qq = opts.multiple ? q.split(opts.separator) : [q];
  214. if (opts.mode == 'remote'){
  215. _setValues(qq);
  216. request(target, null, {q:q}, true);
  217. } else {
  218. var panel = $(target).combo('panel');
  219. panel.find('.combobox-item-hover').removeClass('combobox-item-hover');
  220. panel.find('.combobox-item,.combobox-group').hide();
  221. var data = state.data;
  222. var vv = [];
  223. $.map(qq, function(q){
  224. q = $.trim(q);
  225. var value = q;
  226. var group = undefined;
  227. highlightItem = $();
  228. for(var i=0; i<data.length; i++){
  229. var row = data[i];
  230. if (opts.filter.call(target, q, row)){
  231. var v = row[opts.valueField];
  232. var s = row[opts.textField];
  233. var g = row[opts.groupField];
  234. var item = opts.finder.getEl(target, v).show();
  235. if (s.toLowerCase() == q.toLowerCase()){
  236. value = v;
  237. if (opts.reversed){
  238. highlightItem = item;
  239. } else {
  240. select(target, v, true);
  241. }
  242. }
  243. if (opts.groupField && group != g){
  244. opts.finder.getGroupEl(target, g).show();
  245. group = g;
  246. }
  247. }
  248. }
  249. vv.push(value);
  250. });
  251. _setValues(vv);
  252. }
  253. function _setValues(vv){
  254. if (opts.reversed){
  255. highlightItem.addClass('combobox-item-hover');
  256. } else {
  257. setValues(target, opts.multiple ? (q?vv:[]) : vv, true);
  258. }
  259. }
  260. }
  261. function doEnter(target){
  262. var t = $(target);
  263. var opts = t.combobox('options');
  264. var panel = t.combobox('panel');
  265. var item = panel.children('div.combobox-item-hover');
  266. if (item.length){
  267. item.removeClass('combobox-item-hover');
  268. var row = opts.finder.getRow(target, item);
  269. var value = row[opts.valueField];
  270. if (opts.multiple){
  271. if (item.hasClass('combobox-item-selected')){
  272. t.combobox('unselect', value);
  273. } else {
  274. t.combobox('select', value);
  275. }
  276. } else {
  277. t.combobox('select', value);
  278. }
  279. }
  280. var vv = [];
  281. $.map(t.combobox('getValues'), function(v){
  282. if (getRowIndex(target, v) >= 0){
  283. vv.push(v);
  284. }
  285. });
  286. t.combobox('setValues', vv);
  287. if (!opts.multiple){
  288. t.combobox('hidePanel');
  289. }
  290. }
  291. /**
  292. * create the component
  293. */
  294. function create(target){
  295. var state = $.data(target, 'combobox');
  296. var opts = state.options;
  297. $(target).addClass('combobox-f');
  298. $(target).combo($.extend({}, opts, {
  299. onShowPanel: function(){
  300. $(this).combo('panel').find('div.combobox-item:hidden,div.combobox-group:hidden').show();
  301. setValues(this, $(this).combobox('getValues'), true);
  302. $(this).combobox('scrollTo', $(this).combobox('getValue'));
  303. opts.onShowPanel.call(this);
  304. }
  305. }));
  306. var p = $(target).combo('panel');
  307. p.unbind('.combobox');
  308. for(var event in opts.panelEvents){
  309. p.bind(event+'.combobox', {target:target}, opts.panelEvents[event]);
  310. }
  311. }
  312. function mouseoverHandler(e){
  313. $(this).children('div.combobox-item-hover').removeClass('combobox-item-hover');
  314. var item = $(e.target).closest('div.combobox-item');
  315. if (!item.hasClass('combobox-item-disabled')){
  316. item.addClass('combobox-item-hover');
  317. }
  318. e.stopPropagation();
  319. }
  320. function mouseoutHandler(e){
  321. $(e.target).closest('div.combobox-item').removeClass('combobox-item-hover');
  322. e.stopPropagation();
  323. }
  324. function clickHandler(e){
  325. var target = $(this).panel('options').comboTarget;
  326. if (!target){return;}
  327. var opts = $(target).combobox('options');
  328. var item = $(e.target).closest('div.combobox-item');
  329. if (!item.length || item.hasClass('combobox-item-disabled')){return}
  330. var row = opts.finder.getRow(target, item);
  331. if (!row){return;}
  332. if (opts.blurTimer){
  333. clearTimeout(opts.blurTimer);
  334. opts.blurTimer = null;
  335. }
  336. opts.onClick.call(target, row);
  337. var value = row[opts.valueField];
  338. if (opts.multiple){
  339. if (item.hasClass('combobox-item-selected')){
  340. unselect(target, value);
  341. } else {
  342. select(target, value);
  343. }
  344. } else {
  345. $(target).combobox('setValue', value).combobox('hidePanel');
  346. }
  347. e.stopPropagation();
  348. }
  349. function scrollHandler(e){
  350. var target = $(this).panel('options').comboTarget;
  351. if (!target){return;}
  352. var opts = $(target).combobox('options');
  353. if (opts.groupPosition == 'sticky'){
  354. var stick = $(this).children('.combobox-stick');
  355. if (!stick.length){
  356. stick = $('<div class="combobox-stick"></div>').appendTo(this);
  357. }
  358. stick.hide();
  359. var state = $(target).data('combobox');
  360. $(this).children('.combobox-group:visible').each(function(){
  361. var g = $(this);
  362. var groupData = opts.finder.getGroup(target, g);
  363. var rowData = state.data[groupData.startIndex + groupData.count - 1];
  364. var last = opts.finder.getEl(target, rowData[opts.valueField]);
  365. if (g.position().top < 0 && last.position().top > 0){
  366. stick.show().html(g.html());
  367. return false;
  368. }
  369. });
  370. }
  371. }
  372. $.fn.combobox = function(options, param){
  373. if (typeof options == 'string'){
  374. var method = $.fn.combobox.methods[options];
  375. if (method){
  376. return method(this, param);
  377. } else {
  378. return this.combo(options, param);
  379. }
  380. }
  381. options = options || {};
  382. return this.each(function(){
  383. var state = $.data(this, 'combobox');
  384. if (state){
  385. $.extend(state.options, options);
  386. } else {
  387. state = $.data(this, 'combobox', {
  388. options: $.extend({}, $.fn.combobox.defaults, $.fn.combobox.parseOptions(this), options),
  389. data: []
  390. });
  391. }
  392. create(this);
  393. if (state.options.data){
  394. loadData(this, state.options.data);
  395. } else {
  396. var data = $.fn.combobox.parseData(this);
  397. if (data.length){
  398. loadData(this, data);
  399. }
  400. }
  401. request(this);
  402. });
  403. };
  404. $.fn.combobox.methods = {
  405. options: function(jq){
  406. var copts = jq.combo('options');
  407. return $.extend($.data(jq[0], 'combobox').options, {
  408. width: copts.width,
  409. height: copts.height,
  410. originalValue: copts.originalValue,
  411. disabled: copts.disabled,
  412. readonly: copts.readonly
  413. });
  414. },
  415. cloneFrom: function(jq, from){
  416. return jq.each(function(){
  417. $(this).combo('cloneFrom', from);
  418. $.data(this, 'combobox', $(from).data('combobox'));
  419. $(this).addClass('combobox-f').attr('comboboxName', $(this).attr('textboxName'));
  420. });
  421. },
  422. getData: function(jq){
  423. return $.data(jq[0], 'combobox').data;
  424. },
  425. setValues: function(jq, values){
  426. return jq.each(function(){
  427. setValues(this, values);
  428. });
  429. },
  430. setValue: function(jq, value){
  431. return jq.each(function(){
  432. setValues(this, $.isArray(value)?value:[value]);
  433. });
  434. },
  435. clear: function(jq){
  436. return jq.each(function(){
  437. setValues(this, []);
  438. });
  439. },
  440. reset: function(jq){
  441. return jq.each(function(){
  442. var opts = $(this).combobox('options');
  443. if (opts.multiple){
  444. $(this).combobox('setValues', opts.originalValue);
  445. } else {
  446. $(this).combobox('setValue', opts.originalValue);
  447. }
  448. });
  449. },
  450. loadData: function(jq, data){
  451. return jq.each(function(){
  452. loadData(this, data);
  453. });
  454. },
  455. reload: function(jq, url){
  456. return jq.each(function(){
  457. if (typeof url == 'string'){
  458. request(this, url);
  459. } else {
  460. if (url){
  461. var opts = $(this).combobox('options');
  462. opts.queryParams = url;
  463. }
  464. request(this);
  465. }
  466. });
  467. },
  468. select: function(jq, value){
  469. return jq.each(function(){
  470. select(this, value);
  471. });
  472. },
  473. unselect: function(jq, value){
  474. return jq.each(function(){
  475. unselect(this, value);
  476. });
  477. },
  478. scrollTo: function(jq, value){
  479. return jq.each(function(){
  480. scrollTo(this, value);
  481. });
  482. }
  483. };
  484. $.fn.combobox.parseOptions = function(target){
  485. var t = $(target);
  486. return $.extend({}, $.fn.combo.parseOptions(target), $.parser.parseOptions(target,[
  487. 'valueField','textField','groupField','groupPosition','mode','method','url',
  488. {showItemIcon:'boolean',limitToList:'boolean'}
  489. ]));
  490. };
  491. $.fn.combobox.parseData = function(target){
  492. var data = [];
  493. var opts = $(target).combobox('options');
  494. $(target).children().each(function(){
  495. if (this.tagName.toLowerCase() == 'optgroup'){
  496. var group = $(this).attr('label');
  497. $(this).children().each(function(){
  498. _parseItem(this, group);
  499. });
  500. } else {
  501. _parseItem(this);
  502. }
  503. });
  504. return data;
  505. function _parseItem(el, group){
  506. var t = $(el);
  507. var row = {};
  508. row[opts.valueField] = t.attr('value')!=undefined ? t.attr('value') : t.text();
  509. row[opts.textField] = t.text();
  510. row['selected'] = t.is(':selected');
  511. row['disabled'] = t.is(':disabled');
  512. if (group){
  513. opts.groupField = opts.groupField || 'group';
  514. row[opts.groupField] = group;
  515. }
  516. data.push(row);
  517. }
  518. };
  519. var COMBOBOX_SERNO = 0;
  520. var defaultView = {
  521. render: function(target, container, data){
  522. var state = $.data(target, 'combobox');
  523. var opts = state.options;
  524. COMBOBOX_SERNO++;
  525. state.itemIdPrefix = '_easyui_combobox_i' + COMBOBOX_SERNO;
  526. state.groupIdPrefix = '_easyui_combobox_g' + COMBOBOX_SERNO;
  527. state.groups = [];
  528. var dd = [];
  529. var group = undefined;
  530. for(var i=0; i<data.length; i++){
  531. var row = data[i];
  532. var v = row[opts.valueField]+'';
  533. var s = row[opts.textField];
  534. var g = row[opts.groupField];
  535. if (g){
  536. if (group != g){
  537. group = g;
  538. state.groups.push({
  539. value: g,
  540. startIndex: i,
  541. count: 1
  542. });
  543. dd.push('<div id="' + (state.groupIdPrefix+'_'+(state.groups.length-1)) + '" class="combobox-group">');
  544. dd.push(opts.groupFormatter ? opts.groupFormatter.call(target, g) : g);
  545. dd.push('</div>');
  546. } else {
  547. state.groups[state.groups.length-1].count++;
  548. }
  549. } else {
  550. group = undefined;
  551. }
  552. var cls = 'combobox-item' + (row.disabled ? ' combobox-item-disabled' : '') + (g ? ' combobox-gitem' : '');
  553. dd.push('<div id="' + (state.itemIdPrefix+'_'+i) + '" class="' + cls + '">');
  554. if (opts.showItemIcon && row.iconCls){
  555. dd.push('<span class="combobox-icon ' + row.iconCls + '"></span>');
  556. }
  557. dd.push(opts.formatter ? opts.formatter.call(target, row) : s);
  558. dd.push('</div>');
  559. }
  560. $(container).html(dd.join(''));
  561. }
  562. };
  563. $.fn.combobox.defaults = $.extend({}, $.fn.combo.defaults, {
  564. valueField: 'value',
  565. textField: 'text',
  566. groupPosition: 'static', // or 'sticky'
  567. groupField: null,
  568. groupFormatter: function(group){return group;},
  569. mode: 'local', // or 'remote'
  570. method: 'post',
  571. url: null,
  572. data: null,
  573. queryParams: {},
  574. showItemIcon: false,
  575. limitToList: false, // limit the inputed values to the listed items
  576. view: defaultView,
  577. keyHandler: {
  578. up: function(e){nav(this,'prev');e.preventDefault()},
  579. down: function(e){nav(this,'next');e.preventDefault()},
  580. left: function(e){},
  581. right: function(e){},
  582. enter: function(e){doEnter(this)},
  583. query: function(q,e){doQuery(this, q)}
  584. },
  585. inputEvents: $.extend({}, $.fn.combo.defaults.inputEvents, {
  586. blur: function(e){
  587. var target = e.data.target;
  588. var opts = $(target).combobox('options');
  589. if (opts.reversed || opts.limitToList){
  590. if (opts.blurTimer){
  591. clearTimeout(opts.blurTimer);
  592. }
  593. opts.blurTimer = setTimeout(function(){
  594. var existing = $(target).parent().length;
  595. if (existing){
  596. if (opts.reversed){
  597. $(target).combobox('setValues', $(target).combobox('getValues'));
  598. } else if (opts.limitToList){
  599. doEnter(target);
  600. }
  601. opts.blurTimer = null;
  602. }
  603. },50);
  604. }
  605. }
  606. }),
  607. panelEvents: {
  608. mouseover: mouseoverHandler,
  609. mouseout: mouseoutHandler,
  610. click: clickHandler,
  611. scroll: scrollHandler
  612. },
  613. filter: function(q, row){
  614. var opts = $(this).combobox('options');
  615. return row[opts.textField].toLowerCase().indexOf(q.toLowerCase()) >= 0;
  616. },
  617. formatter: function(row){
  618. var opts = $(this).combobox('options');
  619. return row[opts.textField];
  620. },
  621. loader: function(param, success, error){
  622. var opts = $(this).combobox('options');
  623. if (!opts.url) return false;
  624. $.ajax({
  625. type: opts.method,
  626. url: opts.url,
  627. data: param,
  628. dataType: 'json',
  629. success: function(data){
  630. success(data);
  631. },
  632. error: function(){
  633. error.apply(this, arguments);
  634. }
  635. });
  636. },
  637. loadFilter: function(data){
  638. return data;
  639. },
  640. finder:{
  641. getEl:function(target, value){
  642. var index = getRowIndex(target, value);
  643. var id = $.data(target, 'combobox').itemIdPrefix + '_' + index;
  644. return $('#'+id);
  645. },
  646. getGroupEl:function(target, gvalue){
  647. var state = $.data(target, 'combobox');
  648. var index = $.easyui.indexOfArray(state.groups, 'value', gvalue);
  649. var id = state.groupIdPrefix + '_' + index;
  650. return $('#'+id);
  651. },
  652. getGroup:function(target, p){
  653. var state = $.data(target, 'combobox');
  654. var index = p.attr('id').substr(state.groupIdPrefix.length+1);
  655. return state.groups[parseInt(index)];
  656. },
  657. getRow:function(target, p){
  658. var state = $.data(target, 'combobox');
  659. var index = (p instanceof $) ? p.attr('id').substr(state.itemIdPrefix.length+1) : getRowIndex(target, p);
  660. return state.data[parseInt(index)];
  661. }
  662. },
  663. onBeforeLoad: function(param){},
  664. onLoadSuccess: function(data){},
  665. onLoadError: function(){},
  666. onSelect: function(record){},
  667. onUnselect: function(record){},
  668. onClick: function(record){}
  669. });
  670. })(jQuery);