selectpage.js 72 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260
  1. /**
  2. * @summary SelectPage
  3. * @desc Simple and powerful selection plugin
  4. * @file selectpage.js
  5. * @version 2.19
  6. * @author TerryZeng
  7. * @contact https://terryz.github.io/
  8. * @license MIT License
  9. *
  10. */
  11. ;(function($){
  12. "use strict";
  13. /**
  14. * Default options
  15. */
  16. var defaults = {
  17. /**
  18. * Data source
  19. * @type {string|Object}
  20. *
  21. * string:server side request url address
  22. * Object:JSON array,format:[{a:1,b:2,c:3},{...}]
  23. */
  24. data: undefined,
  25. /**
  26. * Language ('cn', 'en', 'ja', 'es', 'pt-br')
  27. * @type string
  28. * @default 'cn'
  29. */
  30. lang: 'cn',
  31. /**
  32. * Multiple select mode(tags)
  33. * @type boolean
  34. * @default false
  35. */
  36. multiple: false,
  37. /**
  38. * pagination or not
  39. * @type boolean
  40. * @default true
  41. */
  42. pagination: true,
  43. /**
  44. * Show up menu button
  45. * @type boolean
  46. * @default true
  47. */
  48. dropButton: true,
  49. /**
  50. * Result list visible size in pagination bar close
  51. * @type number
  52. * @default 10
  53. */
  54. listSize : 10,
  55. /**
  56. * Show control bar in multiple select mode
  57. * @type boolean
  58. * @default true
  59. */
  60. multipleControlbar: true,
  61. /**
  62. * Max selected item limited in multiple select mode
  63. * @type number
  64. * @default 0(unlimited)
  65. */
  66. maxSelectLimit: 0,
  67. /**
  68. * Select result item to close list, work on multiple select mode
  69. * @type boolean
  70. * @default false
  71. */
  72. selectToCloseList: false,
  73. /**
  74. * Init selected item key, the result will match to option.keyField option
  75. * @type string
  76. */
  77. initRecord: undefined,
  78. /**
  79. * The table parameter in server side mode
  80. * @type string
  81. */
  82. dbTable: 'tbl',
  83. /**
  84. * The value field, the value will fill to hidden element
  85. * @type string
  86. * @default 'id'
  87. */
  88. keyField: 'id',
  89. /**
  90. * The show text field, the text will show to input element or tags(multiple mode)
  91. * @type string
  92. * @default 'name'
  93. */
  94. showField: 'name',
  95. /**
  96. * Actually used to search field
  97. * @type string
  98. */
  99. searchField : undefined,
  100. /**
  101. * Search type ('AND' or 'OR')
  102. * @type string
  103. * @default 'AND'
  104. */
  105. andOr: 'AND',
  106. /**
  107. * Result sort type
  108. * @type array|boolean
  109. * @example
  110. * orderBy : ['id desc']
  111. */
  112. orderBy: false,
  113. /**
  114. * Page size
  115. * @type number
  116. * @default 10
  117. */
  118. pageSize: 10,
  119. /**
  120. * Server side request parameters
  121. * @type function
  122. * @return object
  123. * @example params : function(){return {'name':'aa','sex':1};}
  124. */
  125. params : undefined,
  126. /**
  127. * Custom result list item show text
  128. * @type function
  129. * @param data {object} row data
  130. * @return string
  131. */
  132. formatItem : undefined,
  133. /**
  134. * Have some highlight item and lost focus, auto select the highlight item
  135. * @type boolean
  136. * @default false
  137. */
  138. autoFillResult: false,
  139. /**
  140. * Auto select first item in show up result list or search result
  141. * depend on `autoFillResult` option set to true
  142. * @type boolean
  143. * @default false
  144. */
  145. autoSelectFirst: false,
  146. /**
  147. * Whether clear input element text when enter some keywords to search and no result return
  148. * @type boolean
  149. * @default true
  150. */
  151. noResultClean: true,
  152. /**
  153. * Select only mode
  154. * @type boolean
  155. */
  156. selectOnly: false,
  157. /**
  158. * Input to search delay time, work on ajax data source
  159. * @type number
  160. * @default 0.5
  161. */
  162. inputDelay: 0.5,
  163. /**
  164. * -----------------------------------------Callback--------------------------------------------
  165. */
  166. /**
  167. * Result list item selected callback
  168. * @type function
  169. * @param object - selected item json data
  170. * @param self - plugin object
  171. */
  172. eSelect : undefined,
  173. /**
  174. * Before result list show up callback, you can do anything prepared
  175. * @param self - plugin object
  176. */
  177. eOpen : undefined,
  178. /**
  179. * Server side return data convert callback
  180. * @type function
  181. * @param data {object} server side return data
  182. * @param self {object} plugin object
  183. * @return {object} return data format:
  184. * @example
  185. * {
  186. * list : [{name:'aa',sex:1},{name:'bb',sex:1}...],
  187. * totalRow : 100
  188. * }
  189. */
  190. eAjaxSuccess : undefined,
  191. /**
  192. * Close selected item tag callback (multiple mode)
  193. * @type function
  194. * @param removeCount {number} remove item count
  195. * @param self {object} plugin object
  196. */
  197. eTagRemove : undefined,
  198. /**
  199. * Clear selected item callback(single select mode)
  200. * @type function
  201. * @param self {object} plugin object
  202. */
  203. eClear : undefined
  204. };
  205. /**
  206. * SelectPage class definition
  207. * @constructor
  208. * @param {Object} input - input element
  209. * @param {Object} option
  210. */
  211. var SelectPage = function(input, option) {
  212. this.setOption(option);
  213. this.setLanguage();
  214. this.setCssClass();
  215. this.setProp();
  216. this.setElem(input);
  217. this.setButtonAttrDefault();
  218. this.setInitRecord();
  219. this.eDropdownButton();
  220. this.eInput();
  221. this.eWhole();
  222. };
  223. /**
  224. * Plugin version number
  225. */
  226. SelectPage.version = '2.19';
  227. /**
  228. * Plugin object cache key
  229. */
  230. SelectPage.dataKey = 'selectPageObject';
  231. /**
  232. * Options set
  233. * @param {Object} option
  234. */
  235. SelectPage.prototype.setOption = function(option) {
  236. //use showField to default
  237. option.searchField = option.searchField || option.showField;
  238. option.andOr = option.andOr.toUpperCase();
  239. if(option.andOr!=='AND' && option.andOr!=='OR') option.andOr = 'AND';
  240. //support multiple field set
  241. var arr = ['searchField'];
  242. for (var i = 0; i < arr.length; i++) {
  243. option[arr[i]] = this.strToArray(option[arr[i]]);
  244. }
  245. //set multiple order field
  246. //example: [ ['id ASC'], ['name DESC'] ]
  247. if(option.orderBy !== false) option.orderBy = this.setOrderbyOption(option.orderBy, option.showField);
  248. //close auto fill result and auto select first in multiple mode and select item not close list
  249. if(option.multiple && !option.selectToCloseList){
  250. option.autoFillResult = false;
  251. option.autoSelectFirst = false;
  252. }
  253. //show all item when pagination bar close, limited 200
  254. if(!option.pagination) option.pageSize = 200;
  255. if($.type(option.listSize) !== 'number' || option.listSize < 0) option.listSize = 10;
  256. this.option = option;
  257. };
  258. /**
  259. * String convert to array
  260. * @param str {string}
  261. * @return {Array}
  262. */
  263. SelectPage.prototype.strToArray = function(str) {
  264. return str ? str.replace(/[\s ]+/g, '').split(',') : '';
  265. };
  266. /**
  267. * Set order field
  268. * @param {Array} arg_order
  269. * @param {string} arg_field - default sort field
  270. * @return {Array}
  271. */
  272. SelectPage.prototype.setOrderbyOption = function(arg_order, arg_field) {
  273. var arr = [],orders = [];
  274. if (typeof arg_order === 'object') {
  275. for (var i = 0; i < arg_order.length; i++) {
  276. orders = $.trim(arg_order[i]).split(' ');
  277. if(orders.length) arr.push((orders.length === 2) ? orders.concat(): [orders[0], 'ASC']);
  278. }
  279. } else {
  280. orders = $.trim(arg_order).split(' ');
  281. arr[0] = (orders.length === 2) ? orders.concat(): (orders[0].toUpperCase().match(/^(ASC|DESC)$/i)) ? [arg_field, orders[0].toUpperCase()] : [orders[0], 'ASC'];
  282. }
  283. return arr;
  284. };
  285. /**
  286. * i18n
  287. */
  288. SelectPage.prototype.setLanguage = function() {
  289. var message, p = this.option;
  290. switch (p.lang) {
  291. // German
  292. case 'de':
  293. message = {
  294. add_btn: 'Hinzufügen-Button',
  295. add_title: 'Box hinzufügen',
  296. del_btn: 'Löschen-Button',
  297. del_title: 'Box löschen',
  298. next: 'Nächsten',
  299. next_title: 'Nächsten' + p.pageSize + ' (Pfeil-rechts)',
  300. prev: 'Vorherigen',
  301. prev_title: 'Vorherigen' + p.pageSize + ' (Pfeil-links)',
  302. first_title: 'Ersten (Umschalt + Pfeil-links)',
  303. last_title: 'Letzten (Umschalt + Pfeil-rechts)',
  304. get_all_btn: 'alle (Pfeil-runter)',
  305. get_all_alt: '(Button)',
  306. close_btn: 'Schließen (Tab)',
  307. close_alt: '(Button)',
  308. loading: 'lade...',
  309. loading_alt: '(lade)',
  310. page_info: 'page_num von page_count',
  311. select_ng: 'Achtung: Bitte wählen Sie aus der Liste aus.',
  312. select_ok: 'OK : Richtig ausgewählt.',
  313. not_found: 'nicht gefunden',
  314. ajax_error: 'Bei der Verbindung zum Server ist ein Fehler aufgetreten.',
  315. clear: 'Löschen Sie den Inhalt',
  316. select_all: 'Wähle diese Seite',
  317. unselect_all: 'Diese Seite entfernen',
  318. clear_all: 'Alles löschen',
  319. max_selected: 'Sie können nur bis zu max_selected_limit Elemente auswählen'
  320. };
  321. break;
  322. // English
  323. case 'en':
  324. message = {
  325. add_btn: 'Add button',
  326. add_title: 'add a box',
  327. del_btn: 'Del button',
  328. del_title: 'delete a box',
  329. next: 'Next',
  330. next_title: 'Next' + p.pageSize + ' (Right key)',
  331. prev: 'Prev',
  332. prev_title: 'Prev' + p.pageSize + ' (Left key)',
  333. first_title: 'First (Shift + Left key)',
  334. last_title: 'Last (Shift + Right key)',
  335. get_all_btn: 'Get All (Down key)',
  336. get_all_alt: '(button)',
  337. close_btn: 'Close (Tab key)',
  338. close_alt: '(button)',
  339. loading: 'loading...',
  340. loading_alt: '(loading)',
  341. page_info: 'Page page_num of page_count',
  342. select_ng: 'Attention : Please choose from among the list.',
  343. select_ok: 'OK : Correctly selected.',
  344. not_found: 'not found',
  345. ajax_error: 'An error occurred while connecting to server.',
  346. clear: 'Clear content',
  347. select_all: 'Select current page',
  348. unselect_all: 'Clear current page',
  349. clear_all: 'Clear all selected',
  350. max_selected: 'You can only select up to max_selected_limit items'
  351. };
  352. break;
  353. // Spanish
  354. case 'es':
  355. message = {
  356. add_btn: 'Agregar boton',
  357. add_title: 'Agregar una opcion',
  358. del_btn: 'Borrar boton',
  359. del_title: 'Borrar una opcion',
  360. next: 'Siguiente',
  361. next_title: 'Proximas ' + p.pageSize + ' (tecla derecha)',
  362. prev: 'Anterior',
  363. prev_title: 'Anteriores ' + p.pageSize + ' (tecla izquierda)',
  364. first_title: 'Primera (Shift + Left)',
  365. last_title: 'Ultima (Shift + Right)',
  366. get_all_btn: 'Ver todos (tecla abajo)',
  367. get_all_alt: '(boton)',
  368. close_btn: 'Cerrar (tecla TAB)',
  369. close_alt: '(boton)',
  370. loading: 'Cargando...',
  371. loading_alt: '(Cargando)',
  372. page_info: 'page_num de page_count',
  373. select_ng: 'Atencion: Elija una opcion de la lista.',
  374. select_ok: 'OK: Correctamente seleccionado.',
  375. not_found: 'no encuentre',
  376. ajax_error: 'Un error ocurrió mientras conectando al servidor.',
  377. clear: 'Borrar el contenido',
  378. select_all: 'Elija esta página',
  379. unselect_all: 'Borrar esta página',
  380. clear_all: 'Borrar todo marcado',
  381. max_selected: 'Solo puedes seleccionar hasta max_selected_limit elementos'
  382. };
  383. break;
  384. // Brazilian Portuguese
  385. case 'pt-br':
  386. message = {
  387. add_btn: 'Adicionar botão',
  388. add_title: 'Adicionar uma caixa',
  389. del_btn: 'Apagar botão',
  390. del_title: 'Apagar uma caixa',
  391. next: 'Próxima',
  392. next_title: 'Próxima ' + p.pageSize + ' (tecla direita)',
  393. prev: 'Anterior',
  394. prev_title: 'Anterior ' + p.pageSize + ' (tecla esquerda)',
  395. first_title: 'Primeira (Shift + Left)',
  396. last_title: 'Última (Shift + Right)',
  397. get_all_btn: 'Ver todos (Seta para baixo)',
  398. get_all_alt: '(botão)',
  399. close_btn: 'Fechar (tecla TAB)',
  400. close_alt: '(botão)',
  401. loading: 'Carregando...',
  402. loading_alt: '(Carregando)',
  403. page_info: 'page_num de page_count',
  404. select_ng: 'Atenção: Escolha uma opção da lista.',
  405. select_ok: 'OK: Selecionado Corretamente.',
  406. not_found: 'não encontrado',
  407. ajax_error: 'Um erro aconteceu enquanto conectando a servidor.',
  408. clear: 'Limpe o conteúdo',
  409. select_all: 'Selecione a página atual',
  410. unselect_all: 'Remova a página atual',
  411. clear_all: 'Limpar tudo',
  412. max_selected: 'Você só pode selecionar até max_selected_limit itens'
  413. };
  414. break;
  415. // Japanese
  416. case 'ja':
  417. message = {
  418. add_btn: '追加ボタン',
  419. add_title: '入力ボックスを追加します',
  420. del_btn: '削除ボタン',
  421. del_title: '入力ボックスを削除します',
  422. next: '次へ',
  423. next_title: '次の' + p.pageSize + '件 (右キー)',
  424. prev: '前へ',
  425. prev_title: '前の' + p.pageSize + '件 (左キー)',
  426. first_title: '最初のページへ (Shift + 左キー)',
  427. last_title: '最後のページへ (Shift + 右キー)',
  428. get_all_btn: '全件取得 (下キー)',
  429. get_all_alt: '画像:ボタン',
  430. close_btn: '閉じる (Tabキー)',
  431. close_alt: '画像:ボタン',
  432. loading: '読み込み中...',
  433. loading_alt: '画像:読み込み中...',
  434. page_info: 'page_num 件 (全 page_count 件)',
  435. select_ng: '注意 : リストの中から選択してください',
  436. select_ok: 'OK : 正しく選択されました。',
  437. not_found: '(0 件)',
  438. ajax_error: 'サーバとの通信でエラーが発生しました。',
  439. clear: 'コンテンツをクリアする',
  440. select_all: '当ページを選びます',
  441. unselect_all: '移して当ページを割ります',
  442. clear_all: '選択した項目をクリアする',
  443. max_selected: '最多で max_selected_limit のプロジェクトを選ぶことしかできません'
  444. };
  445. break;
  446. // 中文
  447. case 'cn':
  448. default:
  449. message = {
  450. add_btn: '添加按钮',
  451. add_title: '添加区域',
  452. del_btn: '删除按钮',
  453. del_title: '删除区域',
  454. next: '下一页',
  455. next_title: '下' + p.pageSize + ' (→)',
  456. prev: '上一页',
  457. prev_title: '上' + p.pageSize + ' (←)',
  458. first_title: '首页 (Shift + ←)',
  459. last_title: '尾页 (Shift + →)',
  460. get_all_btn: '获得全部 (↓)',
  461. get_all_alt: '(按钮)',
  462. close_btn: '关闭 (Tab键)',
  463. close_alt: '(按钮)',
  464. loading: '读取中...',
  465. loading_alt: '(读取中)',
  466. page_info: '第 page_num 页(共page_count页)',
  467. select_ng: '请注意:请从列表中选择.',
  468. select_ok: 'OK : 已经选择.',
  469. not_found: '无查询结果',
  470. ajax_error: '连接到服务器时发生错误!',
  471. clear: '清除内容',
  472. select_all: '选择当前页项目',
  473. unselect_all: '取消选择当前页项目',
  474. clear_all: '清除全部已选择项目',
  475. max_selected: '最多只能选择 max_selected_limit 个项目'
  476. };
  477. break;
  478. }
  479. this.message = message;
  480. };
  481. /**
  482. * Css classname defined
  483. */
  484. SelectPage.prototype.setCssClass = function() {
  485. var css_class = {
  486. container: 'sp_container',
  487. container_open: 'sp_container_open',
  488. re_area: 'sp_result_area',
  489. result_open: 'sp_result_area_open',
  490. control_box: 'sp_control_box',
  491. //multiple select mode
  492. element_box: 'sp_element_box',
  493. navi: 'sp_navi',
  494. //result list
  495. results: 'sp_results',
  496. re_off: 'sp_results_off',
  497. select: 'sp_over',
  498. select_ok: 'sp_select_ok',
  499. select_ng: 'sp_select_ng',
  500. selected: 'sp_selected',
  501. input_off: 'sp_input_off',
  502. message_box: 'sp_message_box',
  503. disabled: 'sp_disabled',
  504. button: 'sp_button',
  505. caret_open: 'sp_caret_open',
  506. btn_on: 'sp_btn_on',
  507. btn_out: 'sp_btn_out',
  508. input: 'sp_input',
  509. clear_btn : 'sp_clear_btn',
  510. align_right : 'sp_align_right'
  511. };
  512. this.css_class = css_class;
  513. };
  514. /**
  515. * Plugin inner properties
  516. */
  517. SelectPage.prototype.setProp = function() {
  518. this.prop = {
  519. //input disabled status
  520. disabled : false,
  521. current_page: 1,
  522. //total page
  523. max_page: 1,
  524. //ajax data loading status
  525. is_loading: false,
  526. xhr: false,
  527. key_paging: false,
  528. key_select: false,
  529. //last selected item value
  530. prev_value: '',
  531. //last selected item text
  532. selected_text : '',
  533. last_input_time: undefined,
  534. init_set: false
  535. };
  536. this.template = {
  537. tag: {
  538. content : '<li class="selected_tag" itemvalue="#item_value#">#item_text#<span class="tag_close"><i class="sp-iconfont if-close"></i></span></li>',
  539. textKey : '#item_text#',
  540. valueKey : '#item_value#'
  541. },
  542. page: {
  543. current: 'page_num',
  544. total: 'page_count'
  545. },
  546. msg: {
  547. maxSelectLimit: 'max_selected_limit'
  548. }
  549. };
  550. };
  551. /**
  552. * Get the actual width/height of invisible DOM elements with jQuery.
  553. * Source code come from dreamerslab/jquery.actual
  554. * @param element
  555. * @param method
  556. * @returns {*}
  557. */
  558. SelectPage.prototype.elementRealSize = function(element, method){
  559. var defaults = {
  560. absolute : false,
  561. clone : false,
  562. includeMargin : false,
  563. display : 'block'
  564. };
  565. var configs = defaults, $target = element.eq( 0 ),fix, restore,tmp = [], style = '', $hidden;
  566. fix = function (){
  567. // get all hidden parents
  568. $hidden = $target.parents().addBack().filter( ':hidden' );
  569. style += 'visibility: hidden !important; display: ' + configs.display + ' !important; ';
  570. if( configs.absolute === true ) style += 'position: absolute !important;';
  571. // save the origin style props
  572. // set the hidden el css to be got the actual value later
  573. $hidden.each( function (){
  574. // Save original style. If no style was set, attr() returns undefined
  575. var $this = $( this ), thisStyle = $this.attr( 'style' );
  576. tmp.push( thisStyle );
  577. // Retain as much of the original style as possible, if there is one
  578. $this.attr( 'style', thisStyle ? thisStyle + ';' + style : style );
  579. });
  580. };
  581. restore = function (){
  582. // restore origin style values
  583. $hidden.each( function ( i ){
  584. var $this = $( this ), _tmp = tmp[ i ];
  585. if( _tmp === undefined ) $this.removeAttr( 'style' );
  586. else $this.attr( 'style', _tmp );
  587. });
  588. };
  589. fix();
  590. // get the actual value with user specific methed
  591. // it can be 'width', 'height', 'outerWidth', 'innerWidth'... etc
  592. // configs.includeMargin only works for 'outerWidth' and 'outerHeight'
  593. var actual = /(outer)/.test( method ) ?
  594. $target[ method ]( configs.includeMargin ) :
  595. $target[ method ]();
  596. restore();
  597. // IMPORTANT, this plugin only return the value of the first element
  598. return actual;
  599. };
  600. /**
  601. * Dom building
  602. * @param {Object} combo_input - original input element
  603. */
  604. SelectPage.prototype.setElem = function(combo_input) {
  605. // 1. build Dom object
  606. var elem = {}, p = this.option, css = this.css_class, msg = this.message, input = $(combo_input);
  607. var orgWidth = input.outerWidth();
  608. // fix input width in hidden situation
  609. if(orgWidth <= 0) orgWidth = this.elementRealSize(input, 'outerWidth');
  610. if(orgWidth < 150) orgWidth = 150;
  611. elem.combo_input = input.attr({'autocomplete':'off'}).addClass(css.input).wrap('<div>');
  612. if(p.selectOnly) elem.combo_input.prop('readonly',true);
  613. elem.container = elem.combo_input.parent().addClass(css.container);
  614. if(elem.combo_input.prop('disabled')) {
  615. if(p.multiple) elem.container.addClass(css.disabled);
  616. else elem.combo_input.addClass(css.input_off);
  617. }
  618. // set outer box width
  619. elem.container.width(orgWidth);
  620. elem.button = $('<div>').addClass(css.button);
  621. //drop down button
  622. elem.dropdown = $('<span class="sp_caret"></span>');
  623. //clear button 'X' in single mode
  624. elem.clear_btn = $('<div>').html($('<i>').addClass('sp-iconfont if-close')).addClass(css.clear_btn).attr('title', msg.clear);
  625. if(!p.dropButton) elem.clear_btn.addClass(css.align_right);
  626. //main box in multiple mode
  627. elem.element_box = $('<ul>').addClass(css.element_box);
  628. if(p.multiple && p.multipleControlbar)
  629. elem.control = $('<div>').addClass(css.control_box);
  630. //result list box
  631. elem.result_area = $('<div>').addClass(css.re_area);
  632. //pagination bar
  633. if(p.pagination) elem.navi = $('<div>').addClass('sp_pagination').append('<ul>');
  634. elem.results = $('<ul>').addClass(css.results);
  635. var namePrefix = '_text',
  636. input_id = elem.combo_input.attr('id') || elem.combo_input.attr('name'),
  637. input_name = elem.combo_input.attr('name') || 'selectPage',
  638. hidden_name = input_name,
  639. hidden_id = input_id;
  640. //switch the id and name attributes of input/hidden element
  641. elem.hidden = $('<input type="hidden" class="sp_hidden" />').attr({
  642. name: hidden_name,
  643. id: hidden_id
  644. }).val('');
  645. elem.combo_input.attr({
  646. name: input_name + namePrefix,
  647. id: input_id + namePrefix
  648. });
  649. // 2. DOM element put
  650. elem.container.append(elem.hidden);
  651. if(p.dropButton){
  652. elem.container.append(elem.button)
  653. elem.button.append(elem.dropdown);
  654. }
  655. $(document.body).append(elem.result_area);
  656. elem.result_area.append(elem.results);
  657. if(p.pagination) elem.result_area.append(elem.navi);
  658. //Multiple select mode
  659. if(p.multiple){
  660. if(p.multipleControlbar){
  661. elem.control.append('<button type="button" class="btn btn-default sp_clear_all" ><i class="sp-iconfont if-clear"></i></button>');
  662. elem.control.append('<button type="button" class="btn btn-default sp_unselect_all" ><i class="sp-iconfont if-unselect-all"></i></button>');
  663. elem.control.append('<button type="button" class="btn btn-default sp_select_all" ><i class="sp-iconfont if-select-all"></i></button>');
  664. elem.control_text = $('<p>');
  665. elem.control.append(elem.control_text);
  666. elem.result_area.prepend(elem.control);
  667. }
  668. elem.container.addClass('sp_container_combo');
  669. elem.combo_input.addClass('sp_combo_input').before(elem.element_box);
  670. var li = $('<li>').addClass('input_box');
  671. li.append(elem.combo_input);
  672. elem.element_box.append(li);
  673. if(elem.combo_input.attr('placeholder')) elem.combo_input.attr('placeholder_bak',elem.combo_input.attr('placeholder'));
  674. }
  675. this.elem = elem;
  676. };
  677. /**
  678. * Drop down button set to default
  679. */
  680. SelectPage.prototype.setButtonAttrDefault = function() {
  681. /*
  682. if (this.option.selectOnly) {
  683. if ($(this.elem.combo_input).val() !== '') {
  684. if ($(this.elem.hidden).val() !== '') {
  685. //选择条件
  686. $(this.elem.combo_input).attr('title', this.message.select_ok).removeClass(this.css_class.select_ng).addClass(this.css_class.select_ok);
  687. } else {
  688. //输入方式
  689. $(this.elem.combo_input).attr('title', this.message.select_ng).removeClass(this.css_class.select_ok).addClass(this.css_class.select_ng);
  690. }
  691. } else {
  692. $(this.elem.hidden).val('');
  693. $(this.elem.combo_input).removeAttr('title').removeClass(this.css_class.select_ng);
  694. }
  695. }
  696. */
  697. //this.elem.button.attr('title', this.message.get_all_btn);
  698. if(this.option.dropButton) this.elem.button.attr('title', this.message.close_btn);
  699. };
  700. /**
  701. * Set item need selected after init
  702. * set selected item ways:
  703. * <input value="key">
  704. * <input data-init="key">
  705. */
  706. SelectPage.prototype.setInitRecord = function(refresh) {
  707. var self = this, p = self.option, el = self.elem, key = '';
  708. if($.type(el.combo_input.data('init')) != 'undefined')
  709. p.initRecord = String(el.combo_input.data('init'));
  710. //data-init and value attribute can be init plugin selected item
  711. //but, if set data-init and value attribute in the same time, plugin will choose data-init attribute first
  712. if(!refresh && !p.initRecord && el.combo_input.val())
  713. p.initRecord = el.combo_input.val();
  714. el.combo_input.val('');
  715. if(!refresh) el.hidden.val(p.initRecord);
  716. key = refresh && el.hidden.val() ? el.hidden.val() : p.initRecord;
  717. if(key){
  718. if (typeof p.data === 'object') {
  719. var data = new Array();
  720. var keyarr = key.split(',');
  721. $.each(keyarr,function(index,row){
  722. for (var i = 0; i < p.data.length; i++) {
  723. if (p.data[i][p.keyField] == row) {
  724. data.push(p.data[i]);
  725. break;
  726. }
  727. }
  728. });
  729. if(!p.multiple && data.length > 1) data = [data[0]];
  730. self.afterInit(self, data);
  731. } else {//ajax data source mode to init selected item
  732. $.ajax({
  733. dataType: 'json',
  734. type: 'POST',
  735. url: p.data,
  736. data: {
  737. searchTable: p.dbTable,
  738. searchKey: p.keyField,
  739. searchValue: key
  740. },
  741. success: function(json) {
  742. var d = null;
  743. if(p.eAjaxSuccess && $.isFunction(p.eAjaxSuccess)) d = p.eAjaxSuccess(json);
  744. self.afterInit(self, d.list);
  745. },
  746. error: function() {
  747. self.ajaxErrorNotify(self);
  748. }
  749. });
  750. }
  751. }
  752. };
  753. /**
  754. * Selected item set to plugin
  755. * @param {Object} self
  756. * @param {Object} data - selected item data
  757. */
  758. SelectPage.prototype.afterInit = function(self, data) {
  759. if(!data || ($.isArray(data) && data.length === 0)) return;
  760. if(!$.isArray(data)) data = [data];
  761. var p = self.option, css = self.css_class;
  762. var getText = function(row){
  763. var text = row[p.showField];
  764. if(p.formatItem && $.isFunction(p.formatItem)){
  765. try{
  766. text = p.formatItem(row);
  767. }catch(e){}
  768. }
  769. return text;
  770. };
  771. if(p.multiple){
  772. self.prop.init_set = true;
  773. self.clearAll(self);
  774. $.each(data,function(i,row){
  775. var item = {text:getText(row),value:row[p.keyField]};
  776. if(!self.isAlreadySelected(self,item)) self.addNewTag(self, row, item);
  777. });
  778. self.tagValuesSet(self);
  779. self.inputResize(self);
  780. self.prop.init_set = false;
  781. }else{
  782. var row = data[0];
  783. self.elem.combo_input.val(getText(row));
  784. self.elem.hidden.val(row[p.keyField]);
  785. self.prop.prev_value = getText(row);
  786. self.prop.selected_text = getText(row);
  787. if (p.selectOnly) {
  788. self.elem.combo_input.attr('title', self.message.select_ok).removeClass(css.select_ng).addClass(css.select_ok);
  789. }
  790. self.putClearButton();
  791. }
  792. };
  793. /**
  794. * Drop down button event bind
  795. */
  796. SelectPage.prototype.eDropdownButton = function() {
  797. var self = this;
  798. if(self.option.dropButton){
  799. self.elem.button.mouseup(function(ev) {
  800. ev.stopPropagation();
  801. if (self.elem.result_area.is(':hidden') && !self.elem.combo_input.prop('disabled')) {
  802. self.elem.combo_input.focus();
  803. } else self.hideResults(self);
  804. });
  805. }
  806. };
  807. /**
  808. * Events bind
  809. */
  810. SelectPage.prototype.eInput = function() {
  811. var self = this,p = self.option, el = self.elem, msg = self.message;
  812. var showList = function(){
  813. self.prop.page_move = false;
  814. self.suggest(self);
  815. self.setCssFocusedInput(self);
  816. };
  817. el.combo_input.keyup(function(e) {
  818. self.processKey(self, e);
  819. }).keydown(function(e) {
  820. self.processControl(self, e);
  821. }).focus(function(e) {
  822. //When focus on input, show the result list
  823. if (el.result_area.is(':hidden')) {
  824. e.stopPropagation();
  825. self.prop.first_show = true;
  826. showList();
  827. }
  828. });
  829. el.container.on('click.SelectPage','div.'+self.css_class.clear_btn,function(e){
  830. e.stopPropagation();
  831. if(!self.disabled(self)){
  832. self.clearAll(self, true);
  833. if(p.eClear && $.isFunction(p.eClear)) p.eClear(self);
  834. }
  835. });
  836. el.result_area.on('mousedown.SelectPage',function(e){
  837. e.stopPropagation();
  838. });
  839. if(p.multiple){
  840. if(p.multipleControlbar){
  841. //Select all item of current page
  842. el.control.find('.sp_select_all').on('click.SelectPage',function(){
  843. self.selectAllLine(self);
  844. }).hover(function(){
  845. el.control_text.html(msg.select_all);
  846. },function(){
  847. el.control_text.html('');
  848. });
  849. //Cancel select all item of current page
  850. el.control.find('.sp_unselect_all').on('click.SelectPage',function(){
  851. self.unSelectAllLine(self);
  852. }).hover(function(){
  853. el.control_text.html(msg.unselect_all);
  854. },function(){
  855. el.control_text.html('');
  856. });
  857. //Clear all selected item
  858. el.control.find('.sp_clear_all').on('click.SelectPage',function(){
  859. self.clearAll(self, true);
  860. }).hover(function(){
  861. el.control_text.html(msg.clear_all);
  862. },function(){
  863. el.control_text.html('');
  864. });
  865. }
  866. el.element_box.on('click.SelectPage',function(e){
  867. var srcEl = e.target || e.srcElement;
  868. if($(srcEl).is('ul')) el.combo_input.focus();
  869. });
  870. //Tag close
  871. el.element_box.on('click.SelectPage','span.tag_close',function(){
  872. var li = $(this).closest('li'), data = li.data('dataObj');
  873. self.removeTag(self, li);
  874. showList();
  875. if(p.eTagRemove && $.isFunction(p.eTagRemove)) p.eTagRemove([data]);
  876. });
  877. self.inputResize(self);
  878. }
  879. };
  880. /**
  881. * Out of plugin area click event handler
  882. */
  883. SelectPage.prototype.eWhole = function() {
  884. var self = this, css = self.css_class;
  885. var cleanContent = function(obj){
  886. obj.elem.combo_input.val('');
  887. if(!obj.option.multiple) obj.elem.hidden.val('');
  888. obj.prop.selected_text = '';
  889. };
  890. //Out of plugin area
  891. $(document.body).off('mousedown.selectPage').on('mousedown.selectPage',function(e) {
  892. var ele = e.target || e.srcElement;
  893. var sp = $(ele).closest('div.' + css.container);
  894. //Open status result list
  895. $('div.' + css.container + '.' + css.container_open).each(function(){
  896. if(this == sp[0]) return;
  897. var $this = $(this), d = $this.find('input.' + css.input).data(SelectPage.dataKey);
  898. if(!d.elem.combo_input.val() && d.elem.hidden.val() && !d.option.multiple){
  899. d.prop.current_page = 1;//reset page to 1
  900. cleanContent(d);
  901. d.hideResults(d);
  902. return true;
  903. }
  904. if (d.elem.results.find('li').not('.'+css.message_box).length) {
  905. if(d.option.autoFillResult) {
  906. //have selected item, then hide result list
  907. if (d.elem.hidden.val()) d.hideResults(d);
  908. else if(d.elem.results.find('li.sp_over').length){
  909. //no one selected and have highlight item, select the highlight item
  910. d.selectCurrentLine(d);
  911. }else if(d.option.autoSelectFirst){
  912. //no one selected, no one highlight, select the first item
  913. d.nextLine(d);
  914. d.selectCurrentLine(d);
  915. }else d.hideResults(d);
  916. }else d.hideResults(d);
  917. } else {
  918. //when no one item match, clear search keywords
  919. if (d.option.noResultClean) cleanContent(d);
  920. else{
  921. if(!d.option.multiple) d.elem.hidden.val('');
  922. }
  923. d.hideResults(d);
  924. }
  925. });
  926. });
  927. };
  928. /**
  929. * Result list event bind
  930. */
  931. SelectPage.prototype.eResultList = function() {
  932. var self = this, css = this.css_class;
  933. self.elem.results.children('li').hover(function() {
  934. if (self.prop.key_select) {
  935. self.prop.key_select = false;
  936. return;
  937. }
  938. if(!$(this).hasClass(css.selected) && !$(this).hasClass(css.message_box)){
  939. $(this).addClass(css.select);
  940. self.setCssFocusedResults(self);
  941. }
  942. },function(){
  943. $(this).removeClass(css.select);
  944. }).click(function(e) {
  945. if (self.prop.key_select) {
  946. self.prop.key_select = false;
  947. return;
  948. }
  949. e.preventDefault();
  950. e.stopPropagation();
  951. if(!$(this).hasClass(css.selected)) self.selectCurrentLine(self);
  952. });
  953. };
  954. /**
  955. * Reposition result list when list beyond the visible area
  956. */
  957. SelectPage.prototype.eScroll = function(){
  958. var css = this.css_class;
  959. $(window).on('scroll.SelectPage',function(){
  960. $('div.' + css.container + '.' + css.container_open).each(function(){
  961. var $this = $(this), d = $this.find('input.'+css.input).data(SelectPage.dataKey),
  962. offset = d.elem.result_area.offset(),
  963. screenScrollTop = $(window).scrollTop(),
  964. docHeight = $(document).height(),
  965. viewHeight = $(window).height(),
  966. listHeight = d.elem.result_area.outerHeight(),
  967. listBottom = offset.top + listHeight,
  968. hasOverflow = docHeight > viewHeight,
  969. down = d.elem.result_area.hasClass('shadowDown');
  970. if(hasOverflow){
  971. if(down){//open down
  972. if(listBottom > (viewHeight + screenScrollTop)) d.calcResultsSize(d);
  973. }else{//open up
  974. if(offset.top < screenScrollTop) d.calcResultsSize(d);
  975. }
  976. }
  977. });
  978. });
  979. };
  980. /**
  981. * Page bar button event bind
  982. */
  983. SelectPage.prototype.ePaging = function() {
  984. var self = this;
  985. if(!self.option.pagination) return;
  986. self.elem.navi.find('li.csFirstPage').off('click').on('click',function(ev) {
  987. //$(self.elem.combo_input).focus();
  988. ev.preventDefault();
  989. self.firstPage(self);
  990. });
  991. self.elem.navi.find('li.csPreviousPage').off('click').on('click',function(ev) {
  992. //$(self.elem.combo_input).focus();
  993. ev.preventDefault();
  994. self.prevPage(self);
  995. });
  996. self.elem.navi.find('li.csNextPage').off('click').on('click',function(ev) {
  997. //$(self.elem.combo_input).focus();
  998. ev.preventDefault();
  999. self.nextPage(self);
  1000. });
  1001. self.elem.navi.find('li.csLastPage').off('click').on('click',function(ev) {
  1002. //$(self.elem.combo_input).focus();
  1003. ev.preventDefault();
  1004. self.lastPage(self);
  1005. });
  1006. };
  1007. /**
  1008. * Ajax request fail
  1009. * @param {Object} self
  1010. */
  1011. SelectPage.prototype.ajaxErrorNotify = function(self) {
  1012. self.showMessage(self.message.ajax_error);
  1013. };
  1014. /**
  1015. * Message box
  1016. * @param {Object} self
  1017. * @param msg {string} the text need to show
  1018. */
  1019. SelectPage.prototype.showMessage = function(self,msg){
  1020. if(!msg) return;
  1021. var msgLi = '<li class="'+self.css_class.message_box+'"><i class="sp-iconfont if-warning"></i> '+msg+'</li>';
  1022. self.elem.results.empty().append(msgLi).show();
  1023. self.calcResultsSize(self);
  1024. self.setOpenStatus(self, true);
  1025. self.elem.control.hide();
  1026. if(self.option.pagination) self.elem.navi.hide();
  1027. };
  1028. /**
  1029. * @desc Scroll
  1030. * @param {Object} self
  1031. * @param {boolean} enforce
  1032. */
  1033. SelectPage.prototype.scrollWindow = function(self, enforce) {
  1034. var current_result = self.getCurrentLine(self),
  1035. target_top = (current_result && !enforce) ? current_result.offset().top: self.elem.container.offset().top,
  1036. target_size;
  1037. self.prop.size_li = self.elem.results.children('li:first').outerHeight();
  1038. target_size = self.prop.size_li;
  1039. var gap, client_height = $(window).height(),
  1040. scroll_top = $(window).scrollTop(),
  1041. scroll_bottom = scroll_top + client_height - target_size;
  1042. if (current_result.length) {
  1043. if (target_top < scroll_top || target_size > client_height) {
  1044. //scroll to top
  1045. gap = target_top - scroll_top;
  1046. } else if (target_top > scroll_bottom) {
  1047. //scroll down
  1048. gap = target_top - scroll_bottom;
  1049. } else return; //do not scroll
  1050. } else if (target_top < scroll_top) gap = target_top - scroll_top;
  1051. window.scrollBy(0, gap);
  1052. };
  1053. /**
  1054. * change css class by status
  1055. * @param self
  1056. * @param status {boolean} true: open, false: close
  1057. */
  1058. SelectPage.prototype.setOpenStatus = function(self, status){
  1059. var el = self.elem, css = self.css_class;
  1060. if(status){
  1061. el.container.addClass(css.container_open);
  1062. el.result_area.addClass(css.result_open);
  1063. }else{
  1064. el.container.removeClass(css.container_open);
  1065. el.result_area.removeClass(css.result_open);
  1066. }
  1067. };
  1068. /**
  1069. * input element in focus css class set
  1070. * @param {Object} self
  1071. */
  1072. SelectPage.prototype.setCssFocusedInput = function(self) {
  1073. //$(self.elem.results).addClass(self.css_class.re_off);
  1074. //$(self.elem.combo_input).removeClass(self.css_class.input_off);
  1075. };
  1076. /**
  1077. * set result list get focus and input element lost focus
  1078. * @param {Object} self
  1079. */
  1080. SelectPage.prototype.setCssFocusedResults = function(self) {
  1081. //$(self.elem.results).removeClass(self.css_class.re_off);
  1082. //$(self.elem.combo_input).addClass(self.css_class.input_off);
  1083. };
  1084. /**
  1085. * Quick search input keywords listener
  1086. * @param {Object} self
  1087. */
  1088. SelectPage.prototype.checkValue = function(self) {
  1089. var now_value = self.elem.combo_input.val();
  1090. if (now_value != self.prop.prev_value) {
  1091. self.prop.prev_value = now_value;
  1092. self.prop.first_show = false;
  1093. if(self.option.selectOnly) self.setButtonAttrDefault();
  1094. if(!self.option.multiple && !now_value){
  1095. self.elem.combo_input.val('');
  1096. self.elem.hidden.val('');
  1097. self.elem.clear_btn.remove();
  1098. }
  1099. self.suggest(self);
  1100. }
  1101. };
  1102. /**
  1103. * Input handle(regular input)
  1104. * @param {Object} self
  1105. * @param {Object} e - event object
  1106. */
  1107. SelectPage.prototype.processKey = function(self, e) {
  1108. if($.inArray(e.keyCode, [37, 38, 39, 40, 27, 9, 13]) === -1){
  1109. if(e.keyCode != 16) self.setCssFocusedInput(self); // except Shift(16)
  1110. self.inputResize(self);
  1111. if($.type(self.option.data) === 'string'){
  1112. self.prop.last_input_time = e.timeStamp;
  1113. setTimeout(function(){
  1114. if((e.timeStamp - self.prop.last_input_time) === 0)
  1115. self.checkValue(self);
  1116. },self.option.inputDelay * 1000);
  1117. }else{
  1118. self.checkValue(self);
  1119. }
  1120. }
  1121. }
  1122. /**
  1123. * Input handle(control key)
  1124. * @param {Object} self
  1125. * @param {Object} e - event object
  1126. */
  1127. SelectPage.prototype.processControl = function(self, e) {
  1128. if (($.inArray(e.keyCode, [37, 38, 39, 40, 27, 9]) > -1 && self.elem.result_area.is(':visible')) ||
  1129. ($.inArray(e.keyCode, [13, 9]) > -1 && self.getCurrentLine(self))) {
  1130. e.preventDefault();
  1131. e.stopPropagation();
  1132. e.cancelBubble = true;
  1133. e.returnValue = false;
  1134. switch (e.keyCode) {
  1135. case 37:// left
  1136. if (e.shiftKey) self.firstPage(self);
  1137. else self.prevPage(self);
  1138. break;
  1139. case 38:// up
  1140. self.prop.key_select = true;
  1141. self.prevLine(self);
  1142. break;
  1143. case 39:// right
  1144. if (e.shiftKey) self.lastPage(self);
  1145. else self.nextPage(self);
  1146. break;
  1147. case 40:// down
  1148. if (self.elem.results.children('li').length) {
  1149. self.prop.key_select = true;
  1150. self.nextLine(self);
  1151. } else self.suggest(self);
  1152. break;
  1153. case 9:// tab
  1154. self.prop.key_paging = true;
  1155. self.selectCurrentLine(self);
  1156. //self.hideResults(self);
  1157. break;
  1158. case 13:// return
  1159. self.selectCurrentLine(self);
  1160. break;
  1161. case 27:// escape
  1162. self.prop.key_paging = true;
  1163. self.hideResults(self);
  1164. break;
  1165. }
  1166. }
  1167. };
  1168. /**
  1169. * Abort Ajax request
  1170. * @param {Object} self
  1171. */
  1172. SelectPage.prototype.abortAjax = function(self) {
  1173. if (self.prop.xhr) {
  1174. self.prop.xhr.abort();
  1175. self.prop.xhr = false;
  1176. }
  1177. };
  1178. /**
  1179. * Suggest result of search keywords
  1180. * @param {Object} self
  1181. */
  1182. SelectPage.prototype.suggest = function(self) {
  1183. var q_word, val = $.trim(self.elem.combo_input.val());
  1184. if(self.option.multiple) q_word = val;
  1185. else{
  1186. if(val && val === self.prop.selected_text) q_word = '';
  1187. else q_word = val;
  1188. }
  1189. q_word = q_word.split(/[\s ]+/);
  1190. //Before show up result list callback
  1191. if(self.option.eOpen && $.isFunction(self.option.eOpen))
  1192. self.option.eOpen.call(self);
  1193. self.abortAjax(self);
  1194. //self.setLoading(self);
  1195. var which_page_num = self.prop.current_page || 1;
  1196. if (typeof self.option.data == 'object') self.searchForJson(self, q_word, which_page_num);
  1197. else self.searchForDb(self, q_word, which_page_num);
  1198. };
  1199. /**
  1200. * Loading
  1201. * @param {Object} self
  1202. */
  1203. SelectPage.prototype.setLoading = function(self) {
  1204. if (self.elem.results.html() === '') {
  1205. //self.calcResultsSize(self);
  1206. self.setOpenStatus(self, true);
  1207. }
  1208. };
  1209. /**
  1210. * Search for ajax
  1211. * @param {Object} self
  1212. * @param {Array} q_word - query keyword
  1213. * @param {number} which_page_num - target page number
  1214. */
  1215. SelectPage.prototype.searchForDb = function(self, q_word, which_page_num) {
  1216. var p = self.option;
  1217. if(!p.eAjaxSuccess || !$.isFunction(p.eAjaxSuccess)) self.hideResults(self);
  1218. var _paramsFunc = p.params, _params = {}, searchKey = p.searchField;
  1219. //when have new query keyword, then reset page number to 1.
  1220. if(q_word.length && q_word[0] && q_word[0] !== self.prop.prev_value) which_page_num = 1;
  1221. var _orgParams = {
  1222. q_word: q_word,
  1223. pageNumber: which_page_num,
  1224. pageSize: p.pageSize,
  1225. andOr: p.andOr,
  1226. searchTable: p.dbTable
  1227. };
  1228. if(p.orderBy !== false) _orgParams.orderBy = p.orderBy;
  1229. _orgParams[searchKey] = q_word[0];
  1230. if (_paramsFunc && $.isFunction(_paramsFunc)) {
  1231. var result = _paramsFunc.call(self);
  1232. if (result && $.isPlainObject(result)) {
  1233. _params = $.extend({}, _orgParams, result);
  1234. } else _params = _orgParams;
  1235. } else _params = _orgParams;
  1236. self.prop.xhr = $.ajax({
  1237. dataType: 'json',
  1238. url: p.data,
  1239. type: 'POST',
  1240. data: _params,
  1241. success: function(returnData) {
  1242. if (!returnData || !$.isPlainObject(returnData)) {
  1243. self.hideResults(self);
  1244. self.ajaxErrorNotify(self);
  1245. return;
  1246. }
  1247. var data = {}, json = {};
  1248. try{
  1249. data = p.eAjaxSuccess(returnData);
  1250. json.originalResult = data.list;
  1251. json.cnt_whole = data.totalRow;
  1252. }catch(e){
  1253. self.showMessage(self, self.message.ajax_error);
  1254. return;
  1255. }
  1256. json.candidate = [];
  1257. json.keyField = [];
  1258. if (typeof json.originalResult != 'object') {
  1259. self.prop.xhr = null;
  1260. self.notFoundSearch(self);
  1261. return;
  1262. }
  1263. json.cnt_page = json.originalResult.length;
  1264. for (var i = 0; i < json.cnt_page; i++) {
  1265. for (var key in json.originalResult[i]) {
  1266. if (key == p.keyField) {
  1267. json.keyField.push(json.originalResult[i][key]);
  1268. }
  1269. if (key == p.showField) {
  1270. json.candidate.push(json.originalResult[i][key]);
  1271. }
  1272. }
  1273. }
  1274. self.prepareResults(self, json, q_word, which_page_num);
  1275. },
  1276. error: function(jqXHR, textStatus) {
  1277. if (textStatus != 'abort') {
  1278. self.hideResults(self);
  1279. self.ajaxErrorNotify(self);
  1280. }
  1281. },
  1282. complete: function() {
  1283. self.prop.xhr = null;
  1284. }
  1285. });
  1286. };
  1287. /**
  1288. * Search for json data source
  1289. * @param {Object} self
  1290. * @param {Array} q_word
  1291. * @param {number} which_page_num
  1292. */
  1293. SelectPage.prototype.searchForJson = function(self, q_word, which_page_num) {
  1294. var p = self.option, matched = [], esc_q = [], sorted = [], json = {}, i = 0, arr_reg = [];
  1295. //query keyword filter
  1296. do {
  1297. //'/\W/g'正则代表全部不是字母,数字,下划线,汉字的字符
  1298. //将非法字符进行转义
  1299. esc_q[i] = q_word[i].replace(/\W/g, '\\$&').toString();
  1300. arr_reg[i] = new RegExp(esc_q[i], 'gi');
  1301. i++;
  1302. } while ( i < q_word.length );
  1303. // SELECT * FROM data WHERE field LIKE q_word;
  1304. for ( i = 0; i < p.data.length; i++) {
  1305. var flag = false, row = p.data[i], itemText;
  1306. for (var j = 0; j < arr_reg.length; j++) {
  1307. itemText = row[p.searchField];
  1308. if(p.formatItem && $.isFunction(p.formatItem))
  1309. itemText = p.formatItem(row);
  1310. if (itemText.match(arr_reg[j])) {
  1311. flag = true;
  1312. if (p.andOr == 'OR') break;
  1313. } else {
  1314. flag = false;
  1315. if (p.andOr == 'AND') break;
  1316. }
  1317. }
  1318. if (flag) matched.push(row);
  1319. }
  1320. // (CASE WHEN ...) then く order some column
  1321. if(p.orderBy === false) sorted = matched.concat();
  1322. else{
  1323. var reg1 = new RegExp('^' + esc_q[0] + '$', 'gi'),
  1324. reg2 = new RegExp('^' + esc_q[0], 'gi'),
  1325. matched1 = [], matched2 = [], matched3 = [];
  1326. for ( i = 0; i < matched.length; i++) {
  1327. var orderField = p.orderBy[0][0];
  1328. var orderValue = String(matched[i][orderField]);
  1329. if (orderValue.match(reg1)) {
  1330. matched1.push(matched[i]);
  1331. } else if (orderValue.match(reg2)) {
  1332. matched2.push(matched[i]);
  1333. } else {
  1334. matched3.push(matched[i]);
  1335. }
  1336. }
  1337. if (p.orderBy[0][1].match(/^asc$/i)) {
  1338. matched1 = self.sortAsc(self, matched1);
  1339. matched2 = self.sortAsc(self, matched2);
  1340. matched3 = self.sortAsc(self, matched3);
  1341. } else {
  1342. matched1 = self.sortDesc(self, matched1);
  1343. matched2 = self.sortDesc(self, matched2);
  1344. matched3 = self.sortDesc(self, matched3);
  1345. }
  1346. sorted = sorted.concat(matched1).concat(matched2).concat(matched3);
  1347. }
  1348. /*
  1349. if (sorted.length === undefined || sorted.length === 0 ) {
  1350. self.notFoundSearch(self);
  1351. return;
  1352. }
  1353. */
  1354. json.cnt_whole = sorted.length;
  1355. //page_move used to distinguish between init plugin or page moving
  1356. if(!self.prop.page_move){
  1357. //only single mode can be used page number relocation
  1358. if(!p.multiple){
  1359. //get selected item belong page number
  1360. var currentValue = self.elem.hidden.val();
  1361. if($.type(currentValue) !== 'undefined' && $.trim(currentValue) !== ''){
  1362. var index = 0;
  1363. $.each(sorted,function(i,row){
  1364. if(row[p.keyField] == currentValue){
  1365. index = i + 1;
  1366. return false;
  1367. }
  1368. });
  1369. which_page_num = Math.ceil(index / p.pageSize);
  1370. if(which_page_num < 1) which_page_num = 1;
  1371. self.prop.current_page = which_page_num;
  1372. }
  1373. }
  1374. }else{
  1375. //set page number to 1 when result number less then page size
  1376. if(sorted.length <= ((which_page_num - 1) * p.pageSize)){
  1377. which_page_num = 1;
  1378. self.prop.current_page = 1;
  1379. }
  1380. }
  1381. //LIMIT xx OFFSET xx
  1382. var start = (which_page_num - 1) * p.pageSize, end = start + p.pageSize;
  1383. //save original data
  1384. json.originalResult = [];
  1385. //after data filter handle
  1386. for ( i = start; i < end; i++) {
  1387. if (sorted[i] === undefined) break;
  1388. json.originalResult.push(sorted[i]);
  1389. for (var key in sorted[i]) {
  1390. if (key == p.keyField) {
  1391. if (json.keyField === undefined) json.keyField = [];
  1392. json.keyField.push(sorted[i][key]);
  1393. }
  1394. if (key == p.showField) {
  1395. if (json.candidate === undefined) json.candidate = [];
  1396. json.candidate.push(sorted[i][key]);
  1397. }
  1398. }
  1399. }
  1400. if (json.candidate === undefined) json.candidate = [];
  1401. json.cnt_page = json.candidate.length;
  1402. self.prepareResults(self, json, q_word, which_page_num);
  1403. };
  1404. /**
  1405. * Set order asc
  1406. * @param {Object} self
  1407. * @param {Array} arr - result array
  1408. */
  1409. SelectPage.prototype.sortAsc = function(self, arr) {
  1410. arr.sort(function(a, b) {
  1411. var valA = a[self.option.orderBy[0][0]], valB = b[self.option.orderBy[0][0]];
  1412. return $.type(valA) === 'number' ? valA - valB : String(valA).localeCompare(String(valB));
  1413. });
  1414. return arr;
  1415. };
  1416. /**
  1417. * Set order desc
  1418. * @param {Object} self
  1419. * @param {Array} arr - result array
  1420. */
  1421. SelectPage.prototype.sortDesc = function(self, arr) {
  1422. arr.sort(function(a, b) {
  1423. var valA = a[self.option.orderBy[0][0]], valB = b[self.option.orderBy[0][0]];
  1424. return $.type(valA) === 'number' ? valB - valA : String(valB).localeCompare(String(valA));
  1425. });
  1426. return arr;
  1427. };
  1428. /**
  1429. * Not result found handle
  1430. * @param {Object} self
  1431. */
  1432. SelectPage.prototype.notFoundSearch = function(self) {
  1433. self.elem.results.empty();
  1434. self.calcResultsSize(self);
  1435. self.setOpenStatus(self, true);
  1436. self.setCssFocusedInput(self);
  1437. };
  1438. /**
  1439. * Prepare data to show
  1440. * @param {Object} self
  1441. * @param {Object} json - data result
  1442. * @param {Array} q_word - query keyword
  1443. * @param {number} which_page_num - target page number
  1444. */
  1445. SelectPage.prototype.prepareResults = function(self, json, q_word, which_page_num) {
  1446. if(self.option.pagination) self.setNavi(self, json.cnt_whole, json.cnt_page, which_page_num);
  1447. if (!json.keyField) json.keyField = false;
  1448. if (self.option.selectOnly && json.candidate.length === 1 && json.candidate[0] == q_word[0]) {
  1449. self.elem.hidden.val(json.keyField[0]);
  1450. this.setButtonAttrDefault();
  1451. }
  1452. var is_query = false;
  1453. if (q_word && q_word.length && q_word[0]) is_query = true;
  1454. self.displayResults(self, json, is_query);
  1455. };
  1456. /**
  1457. * Build page bar
  1458. * @param {Object} self
  1459. * @param {number} cnt_whole - total record count
  1460. * @param {number} cnt_page
  1461. * @param {number} page_num - current page number
  1462. */
  1463. SelectPage.prototype.setNavi = function(self, cnt_whole, cnt_page, page_num) {
  1464. var msg = self.message;
  1465. /**
  1466. * build pagination bar
  1467. */
  1468. var buildPageNav = function(self, pagebar, page_num, last_page) {
  1469. var updatePageInfo = function(){
  1470. var pageInfo = msg.page_info;
  1471. return pageInfo.replace(self.template.page.current, page_num).replace(self.template.page.total, last_page);
  1472. };
  1473. if (pagebar.find('li').length === 0) {
  1474. pagebar.hide().empty();
  1475. var iconFist='sp-iconfont if-first',
  1476. iconPrev='sp-iconfont if-previous',
  1477. iconNext='sp-iconfont if-next',
  1478. iconLast='sp-iconfont if-last';
  1479. pagebar.append('<li class="csFirstPage" title="' + msg.first_title + '" ><a href="javascript:void(0);"> <i class="'+iconFist+'"></i> </a></li>');
  1480. pagebar.append('<li class="csPreviousPage" title="' + msg.prev_title + '" ><a href="javascript:void(0);"><i class="'+iconPrev+'"></i></a></li>');
  1481. //pagination information
  1482. pagebar.append('<li class="pageInfoBox"><a href="javascript:void(0);"> ' + updatePageInfo() + ' </a></li>');
  1483. pagebar.append('<li class="csNextPage" title="' + msg.next_title + '" ><a href="javascript:void(0);"><i class="'+iconNext+'"></i></a></li>');
  1484. pagebar.append('<li class="csLastPage" title="' + msg.last_title + '" ><a href="javascript:void(0);"> <i class="'+iconLast+'"></i> </a></li>');
  1485. pagebar.show();
  1486. }else{
  1487. pagebar.find('li.pageInfoBox a').html(updatePageInfo());
  1488. }
  1489. };
  1490. var pagebar = self.elem.navi.find('ul'),
  1491. last_page = Math.ceil(cnt_whole / self.option.pageSize); //calculate total page
  1492. if(last_page === 0) page_num = 0;
  1493. else{
  1494. if(last_page < page_num) page_num = last_page;
  1495. else if(page_num === 0) page_num = 1;
  1496. }
  1497. self.prop.current_page = page_num;//update current page number
  1498. self.prop.max_page = last_page;//update page count
  1499. buildPageNav(self, pagebar, page_num, last_page);
  1500. //update paging status
  1501. var dClass = 'disabled',
  1502. first = pagebar.find('li.csFirstPage'),
  1503. previous = pagebar.find('li.csPreviousPage'),
  1504. next = pagebar.find('li.csNextPage'),
  1505. last = pagebar.find('li.csLastPage');
  1506. //first and previous
  1507. if (page_num === 1 || page_num === 0) {
  1508. if (!first.hasClass(dClass)) first.addClass(dClass);
  1509. if (!previous.hasClass(dClass)) previous.addClass(dClass);
  1510. } else {
  1511. if (first.hasClass(dClass)) first.removeClass(dClass);
  1512. if (previous.hasClass(dClass)) previous.removeClass(dClass);
  1513. }
  1514. //next and last
  1515. if (page_num === last_page || last_page === 0) {
  1516. if (!next.hasClass(dClass)) next.addClass(dClass);
  1517. if (!last.hasClass(dClass)) last.addClass(dClass);
  1518. } else {
  1519. if (next.hasClass(dClass)) next.removeClass(dClass);
  1520. if (last.hasClass(dClass)) last.removeClass(dClass);
  1521. }
  1522. if (last_page > 1) self.ePaging(); //pagination event bind
  1523. };
  1524. /**
  1525. * Render result list
  1526. * @param {Object} self
  1527. * @param {Object} json - result data
  1528. * @param {boolean} is_query - used to different from search to open and just click to open
  1529. */
  1530. SelectPage.prototype.displayResults = function(self, json, is_query) {
  1531. var p = self.option, el = self.elem;
  1532. el.results.hide().empty();
  1533. if(p.multiple && $.type(p.maxSelectLimit) === 'number' && p.maxSelectLimit > 0){
  1534. var selectedSize = el.element_box.find('li.selected_tag').length;
  1535. if(selectedSize > 0 && selectedSize >= p.maxSelectLimit){
  1536. var msg = self.message.max_selected;
  1537. self.showMessage(self, msg.replace(self.template.msg.maxSelectLimit, p.maxSelectLimit));
  1538. return;
  1539. }
  1540. }
  1541. if(json.candidate.length){
  1542. var arr_candidate = json.candidate,
  1543. arr_primary_key = json.keyField,
  1544. keystr = el.hidden.val(),
  1545. keyArr = keystr ? keystr.split(',') : new Array(),
  1546. itemText = '';
  1547. for (var i = 0; i < arr_candidate.length; i++) {
  1548. if(p.formatItem && $.isFunction(p.formatItem)){
  1549. try {
  1550. itemText = p.formatItem(json.originalResult[i]);
  1551. } catch (e) {
  1552. /*eslint no-console: ["error", { allow: ["warn", "error"] }] */
  1553. console.error('formatItem内容格式化函数内容设置不正确!');
  1554. itemText = arr_candidate[i];
  1555. }
  1556. }else itemText = arr_candidate[i];
  1557. var list = $('<li>').html(itemText).attr({
  1558. pkey: arr_primary_key[i]
  1559. });
  1560. if(!p.formatItem) list.attr('title', itemText);
  1561. //Set selected item highlight
  1562. if ($.inArray(arr_primary_key[i].toString(),keyArr) !== -1) {
  1563. list.addClass(self.css_class.selected);
  1564. }
  1565. //cache item data
  1566. list.data('dataObj',json.originalResult[i]);
  1567. el.results.append(list);
  1568. }
  1569. }else{
  1570. var li = '<li class="'+self.css_class.message_box+'"><i class="sp-iconfont if-warning"></i> '+
  1571. self.message.not_found + '</li>';
  1572. el.results.append(li);
  1573. }
  1574. el.results.show();
  1575. if(p.multiple && p.multipleControlbar) el.control.show();
  1576. if(p.pagination) el.navi.show();
  1577. self.calcResultsSize(self);
  1578. self.setOpenStatus(self, true);
  1579. //Result item event bind
  1580. self.eResultList();
  1581. //scrolling listen
  1582. self.eScroll();
  1583. //auto highlight first item in search, have result and set autoSelectFirst to true situation
  1584. if (is_query && json.candidate.length && p.autoSelectFirst) self.nextLine(self);
  1585. };
  1586. /**
  1587. * Calculate result list size and position
  1588. * @param {Object} self
  1589. */
  1590. SelectPage.prototype.calcResultsSize = function(self) {
  1591. var p = self.option, el = self.elem;
  1592. var rePosition = function(){
  1593. if (el.container.css('position') === 'static') {
  1594. // position: static
  1595. var st_offset = el.combo_input.offset();
  1596. el.result_area.css({
  1597. top: st_offset.top + el.combo_input.outerHeight() + 'px',
  1598. left: st_offset.left + 'px'
  1599. });
  1600. } else {
  1601. var listHeight;
  1602. if(!p.pagination){
  1603. var itemHeight = el.results.find('li:first').outerHeight(true);
  1604. listHeight = itemHeight * p.listSize;
  1605. el.results.css({
  1606. 'max-height':listHeight,
  1607. 'overflow-y':'auto'
  1608. });
  1609. }
  1610. //handle result list show up side(left, right, up or down)
  1611. var docWidth = $(document).width(),
  1612. docHeight = $(document).height(),//the document full height
  1613. viewHeight = $(window).height(),//browser visible area height
  1614. offset = el.container.offset(),
  1615. screenScrollTop = $(window).scrollTop(),
  1616. listWidth = el.result_area.outerWidth(),
  1617. //default left used input element left
  1618. defaultLeft = offset.left,//p.multiple ? -1 : 0;
  1619. //input element height
  1620. inputHeight = el.container.outerHeight(),
  1621. left = (offset.left + listWidth) > docWidth ?
  1622. defaultLeft - (listWidth - el.container.outerWidth()) :
  1623. defaultLeft,
  1624. //the actual top coordinate of input element(outer div)
  1625. screenTop = offset.top,//$(el.container).scrollTop();//offset.top - screenScrollTop;
  1626. top = 0,dist = 5,//set distance between input element and result list
  1627. //the actual top coordinate of result list
  1628. listBottom = screenTop + inputHeight + listHeight + dist,
  1629. hasOverflow = docHeight > viewHeight;
  1630. //result list height
  1631. listHeight = el.result_area.outerHeight();
  1632. if((screenTop - screenScrollTop - dist > listHeight) &&
  1633. (hasOverflow && listBottom > (viewHeight + screenScrollTop)) ||
  1634. (!hasOverflow && listBottom > viewHeight)){
  1635. //open up
  1636. top = offset.top - listHeight - dist;
  1637. el.result_area.removeClass('shadowUp shadowDown').addClass('shadowUp');
  1638. }else{
  1639. //open down
  1640. top = offset.top + (p.multiple ? el.container.outerHeight() : inputHeight);
  1641. el.result_area.removeClass('shadowUp shadowDown').addClass('shadowDown');
  1642. top += dist;
  1643. }
  1644. return {
  1645. top : top + 'px', left: left + 'px'
  1646. };
  1647. }
  1648. };
  1649. if(el.result_area.is(':visible')){
  1650. el.result_area.css(rePosition());
  1651. }else{
  1652. var pss = rePosition();
  1653. el.result_area.css(pss).show(1,function(){
  1654. var repss = rePosition();
  1655. if(pss.top !== repss.top || pss.left !== repss.left) el.result_area.css(repss);
  1656. });
  1657. }
  1658. };
  1659. /**
  1660. * hide result list
  1661. * @param {Object} self
  1662. */
  1663. SelectPage.prototype.hideResults = function(self) {
  1664. if (self.prop.key_paging) {
  1665. self.scrollWindow(self, true);
  1666. self.prop.key_paging = false;
  1667. }
  1668. self.setCssFocusedInput(self);
  1669. if (self.option.autoFillResult) {
  1670. //self.selectCurrentLine(self, true);
  1671. }
  1672. self.elem.results.empty();
  1673. self.elem.result_area.hide();
  1674. self.setOpenStatus(self, false);
  1675. //unbind window scroll listen
  1676. $(window).off('scroll.SelectPage');
  1677. self.abortAjax(self);
  1678. self.setButtonAttrDefault();
  1679. };
  1680. /**
  1681. * set plugin to disabled / enabled
  1682. * @param self
  1683. * @param disabled
  1684. */
  1685. SelectPage.prototype.disabled = function(self, disabled){
  1686. var el = self.elem;
  1687. if($.type(disabled) === 'undefined') return el.combo_input.prop('disabled');
  1688. if($.type(disabled) === 'boolean'){
  1689. el.combo_input.prop('disabled', disabled);
  1690. if(disabled) el.container.addClass(self.css_class.disabled);
  1691. else el.container.removeClass(self.css_class.disabled);
  1692. }
  1693. };
  1694. /**
  1695. * Go fist page
  1696. * @param {Object} self
  1697. */
  1698. SelectPage.prototype.firstPage = function(self) {
  1699. if (self.prop.current_page > 1) {
  1700. self.prop.current_page = 1;
  1701. self.prop.page_move = true;
  1702. self.suggest(self);
  1703. }
  1704. };
  1705. /**
  1706. * Go previous page
  1707. * @param {Object} self
  1708. */
  1709. SelectPage.prototype.prevPage = function(self) {
  1710. if (self.prop.current_page > 1) {
  1711. self.prop.current_page--;
  1712. self.prop.page_move = true;
  1713. self.suggest(self);
  1714. }
  1715. };
  1716. /**
  1717. * Go next page
  1718. * @param {Object} self
  1719. */
  1720. SelectPage.prototype.nextPage = function(self) {
  1721. if (self.prop.current_page < self.prop.max_page) {
  1722. self.prop.current_page++;
  1723. self.prop.page_move = true;
  1724. self.suggest(self);
  1725. }
  1726. };
  1727. /**
  1728. * Go last page
  1729. * @param {Object} self
  1730. */
  1731. SelectPage.prototype.lastPage = function(self) {
  1732. if (self.prop.current_page < self.prop.max_page) {
  1733. self.prop.current_page = self.prop.max_page;
  1734. self.prop.page_move = true;
  1735. self.suggest(self);
  1736. }
  1737. };
  1738. /**
  1739. * do something after select/unSelect action
  1740. * @param {Object} self
  1741. * @param {boolean} reOpen
  1742. */
  1743. SelectPage.prototype.afterAction = function(self, reOpen){
  1744. self.inputResize(self);
  1745. self.elem.combo_input.change();
  1746. self.setCssFocusedInput(self);
  1747. if(self.prop.init_set) return;
  1748. if(self.option.multiple){
  1749. if(self.option.selectToCloseList){
  1750. self.hideResults(self);
  1751. self.elem.combo_input.blur();
  1752. }
  1753. if(!self.option.selectToCloseList && reOpen){
  1754. self.suggest(self);
  1755. self.elem.combo_input.focus();
  1756. }
  1757. }else{
  1758. self.hideResults(self);
  1759. self.elem.combo_input.blur();
  1760. }
  1761. };
  1762. /**
  1763. * Select current list item
  1764. * @param {Object} self
  1765. */
  1766. SelectPage.prototype.selectCurrentLine = function(self) {
  1767. self.scrollWindow(self, true);
  1768. var p = self.option, current = self.getCurrentLine(self);
  1769. if (current) {
  1770. var data = current.data('dataObj');
  1771. if(!p.multiple){
  1772. self.elem.combo_input.val(current.text());
  1773. self.elem.hidden.val(current.attr('pkey'));
  1774. }else{
  1775. //build tags in multiple selection mode
  1776. self.elem.combo_input.val('');
  1777. var item = {text:current.text(), value:current.attr('pkey')};
  1778. if(!self.isAlreadySelected(self, item)){
  1779. self.addNewTag(self, data, item);
  1780. self.tagValuesSet(self);
  1781. }
  1782. }
  1783. if (p.selectOnly) self.setButtonAttrDefault();
  1784. //Select item callback
  1785. if(p.eSelect && $.isFunction(p.eSelect)) p.eSelect(data, self);
  1786. self.prop.prev_value = self.elem.combo_input.val();
  1787. self.prop.selected_text = self.elem.combo_input.val();
  1788. self.putClearButton();
  1789. }
  1790. self.afterAction(self, true);
  1791. };
  1792. /**
  1793. * Show clear button when item selected in single selection mode
  1794. */
  1795. SelectPage.prototype.putClearButton = function(){
  1796. if(!this.option.multiple && !this.elem.combo_input.prop('disabled')) this.elem.container.append(this.elem.clear_btn);
  1797. };
  1798. /**
  1799. * Select all list item
  1800. * @param {Object} self
  1801. */
  1802. SelectPage.prototype.selectAllLine = function(self){
  1803. var p = self.option, jsonarr = new Array();
  1804. self.elem.results.find('li').each(function(i,row){
  1805. var $row = $(row), data = $row.data('dataObj');
  1806. var item = {text:$row.text(),value:$row.attr('pkey')};
  1807. if(!self.isAlreadySelected(self,item)){
  1808. self.addNewTag(self, data, item);
  1809. self.tagValuesSet(self);
  1810. }
  1811. jsonarr.push(data);
  1812. //limited max selected items
  1813. if($.type(p.maxSelectLimit) === 'number' &&
  1814. p.maxSelectLimit > 0 &&
  1815. p.maxSelectLimit === self.elem.element_box.find('li.selected_tag').length){
  1816. return false;
  1817. }
  1818. });
  1819. if(p.eSelect && $.isFunction(p.eSelect)) p.eSelect(jsonarr, self);
  1820. self.afterAction(self, true);
  1821. };
  1822. /**
  1823. * Cancel select all item in current page
  1824. * @param {Object} self
  1825. */
  1826. SelectPage.prototype.unSelectAllLine = function(self){
  1827. var p = self.option, ds = [];
  1828. self.elem.results.find('li').each(function(i, row){
  1829. var key = $(row).attr('pkey');
  1830. var tag = self.elem.element_box.find('li.selected_tag[itemvalue="'+key+'"]');
  1831. if(tag.length) ds.push(tag.data('dataObj'));
  1832. self.removeTag(self, tag);
  1833. });
  1834. self.afterAction(self, true);
  1835. if(p.eTagRemove && $.isFunction(p.eTagRemove)) p.eTagRemove(ds);
  1836. };
  1837. /**
  1838. * Clear all selected items
  1839. * @param {Object} self
  1840. * @param {boolean} open - open list after clear selected item
  1841. */
  1842. SelectPage.prototype.clearAll = function(self, open){
  1843. var p = self.option, ds = [];
  1844. if(p.multiple){
  1845. self.elem.element_box.find('li.selected_tag').each(function(i, row){
  1846. ds.push($(row).data('dataObj'));
  1847. row.remove();
  1848. });
  1849. self.elem.element_box.find('li.selected_tag').remove();
  1850. }
  1851. self.reset(self);
  1852. self.afterAction(self, open);
  1853. if(p.multiple) {
  1854. if (p.eTagRemove && $.isFunction(p.eTagRemove)) p.eTagRemove(ds);
  1855. }else self.elem.clear_btn.remove();
  1856. };
  1857. /**
  1858. * reset
  1859. */
  1860. SelectPage.prototype.reset = function(self){
  1861. self.elem.combo_input.val('');
  1862. self.elem.hidden.val('');
  1863. self.prop.prev_value = '';
  1864. self.prop.selected_text = '';
  1865. self.prop.current_page = 1;
  1866. };
  1867. /**
  1868. * Get current highlight item
  1869. * @param {Object} self
  1870. */
  1871. SelectPage.prototype.getCurrentLine = function(self) {
  1872. if (self.elem.result_area.is(':hidden')) return false;
  1873. var obj = self.elem.results.find('li.' + self.css_class.select);
  1874. if (obj.length) return obj;
  1875. else return false;
  1876. };
  1877. /**
  1878. * Check the result item is already selected or not
  1879. * @param {Object} self
  1880. * @param {Object} item - item info
  1881. */
  1882. SelectPage.prototype.isAlreadySelected = function(self,item){
  1883. var isExist = false;
  1884. if(item.value){
  1885. var keys = self.elem.hidden.val();
  1886. if(keys){
  1887. var karr = keys.split(',');
  1888. if(karr && karr.length && $.inArray(item.value,karr) != -1) isExist = true;
  1889. }
  1890. }
  1891. return isExist;
  1892. };
  1893. /**
  1894. * Add a new tag in multiple selection mode
  1895. * @param {Object} self
  1896. * @param {object} data - raw row data
  1897. * @param {Object} item
  1898. */
  1899. SelectPage.prototype.addNewTag = function(self, data, item){
  1900. if(!self.option.multiple || !data || !item) return;
  1901. var tmp = self.template.tag.content,tag;
  1902. tmp = tmp.replace(self.template.tag.textKey,item.text);
  1903. tmp = tmp.replace(self.template.tag.valueKey,item.value);
  1904. tag = $(tmp);
  1905. tag.data('dataObj', data);
  1906. if(self.elem.combo_input.prop('disabled')) tag.find('span.tag_close').hide();
  1907. self.elem.combo_input.closest('li').before(tag);
  1908. };
  1909. /**
  1910. * Remove a tag in multiple selection mode
  1911. * @param {Object} self
  1912. * @param {Object} item
  1913. */
  1914. SelectPage.prototype.removeTag = function(self,item){
  1915. var key = $(item).attr('itemvalue');
  1916. var keys = self.elem.hidden.val();
  1917. if($.type(key)!='undefined' && keys){
  1918. var keyarr = keys.split(','),
  1919. index = $.inArray(key.toString(),keyarr);
  1920. if(index != -1){
  1921. keyarr.splice(index,1);
  1922. self.elem.hidden.val(keyarr.toString());
  1923. }
  1924. }
  1925. $(item).remove();
  1926. self.inputResize(self);
  1927. };
  1928. /**
  1929. * Selected item value(keyField) put in to hidden element
  1930. * @param {Object} self
  1931. */
  1932. SelectPage.prototype.tagValuesSet = function(self){
  1933. if(!self.option.multiple) return;
  1934. var tags = self.elem.element_box.find('li.selected_tag');
  1935. if(tags && tags.length){
  1936. var result = new Array();
  1937. $.each(tags,function(i,li){
  1938. var v = $(li).attr('itemvalue');
  1939. if($.type(v)!=='undefined') result.push(v);
  1940. });
  1941. if(result.length){
  1942. self.elem.hidden.val(result.join(','));
  1943. }
  1944. }
  1945. };
  1946. /**
  1947. * auto resize input element width in multiple select mode
  1948. * @param {Object} self
  1949. */
  1950. SelectPage.prototype.inputResize = function(self){
  1951. if(!self.option.multiple) return;
  1952. var inputLi = self.elem.combo_input.closest('li');
  1953. var setDefaultSize = function(self,inputLi){
  1954. inputLi.removeClass('full_width');
  1955. var minimumWidth = self.elem.combo_input.val().length + 1,
  1956. width = (minimumWidth * 0.75) + 'em';
  1957. self.elem.combo_input.css('width', width).removeAttr('placeholder');
  1958. };
  1959. if(self.elem.element_box.find('li.selected_tag').length === 0){
  1960. if(self.elem.combo_input.attr('placeholder_bak')){
  1961. if(!inputLi.hasClass('full_width')) inputLi.addClass('full_width');
  1962. self.elem.combo_input.attr('placeholder',self.elem.combo_input.attr('placeholder_bak')).removeAttr('style');
  1963. }else setDefaultSize(self,inputLi);
  1964. }else setDefaultSize(self,inputLi);
  1965. };
  1966. /**
  1967. * Move to next line
  1968. * @param {Object} self
  1969. */
  1970. SelectPage.prototype.nextLine = function(self) {
  1971. var obj = self.getCurrentLine(self), idx;
  1972. if (!obj) idx = -1;
  1973. else {
  1974. idx = self.elem.results.children('li').index(obj);
  1975. obj.removeClass(self.css_class.select);
  1976. }
  1977. idx++;
  1978. if (idx < self.elem.results.children('li').length) {
  1979. var next = self.elem.results.children('li').eq(idx);
  1980. next.addClass(self.css_class.select);
  1981. self.setCssFocusedResults(self);
  1982. } else self.setCssFocusedInput(self);
  1983. self.scrollWindow(self, false);
  1984. };
  1985. /**
  1986. * Move to previous line
  1987. * @param {Object} self
  1988. */
  1989. SelectPage.prototype.prevLine = function(self) {
  1990. var obj = self.getCurrentLine(self), idx;
  1991. if (!obj) idx = self.elem.results.children('li').length;
  1992. else {
  1993. idx = self.elem.results.children('li').index(obj);
  1994. obj.removeClass(self.css_class.select);
  1995. }
  1996. idx--;
  1997. if (idx > -1) {
  1998. var prev = self.elem.results.children('li').eq(idx);
  1999. prev.addClass(self.css_class.select);
  2000. self.setCssFocusedResults(self);
  2001. } else self.setCssFocusedInput(self);
  2002. self.scrollWindow(self, false);
  2003. };
  2004. /**
  2005. * SelectPage plugin definition
  2006. * @global
  2007. * @param option {Object} init plugin option
  2008. */
  2009. function Plugin(option) {
  2010. return this.each(function(){
  2011. var $this = $(this),
  2012. data = $this.data(SelectPage.dataKey),
  2013. params = $.extend({}, defaults, $this.data(), data && data.option ,typeof option === 'object' && option);
  2014. if(!data) $this.data(SelectPage.dataKey,(data = new SelectPage(this,params)));
  2015. });
  2016. }
  2017. /**
  2018. * Get plugin object
  2019. * @param {object} obj
  2020. * @returns
  2021. */
  2022. function getPlugin(obj){
  2023. return $(obj).closest('div.sp_container').find('input.sp_input');
  2024. }
  2025. /**
  2026. * Clear all selected item
  2027. */
  2028. function ClearSelected(){
  2029. return this.each(function(){
  2030. var $this = getPlugin(this),
  2031. data = $this.data(SelectPage.dataKey);
  2032. if(data){
  2033. data.prop.init_set = true;
  2034. data.clearAll(data);
  2035. data.prop.init_set = false;
  2036. }
  2037. });
  2038. }
  2039. /**
  2040. * Refresh result list
  2041. * use case:
  2042. * 1.use $(obj).val('xxx') to modify selectpage selected item key
  2043. * 2.refresh selected item show content/tag text
  2044. */
  2045. function SelectedRefresh(){
  2046. return this.each(function(){
  2047. var $this = getPlugin(this),
  2048. data = $this.data(SelectPage.dataKey);
  2049. if(data && data.elem.hidden.val())
  2050. data.setInitRecord(true);
  2051. });
  2052. }
  2053. /**
  2054. * Modify plugin datasource, only work on json datasource mode
  2055. * @param {array} data - new datasource
  2056. * @example
  2057. * [{name:'aa',sex:1},{name:'bb',sex:0},{...}]
  2058. */
  2059. function ModifyDataSource(data){
  2060. return this.each(function(){
  2061. if(data && $.isArray(data)){
  2062. var $this = getPlugin(this),
  2063. plugin = $this.data(SelectPage.dataKey);
  2064. if(plugin){
  2065. plugin.clearAll(plugin);
  2066. plugin.option.data = data;
  2067. }
  2068. }
  2069. });
  2070. }
  2071. /**
  2072. * Get plugin disabled status or Modify plugin disabled status
  2073. * @param disabled {boolean} set disabled status
  2074. */
  2075. function PluginDisabled(disabled){
  2076. var status = false;
  2077. this.each(function(){
  2078. var $this = getPlugin(this),
  2079. plugin = $this.data(SelectPage.dataKey);
  2080. if(plugin) {
  2081. if($.type(disabled) !== 'undefined')
  2082. plugin.disabled(plugin, disabled);
  2083. else
  2084. status = plugin.disabled(plugin);
  2085. }
  2086. });
  2087. return status;
  2088. }
  2089. /**
  2090. * Get selected item text
  2091. * @returns {string}
  2092. */
  2093. function GetInputText(){
  2094. var str = '';
  2095. this.each(function(){
  2096. var $this = getPlugin(this),data = $this.data(SelectPage.dataKey);
  2097. if(data){
  2098. if(data.option.multiple){
  2099. var tags = [];
  2100. data.elem.element_box.find('li.selected_tag').each(function(i, tag){
  2101. tags.push($(tag).text());
  2102. });
  2103. str += tags.toString();
  2104. }else str += data.elem.combo_input.val();
  2105. }
  2106. });
  2107. return str;
  2108. }
  2109. var old = $.fn.selectPage;
  2110. $.fn.selectPage = Plugin;
  2111. $.fn.selectPage.Constructor = SelectPage;
  2112. $.fn.selectPageClear = ClearSelected;
  2113. $.fn.selectPageRefresh = SelectedRefresh;
  2114. $.fn.selectPageData = ModifyDataSource;
  2115. $.fn.selectPageDisabled = PluginDisabled;
  2116. $.fn.selectPageText = GetInputText;
  2117. // SelectPage no conflict
  2118. // =================
  2119. $.fn.selectPage.noConflict = function () {
  2120. $.fn.selectPage = old;
  2121. return this;
  2122. };
  2123. })(window.jQuery);