selectpage.js 75 KB

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