/OpusSuite/OpusSuite/Scripts/jquery.ui.combogrid-1.6.2.js

http://opussuite.codeplex.com · JavaScript · 1057 lines · 897 code · 52 blank · 108 comment · 154 complexity · 61b77a6dcd703577ca768c7359a990e8 MD5 · raw file

  1. /*
  2. * jQuery UI Combogrid 1.6.2
  3. *
  4. * Copyright 2011 D'Alia Sara
  5. * Dual licensed under the MIT or GPL Version 2 licenses.
  6. * http://jquery.org/license
  7. *
  8. *
  9. * Depends:
  10. * jquery.ui.core.js
  11. * jquery.ui.widget.js
  12. * jquery.ui.position.js
  13. * jquery.i18n.properties.js
  14. */
  15. (function( $, undefined ) {
  16. $.widget( "cg.combogrid", {
  17. options: {
  18. resetButton: false,
  19. resetFields: null,
  20. searchButton: false,
  21. searchIcon:false,
  22. okIcon:false,
  23. alternate: false,
  24. appendTo: "body",
  25. autoFocus: false,
  26. autoChoose : false,
  27. delayChoose : 300,
  28. delay: 300,
  29. rows : 10,
  30. addClass:null,
  31. addId:null,
  32. minLength: 0,
  33. munit: "%",
  34. position: {
  35. my: "left top",
  36. at: "left bottom",
  37. collision: "none"
  38. },
  39. url : null,
  40. colModel: null,
  41. sidx: "",
  42. sord: "",
  43. datatype : "json",
  44. debug : false,
  45. i18n: false,
  46. draggable:false,
  47. rememberDrag: false,
  48. replaceNull: false,
  49. rowsArray: [10,20,30],
  50. showOn:false,
  51. width:null
  52. },
  53. source: null,
  54. lastOrdered: "",
  55. cssCol: "",
  56. pending: 0,
  57. page : 1,
  58. rowNumber : 0,
  59. pos: null,
  60. _create: function() {
  61. var self = this,
  62. doc = this.element[ 0 ].ownerDocument,
  63. suppressKeyPress;
  64. if (self.options.resetButton){
  65. this.element.after('<span class="ui-state-default ui-corner-all '+self.element.attr('id') +' cg-resetButton"><span class="ui-icon ui-icon-circle-close"></span></span>');
  66. $('.' +self.element.attr('id') +'.cg-resetButton').bind('click.combogrid', function(){
  67. self.element.val('');
  68. self.term=self.element.val();
  69. if(self.options.okIcon){
  70. $('.' +self.element.attr('id') +'.ok-icon').remove();
  71. $('.' +self.element.attr('id') + '.notok-icon').remove();
  72. if(self.options.resetButton){
  73. $('.' +self.element.attr('id') +'.cg-resetButton').after('<span class="'+ self.element.attr('id') +' notok-icon"></span>');
  74. }else if(self.options.searchButton){
  75. $('.' +self.element.attr('id') +'.cg-searchButton').after('<span class="'+ self.element.attr('id') +' notok-icon"></span>');
  76. }else{
  77. self.element.after('<span class="'+ self.element.attr('id') +' notok-icon"></span>');
  78. }
  79. }
  80. //keyup trigger leaved for backward compatibility
  81. self.element.trigger('keyup');
  82. if(self.options.resetFields!=null){
  83. $.each(self.options.resetFields, function() {
  84. $( ''+this ).val('');
  85. });
  86. }
  87. });
  88. }
  89. if (self.options.searchButton){
  90. this.element.after('<span class="ui-state-default ui-corner-all '+ self.element.attr('id') +' cg-searchButton"><span class="ui-icon ui-icon-search"></span></span>');
  91. $('.' +self.element.attr('id') +'.cg-searchButton').bind('click.combogrid', function(){
  92. self.element.trigger('focus.combogrid');
  93. self._search(self.element.val());
  94. self.element.trigger('focus.combogrid');
  95. });
  96. }
  97. if(self.options.showOn){
  98. this.element.focus(function(){
  99. self._search(self.element.val());
  100. });
  101. }
  102. this.element
  103. .addClass( "ui-autocomplete-input" )
  104. .attr( "autocomplete", "off" )
  105. // TODO verify these actually work as intended
  106. .attr({
  107. role: "textbox",
  108. "aria-autocomplete": "list",
  109. "aria-haspopup": "true"
  110. })
  111. .bind( "keydown.combogrid", function( event ) {
  112. if ( self.options.disabled || self.element.attr( "readonly" ) ) {
  113. return;
  114. }
  115. suppressKeyPress = false;
  116. var keyCode = $.ui.keyCode;
  117. switch( event.keyCode ) {
  118. case keyCode.LEFT:
  119. $('.' +self.element.attr('id') +'.cg-keynav-prev').trigger('click.combogrid');
  120. break;
  121. case keyCode.PAGE_UP:
  122. self._move( "previousPage", event );
  123. break;
  124. case keyCode.RIGHT:
  125. $('.' +self.element.attr('id') +'.cg-keynav-next').trigger('click.combogrid');
  126. break;
  127. case keyCode.PAGE_DOWN:
  128. self._move( "nextPage", event );
  129. break;
  130. case keyCode.UP:
  131. self._move( "previous", event );
  132. // prevent moving cursor to beginning of text field in some browsers
  133. event.preventDefault();
  134. break;
  135. case keyCode.DOWN:
  136. self._move( "next", event );
  137. // prevent moving cursor to end of text field in some browsers
  138. event.preventDefault();
  139. break;
  140. case keyCode.ENTER:
  141. case keyCode.NUMPAD_ENTER:
  142. // when menu is open and has focus
  143. if ( self.menucombo.active ) {
  144. // #6055 - Opera still allows the keypress to occur
  145. // which causes forms to submit
  146. suppressKeyPress = true;
  147. event.preventDefault();
  148. }
  149. //passthrough - ENTER and TAB both select the current element
  150. case keyCode.TAB:
  151. if ( !self.menucombo.active ) {
  152. return;
  153. }
  154. self.menucombo.select( event );
  155. break;
  156. case keyCode.DELETE:
  157. if(self.options.okIcon){
  158. $('.' +self.element.attr('id') +'.ok-icon').remove();
  159. $('.' +self.element.attr('id') +'.notok-icon').remove();
  160. if(self.options.resetButton){
  161. $('.' +self.element.attr('id') +'.cg-resetButton').after('<span class="' + self.element.attr('id') +' notok-icon"></span>');
  162. }else if(self.options.searchButton){
  163. $('.' +self.element.attr('id') +'.cg-searchButton').after('<span class="' + self.element.attr('id') +' notok-icon"></span>');
  164. }else{
  165. self.element.after('<span class="' + self.element.attr('id') +' notok-icon"></span>');
  166. }
  167. }
  168. if(self.options.resetFields!=null){
  169. $.each(self.options.resetFields, function() {
  170. $( ''+this ).val('');
  171. });
  172. }
  173. self.element.val('');
  174. //Introduced in 1.5.1 to trigger search on DELETE input field
  175. /* // keypress is triggered before the input value is changed
  176. clearTimeout( self.searching );
  177. self.searching = setTimeout(function() {
  178. // only search if the value has changed
  179. if ( self.term != self.element.val()) {
  180. self.selectedItem = null;
  181. self.search( null, event );
  182. }
  183. }, self.options.delay );*/
  184. break;
  185. case keyCode.ESCAPE:
  186. self.element.val( self.term );
  187. self.close( event );
  188. //ESCAPE needs this workaround
  189. $('.' +self.element.attr('id') +'.ok-icon').remove();
  190. $('.' +self.element.attr('id') +'.notok-icon').remove();
  191. if(self.options.okIcon){
  192. if(self.options.resetButton){
  193. $('.' +self.element.attr('id') + '.cg-resetButton').after('<span class="'+self.element.attr('id') +' ok-icon"></span>');
  194. }else if(self.options.searchButton){
  195. $('.' +self.element.attr('id') +'.cg-searchButton').after('<span class="'+self.element.attr('id') +' ok-icon"></span>');
  196. }else{
  197. self.element.after('<span class="'+self.element.attr('id') +' ok-icon"></span>');
  198. }
  199. }
  200. break;
  201. default:
  202. if(self.options.okIcon){
  203. $('.' +self.element.attr('id') +'.ok-icon').remove();
  204. $('.' +self.element.attr('id') +'.notok-icon').remove();
  205. if(self.options.resetButton){
  206. $('.' +self.element.attr('id') +'.cg-resetButton').after('<span class="'+ self.element.attr('id') +' notok-icon"></span>');
  207. }else if(self.options.searchButton){
  208. $('.' +self.element.attr('id') +'.cg-searchButton').after('<span class="'+self.element.attr('id') +' notok-icon"></span>');
  209. }else{
  210. self.element.after('<span class="'+self.element.attr('id') +' notok-icon"></span>');
  211. }
  212. }
  213. // keypress is triggered before the input value is changed
  214. clearTimeout( self.searching );
  215. self.searching = setTimeout(function() {
  216. // only search if the value has changed
  217. // if ( self.term != self.element.val()) {
  218. self.selectedItem = null;
  219. self.search( null, event );
  220. // }
  221. }, self.options.delay );
  222. break;
  223. }
  224. })
  225. .bind( "keypress.combogrid", function( event ) {
  226. if ( suppressKeyPress ) {
  227. suppressKeyPress = false;
  228. event.preventDefault();
  229. }
  230. })
  231. .bind( "focus.combogrid", function() {
  232. if ( self.options.disabled ) {
  233. return;
  234. }
  235. self.selectedItem = null;
  236. self.previous = self.element.val();
  237. })
  238. .bind( "blur.combogrid", function( event ) {
  239. if ( self.options.disabled ) {
  240. return;
  241. }
  242. //preventing from closing when a button trigger a search
  243. if(self.options.searchButton){
  244. if(self.menucombo.element.is(":visible")){
  245. clearTimeout( self.searching );
  246. self.closing = setTimeout(function() {
  247. self.close( event );
  248. self._change( event );
  249. }, 70 );
  250. }
  251. }else{
  252. clearTimeout( self.searching );
  253. // clicks on the menu (or a button to trigger a search) will cause a blur event
  254. self.closing = setTimeout(function() {
  255. self.close( event );
  256. self._change( event );
  257. }, 150 );
  258. }
  259. });
  260. if (this.options.searchIcon){
  261. this.element.addClass("input-bg");
  262. }
  263. this.options.source = function( request, response ) {
  264. $.ajax({
  265. url: self.options.url,
  266. dataType: self.options.datatype,
  267. data: {
  268. sidx: self.options.sidx,
  269. page : self.page,
  270. sord : self.options.sord,
  271. rows: self.options.rows,
  272. searchTerm : request.term
  273. },
  274. success: function( data ) {
  275. if(data.records==0) {
  276. self.pending--;
  277. if ( !self.pending ) {
  278. self.element.removeClass( "cg-loading" );
  279. self.close();
  280. }
  281. } else if(data.records==1) {
  282. response(data.records, data.total, $.map( data.rows, function( item ) {
  283. return item;
  284. }));
  285. self.menucombo.activate($.Event({ type: "mouseenter" }), self.menucombo.element.children(".cg-menu-item:first"));
  286. if(self.options.autoChoose){
  287. setTimeout(function() {
  288. self.menucombo._trigger("selected", $.Event({ type: "click" }), { item: self.menucombo.active });
  289. }, self.options.delayChoose);
  290. }
  291. } else{
  292. response(data.records, data.total, $.map( data.rows, function( item ) {
  293. return item;
  294. }));
  295. }
  296. }
  297. });
  298. };
  299. this._initSource();
  300. this.response = function() {
  301. return self._response.apply( self, arguments );
  302. };
  303. this.menucombo = $( "<div></div>" )
  304. .addClass( "cg-autocomplete" )
  305. .appendTo( $( this.options.appendTo || "body", doc )[0] )
  306. // prevent the close-on-blur in case of a "slow" click on the menu (long mousedown)
  307. .mousedown(function( event ) {
  308. // clicking on the scrollbar causes focus to shift to the body
  309. // but we can't detect a mouseup or a click immediately afterward
  310. // so we have to track the next mousedown and close the menu if
  311. // the user clicks somewhere outside of the autocomplete
  312. var menuElement = self.menucombo.element[ 0 ];
  313. if ( !$( event.target ).closest( ".cg-menu-item" ).length ) {
  314. setTimeout(function() {
  315. $( document ).one( 'mousedown', function( event ) {
  316. if ( event.target !== self.element[ 0 ] &&
  317. event.target !== menuElement &&
  318. !$.ui.contains( menuElement, event.target ) ) {
  319. self.close();
  320. }
  321. });
  322. }, 1 );
  323. }
  324. // use another timeout to make sure the blur-event-handler on the input was already triggered
  325. setTimeout(function() {
  326. clearTimeout( self.closing );
  327. }, 13);
  328. })
  329. .menucombo({
  330. focus: function( event, ui ) {
  331. var item = ui.item.data( "item.combogrid" );
  332. if ( false !== self._trigger( "focus", event, { item: item } ) ) {
  333. // use value to match what will end up in the input, if it was a key event
  334. if ( /^key/.test(event.originalEvent.type) ) {
  335. if(item.value!=undefined)
  336. self.element.val( item.value );
  337. }
  338. }
  339. },
  340. selected: function( event, ui ) {
  341. var item = ui.item.data( "item.combogrid" ),
  342. previous = self.previous;
  343. // only trigger when focus was lost (click on menu)
  344. if ( self.element[0] !== doc.activeElement ) {
  345. if(!self.options.showOn){
  346. self.element.focus();
  347. }
  348. self.previous = previous;
  349. // #6109 - IE triggers two focus events and the second
  350. // is asynchronous, so we need to reset the previous
  351. // term synchronously and asynchronously :-(
  352. setTimeout(function() {
  353. self.previous = previous;
  354. self.selectedItem = item;
  355. }, 1);
  356. }
  357. if ( false !== self._trigger( "select", event, { item: item } ) ) {
  358. self.element.val( item.value );
  359. }
  360. // reset the term after the select event
  361. // this allows custom select handling to work properly
  362. self.term = self.element.val();
  363. self.close( event );
  364. self.selectedItem = item;
  365. if(self.options.okIcon){
  366. $('.' +self.element.attr('id') +'.ok-icon').remove();
  367. $('.' +self.element.attr('id') +'.notok-icon').remove();
  368. if(self.options.resetButton){
  369. $('.' +self.element.attr('id') +'.cg-resetButton').after('<span class="'+ self.element.attr('id') +' ok-icon"></span>');
  370. }else if(self.options.searchButton){
  371. $('.' +self.element.attr('id') +'.cg-searchButton').after('<span class="'+self.element.attr('id') +' ok-icon"></span>');
  372. }else{
  373. self.element.after('<span class="'+self.element.attr('id') +' ok-icon"></span>');
  374. }
  375. }
  376. },
  377. blur: function( event, ui ) {
  378. // don't set the value of the text field if it's already correct
  379. // this prevents moving the cursor unnecessarily
  380. if ( self.menucombo.element.is(":visible") &&
  381. ( self.element.val() !== self.term ) ) {
  382. // self.element.val( self.term );
  383. }
  384. }
  385. })
  386. .zIndex( this.element.zIndex() + 1 )
  387. // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
  388. .css({ top: 0, left: 0 })
  389. .hide()
  390. .data( "menucombo" );
  391. if(this.options.draggable){
  392. this.menucombo.element.draggable({
  393. stop: function(event, ui) {
  394. self.pos = ui.position;
  395. }
  396. });
  397. }
  398. if ( $.fn.bgiframe ) {
  399. this.menucombo.element.bgiframe();
  400. }
  401. if (this.options.addClass!=null){
  402. this.menucombo.element.addClass( this.options.addClass );
  403. }
  404. if (this.options.addId!=null){
  405. this.menucombo.element.attr('id', this.options.addId );
  406. }
  407. },
  408. destroy: function() {
  409. this.element
  410. .removeClass( "cg-autocomplete-input" )
  411. .removeAttr( "autocomplete" )
  412. .removeAttr( "role" )
  413. .removeAttr( "aria-autocomplete" )
  414. .removeAttr( "aria-haspopup" );
  415. this.menucombo.element.remove();
  416. $.Widget.prototype.destroy.call( this );
  417. },
  418. _setOption: function( key, value ) {
  419. $.Widget.prototype._setOption.apply( this, arguments );
  420. if ( key === "source" ) {
  421. this._initSource();
  422. }
  423. if ( key === "appendTo" ) {
  424. this.menucombo.element.appendTo( $( value || "body", this.element[0].ownerDocument )[0] );
  425. }
  426. if ( key === "disabled" && value && this.xhr ) {
  427. this.xhr.abort();
  428. }
  429. },
  430. _initSource: function() {
  431. var self = this,
  432. array,
  433. url;
  434. if ( $.isArray(this.options.source) ) {
  435. array = this.options.source;
  436. this.source = function( request, response ) {
  437. response( $.cg.combogrid.filter(array, request.term) );
  438. };
  439. } else if ( typeof this.options.source === "string" ) {
  440. url = this.options.source;
  441. this.source = function( request, response ) {
  442. if ( self.xhr ) {
  443. self.xhr.abort();
  444. }
  445. self.xhr = $.ajax({
  446. url: url,
  447. data: request,
  448. dataType: "json",
  449. success: function( data, status, xhr ) {
  450. if ( xhr === self.xhr ) {
  451. response( data );
  452. }
  453. self.xhr = null;
  454. },
  455. error: function( xhr ) {
  456. if ( xhr === self.xhr ) {
  457. response( [] );
  458. }
  459. self.xhr = null;
  460. }
  461. });
  462. };
  463. } else {
  464. this.source = this.options.source;
  465. }
  466. },
  467. search: function( value, event ) {
  468. value = value != null ? value : this.element.val();
  469. //reset pagination values on a new term search
  470. this.page = 1;
  471. this.rows = 10;
  472. // always save the actual value, not the one passed as an argument
  473. this.term = this.element.val();
  474. if ( value.length < this.options.minLength) {
  475. return this.close( event );
  476. }
  477. clearTimeout( this.closing );
  478. if ( this._trigger( "search", event ) === false ) {
  479. return;
  480. }
  481. if(!this.options.searchButton){
  482. return this._search( value );
  483. }
  484. },
  485. _search: function( value ) {
  486. this.pending++;
  487. this.element.addClass( "cg-loading" );
  488. this.source( { term: value }, this.response );
  489. },
  490. _response: function(records, total, content ) {
  491. if ( !this.options.disabled && content && content.length ) {
  492. //content = this._normalize( content );
  493. this._suggest(records, total, content );
  494. this._trigger( "open" );
  495. } else {
  496. this.close();
  497. }
  498. this.pending--;
  499. if ( !this.pending ) {
  500. this.element.removeClass( "cg-loading" );
  501. }
  502. },
  503. close: function( event ) {
  504. var self = this;
  505. clearTimeout( this.closing );
  506. if ( this.menucombo.element.is(":visible") ) {
  507. this.menucombo.element.hide();
  508. this.menucombo.deactivate();
  509. $('.' +self.element.attr('id') +'.cg-keynav-next').unbind('click.combogrid');
  510. $('.' +self.element.attr('id') +'.cg-keynav-prev').unbind('click.combogrid');
  511. $('.' +self.element.attr('id') +'.cg-keynav-last').unbind('click.combogrid');
  512. $('.' +self.element.attr('id') +'.cg-keynav-first').unbind('click.combogrid');
  513. if(!this.options.debug) this.menucombo.element.empty();
  514. this.options.sidx=self.options.sidx;
  515. this.cssCol="";
  516. this.lastOrdered="";
  517. this.options.rows=10;
  518. if(!this.options.rememberDrag){
  519. this.pos=null;
  520. }
  521. this._trigger( "close", event );
  522. }
  523. },
  524. _change: function( event ) {
  525. if ( this.previous !== this.element.val() ) {
  526. this._trigger( "change", event, { item: this.selectedItem } );
  527. }
  528. },
  529. _normalize: function( items ) {
  530. // assume all items have the right format when the first item is complete
  531. if ( items.length && items[0].label && items[0].value ) {
  532. return items;
  533. }
  534. return $.map( items, function(item) {
  535. if ( typeof item === "string" ) {
  536. return {
  537. label: item,
  538. value: item
  539. };
  540. }
  541. return $.extend({
  542. //label: item.label || item.value,
  543. //value: item.value || item.label
  544. value: $.parseJSON(item)
  545. }, item );
  546. });
  547. },
  548. _suggest: function(records, total, items ) {
  549. var self = this;
  550. var ul = this.menucombo.element
  551. .empty()
  552. .zIndex( this.element.zIndex() + 1 );
  553. $('.' +self.element.attr('id') +'.cg-keynav-next').unbind('click.combogrid');
  554. $('.' +self.element.attr('id') +'.cg-keynav-prev').unbind('click.combogrid');
  555. $('.' +self.element.attr('id') +'.cg-keynav-last').unbind('click.combogrid');
  556. $('.' +self.element.attr('id') +'.cg-keynav-first').unbind('click.combogrid');
  557. $('.cg-colHeader-label').unbind('click.combogrid');
  558. this._renderHeader(ul, this.options.colModel );
  559. this._renderMenu( ul, items, this.options.colModel );
  560. // var ul2 = $("<ul class='cg-menu'></ul>");
  561. // this._renderMenu( ul2, items, this.options.colModel );
  562. // ul2.appendTo(ul);
  563. this._renderPager( ul, records, total );
  564. // TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate
  565. this.menucombo.deactivate();
  566. this.menucombo.refresh();
  567. // size and position menu
  568. ul.show();
  569. this._resizeMenu();
  570. if(this.pos==null){
  571. ul.position( $.extend({
  572. of: this.element
  573. }, this.options.position ));
  574. }
  575. if ( this.options.autoFocus ) {
  576. this.menucombo.next( new $.Event("mouseover") );
  577. }
  578. },
  579. _resizeMenu: function() {
  580. var ul = this.menucombo.element;
  581. if(this.options.width!=null){
  582. ul.css('width',this.options.width);
  583. }else{
  584. /*alert(Math.max(
  585. ul.width( "" ).outerWidth(),
  586. this.element.outerWidth()
  587. ));
  588. ul.outerWidth( Math.max(
  589. ul.width( "" ).outerWidth(),
  590. this.element.outerWidth()
  591. ));*/
  592. }
  593. },
  594. _renderHeader: function(ul, colModel ) {
  595. var self = this;
  596. div = $('<div id="cg-divHeader" class="ui-state-default">');
  597. $.each( colModel, function( index, col ) {
  598. if(col.width==undefined){col.width=100/colModel.length;}
  599. if(col.align==undefined){col.align="center";}
  600. var hide = "";
  601. if(col.hidden!=undefined&&col.hidden){
  602. hide="display:none;";
  603. if(col.width!=undefined) col.width=0;
  604. }
  605. // Check if column is ordered or not to provide asc/desc icon
  606. if(col.columnName==self.cssCol){
  607. div.append('<div class="cg-colHeader" style="width:'+col.width+self.options.munit+';'+hide+' text-align:'+col.align+'"><label class="cg-colHeader-label" id="'+ col.columnName +'">'
  608. +self._renderLabel(col.label)
  609. +'</label><span class="cg-colHeader '+ self.options.sord +'"></span></div>');
  610. } else {
  611. div.append('<div class="cg-colHeader" style="width:'+col.width+self.options.munit+';'+hide+' text-align:'+col.align+'"><label class="cg-colHeader-label" id="'+ col.columnName +'">'
  612. +self._renderLabel(col.label)
  613. +'</label></div>');
  614. }
  615. });
  616. div.append('</div').appendTo(ul);
  617. if(this.options.draggable){
  618. $('#cg-divHeader').css("cursor","move");
  619. }
  620. $(".cg-colHeader-label").bind('click.combogrid',function(){
  621. self.options.sord="";
  622. self.cssCol = "";
  623. value = $(this).attr('id');
  624. self.cssCol = value;
  625. if(self.lastOrdered==value) {
  626. self.lastOrdered = "";
  627. self.options.sord = "desc";
  628. } else {
  629. self.lastOrdered = value;
  630. self.options.sord = "asc";
  631. }
  632. self.options.sidx = value;
  633. self._search(self.term);
  634. });
  635. },
  636. _renderLabel: function (label){
  637. if(this.options.i18n){
  638. return $.i18n.prop(label);
  639. } else {
  640. return label;
  641. }
  642. },
  643. _renderMenu: function( ul, items, colModel ) {
  644. var self = this;
  645. $.each( items, function( index, item ) {
  646. self._renderItem( ul, item, colModel );
  647. });
  648. },
  649. _renderItem: function( ul, item, colModel) {
  650. var self = this;
  651. this.rowNumber++;
  652. div = $("<div class='cg-colItem'>");
  653. $.each( colModel, function( index, col ) {
  654. if(col.width==undefined){col.width=100/colModel.length;}
  655. if(col.align==undefined){col.align="center";}
  656. var hide = "";
  657. if(col.hidden!=undefined&&col.hidden){
  658. hide="display:none;";
  659. }
  660. var colItem;
  661. if(item[col.columnName]!=null && typeof item[col.columnName] === "object"){
  662. subItem = item[col.columnName];
  663. colItem = subItem[col.subName]
  664. }else if(item[col.columnName]==null && self.options.replaceNull){
  665. colItem = "";
  666. }else{
  667. colItem=item[col.columnName];
  668. }
  669. $("<div style='width:"+col.width+self.options.munit+";"+hide+" text-align:"+col.align+"' class='cg-DivItem'>"+ colItem +"</div>").appendTo(div);
  670. });
  671. div.append("</div>");
  672. if(self.options.alternate){
  673. if(this.rowNumber%2==0){
  674. return $( "<div class='cg-comboItem-even'></div>" ).data( "item.combogrid", item ).append(div).appendTo(ul);
  675. } else {
  676. return $( "<div class='cg-comboItem-odd'></div>" ).data( "item.combogrid", item ).append(div).appendTo(ul);
  677. }
  678. }else{
  679. return $( "<div class='cg-comboItem'></div>" ).data( "item.combogrid", item ).append(div).appendTo(ul);
  680. }
  681. },
  682. _renderPager: function( ul, records, total ) {
  683. var self = this;
  684. var initRecord = ((self.page*self.options.rows)-self.options.rows)+1;
  685. var lastRecord = 0;
  686. if (self.page<total){
  687. lastRecord = (self.page*self.options.rows);
  688. } else {
  689. lastRecord = records;
  690. }
  691. div = $("<div class='cg-comboButton ui-state-default'>");
  692. $("<table cellspacing='0' cellpadding='0' border='0' class='cg-navTable'>"
  693. +"<tbody>"
  694. +"<td align='center' style='white-space: pre; width: 264px;' id='cg-keynav-center'>"
  695. +"<table cellspacing='0' cellpadding='0' border='0' class='cg-pg-table' style='table-layout: auto;'>"
  696. +"<tbody>"
  697. +"<tr>"
  698. +"<td class='cg-pg-button ui-corner-all cg-state-disabled cg-keynav-first " +self.element.attr('id') + "'>"
  699. +"<span class='ui-icon ui-icon-seek-first'></span>"
  700. +"</td>"
  701. +"<td class='cg-pg-button ui-corner-all cg-state-disabled cg-keynav-prev " +self.element.attr('id') + "'>"
  702. +"<span class='ui-icon ui-icon-seek-prev'></span>"
  703. +"</td>"
  704. +"<td style='width: 4px;' class='cg-state-disabled'>"
  705. +"<span class='ui-separator'></span>"
  706. +"</td>"
  707. +"<td dir='ltr' id='cg-navInfo'>"
  708. +self._renderPagerPage('page', self.page, total)
  709. +"</td>"
  710. +"<td style='width: 4px;' class='cg-state-disabled'>"
  711. +"<span class='ui-separator'></span>"
  712. +"</td>"
  713. +"<td class='cg-pg-button ui-corner-all cg-keynav-next " +self.element.attr('id') + "'>"
  714. +"<span class='ui-icon ui-icon-seek-next'></span>"
  715. +"</td>"
  716. +"<td class='cg-pg-button ui-corner-all cg-keynav-last " +self.element.attr('id') + "'>"
  717. +"<span class='ui-icon ui-icon-seek-end'></span>"
  718. +"</td>"
  719. // Select page
  720. +"<td dir='ltr'>"
  721. +"<select class='" +self.element.attr('id') + " recordXP'>"
  722. +"</select>"
  723. +"</td>"
  724. +"</tr>"
  725. +"</tbody>"
  726. +"</table>"
  727. +"</td>"
  728. +"<td align='right' id='cg-keynav-right'>"
  729. +"<div class='ui-paging-info' style='text-align: right;' dir='ltr'>"
  730. + self._renderPagerView('recordtext', initRecord, lastRecord, records)
  731. +"</div>"
  732. +"</td>"
  733. +"</tr>"
  734. +"</tbody>"
  735. +"</table>").appendTo(div);
  736. div.append("</div>");
  737. div.appendTo(ul);
  738. $.each(self.options.rowsArray, function( index, value ) {
  739. $('.' +self.element.attr('id') +'.recordXP').append("<option value='"+ value +"' role='option'>"+ value +"</option>");
  740. });
  741. $('.' +self.element.attr('id') +'.recordXP').val(self.options.rows);
  742. if(self.page>1){
  743. $('.' +self.element.attr('id') +'.cg-keynav-first').removeClass("cg-state-disabled");
  744. $('.' +self.element.attr('id') +'.cg-keynav-prev').removeClass("cg-state-disabled");
  745. } else {
  746. $('.' +self.element.attr('id') +'.cg-keynav-first').addClass("cg-state-disabled");
  747. $('.' +self.element.attr('id') +'.cg-keynav-prev').addClass("cg-state-disabled");
  748. };
  749. if(self.page==total){
  750. $('.' +self.element.attr('id') +'.cg-keynav-next').addClass("cg-state-disabled");
  751. $('.' +self.element.attr('id') +'.cg-keynav-last').addClass("cg-state-disabled");
  752. };
  753. $('.' +self.element.attr('id') +'.cg-keynav-next').bind('click.combogrid',function(){
  754. if(self.page<total){
  755. self.page++;
  756. self._search(self.term);
  757. }
  758. });
  759. $('.' +self.element.attr('id') +'.cg-keynav-prev').bind('click.combogrid',function(){
  760. if(self.page>1){
  761. self.page--;
  762. self._search(self.term);
  763. }
  764. });
  765. $('.' +self.element.attr('id') +'.cg-keynav-last').bind('click.combogrid',function(){
  766. if( total>1 && self.page<total ){
  767. self.page=total;
  768. self._search(self.term);
  769. }
  770. });
  771. $('.' +self.element.attr('id') +'.cg-keynav-first').bind('click.combogrid',function(){
  772. if( total>1 && self.page>1){
  773. self.page=1;
  774. self._search(self.term);
  775. }
  776. });
  777. $('.' +self.element.attr('id') +'.currentPage').keypress(function(e) {
  778. var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
  779. if(key == 13) {
  780. if(!isNaN($(this).val()) && $(this).val()!=0){
  781. if($(this).val()>total){
  782. self.page=total;
  783. }else{
  784. self.page=$(this).val();
  785. }
  786. self._search(self.term);
  787. }
  788. }
  789. });
  790. $('.' +self.element.attr('id') +'.recordXP').bind('change',function() {
  791. self.options.rows=this.value;
  792. self.page=1;
  793. self._search(self.term);
  794. });
  795. return div;
  796. },
  797. _renderPagerPage: function (label, page, total){
  798. var self = this;
  799. if(this.options.i18n){
  800. return $.i18n.prop('page')+' <input type="text" size="1" class="' +self.element.attr('id')+ ' currentPage" value="'+ page + '"></input> ' + $.i18n.prop('of') + ' ' + total;
  801. } else {
  802. return 'Page <input type="text" size="1" class="' +self.element.attr('id')+ ' currentPage" value="' + page + '"></input> of ' + total;
  803. }
  804. },
  805. _renderPagerView: function (label, initRecord, lastRecord, records){
  806. if(this.options.i18n){
  807. return $.i18n.prop(label, initRecord, lastRecord, records);
  808. } else {
  809. return "View " + initRecord + " - " + lastRecord + " of " + records;
  810. }
  811. },
  812. _move: function( direction, event ) {
  813. if ( !this.menucombo.element.is(":visible") ) {
  814. this.search( null, event );
  815. return;
  816. }
  817. if ( this.menucombo.first() && /^previous/.test(direction) ||
  818. this.menucombo.last() && /^next/.test(direction) ) {
  819. this.element.val( this.term );
  820. this.menucombo.deactivate();
  821. return;
  822. }
  823. this.menucombo[ direction ]( event );
  824. },
  825. widget: function() {
  826. return this.menucombo.element;
  827. }
  828. });
  829. $.extend( $.cg.combogrid, {
  830. escapeRegex: function( value ) {
  831. return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
  832. },
  833. filter: function(array, term) {
  834. var matcher = new RegExp( $.cg.combogrid.escapeRegex(term), "i" );
  835. return $.grep( array, function(value) {
  836. return matcher.test( value.label || value.value || value );
  837. });
  838. }
  839. });
  840. }( jQuery ));
  841. /*
  842. * jQuery UI Menu (not officially released)
  843. *
  844. * This widget isn't yet finished and the API is subject to change. We plan to finish
  845. * it for the next release. You're welcome to give it a try anyway and give us feedback,
  846. * as long as you're okay with migrating your code later on. We can help with that, too.
  847. *
  848. * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
  849. * Dual licensed under the MIT or GPL Version 2 licenses.
  850. * http://jquery.org/license
  851. *
  852. * http://docs.jquery.com/UI/Menu
  853. *
  854. * Depends:
  855. * jquery.ui.core.js
  856. * jquery.ui.widget.js
  857. */
  858. (function($) {
  859. $.widget("cg.menucombo", {
  860. _create: function() {
  861. var self = this;
  862. this.element
  863. .addClass("cg-menu ui-widget ui-widget-content ui-corner-all combogrid")
  864. .attr({
  865. role: "listbox",
  866. "aria-activedescendant": "ui-active-menuitem"
  867. })
  868. .click(function( event ) {
  869. if ( !$( event.target ).closest( ".cg-menu-item div" ).length ) {
  870. return;
  871. }
  872. // temporary
  873. event.preventDefault();
  874. self.select( event );
  875. });
  876. this.refresh();
  877. },
  878. refresh: function() {
  879. var self = this;
  880. // don't refresh list items that are already adapted
  881. var items = this.element.children("div:not(.cg-menu-item):not(#cg-divHeader):not(.cg-comboButton):has(div)")
  882. .addClass("cg-menu-item")
  883. .attr("role", "menuitem");
  884. items.children("div")
  885. .addClass("ui-corner-all")
  886. .attr("tabindex", -1)
  887. // mouseenter doesn't work with event delegation
  888. .mouseenter(function( event ) {
  889. self.activate( event, $(this).parent() );
  890. })
  891. .mouseleave(function() {
  892. self.deactivate();
  893. });
  894. },
  895. activate: function( event, item ) {
  896. this.deactivate();
  897. if (this.hasScroll()) {
  898. var offset = item.offset().top - this.element.offset().top,
  899. scroll = this.element.attr("scrollTop"),
  900. elementHeight = this.element.height();
  901. if (offset < 0) {
  902. this.element.attr("scrollTop", scroll + offset);
  903. } else if (offset >= elementHeight) {
  904. this.element.attr("scrollTop", scroll + offset - elementHeight + item.height());
  905. }
  906. }
  907. this.active = item.eq(0)
  908. //.children("div")
  909. .addClass("ui-state-hover")
  910. .attr("id", "ui-active-menuitem")
  911. .end();
  912. this._trigger("focus", event, { item: item });
  913. },
  914. deactivate: function() {
  915. if (!this.active) { return; }
  916. this.active
  917. //.children("div")
  918. .removeClass("ui-state-hover")
  919. .removeAttr("id");
  920. this._trigger("blur");
  921. this.active = null;
  922. },
  923. next: function(event) {
  924. this.move("next", ".cg-menu-item:first", event);
  925. },
  926. previous: function(event) {
  927. this.move("prev", ".cg-menu-item:last", event);
  928. },
  929. first: function() {
  930. return this.active && !this.active.prevAll(".cg-menu-item").length;
  931. },
  932. last: function() {
  933. return this.active && !this.active.nextAll(".cg-menu-item").length;
  934. },
  935. move: function(direction, edge, event) {
  936. if (!this.active) {
  937. this.activate(event, this.element.children(edge));
  938. return;
  939. }
  940. var next = this.active[direction + "All"](".cg-menu-item").eq(0);
  941. if (next.length) {
  942. this.activate(event, next);
  943. } else {
  944. this.activate(event, this.element.children(edge));
  945. }
  946. },
  947. // TODO merge with previousPage
  948. nextPage: function(event) {
  949. if (this.hasScroll()) {
  950. // TODO merge with no-scroll-else
  951. if (!this.active || this.last()) {
  952. this.activate(event, this.element.children(".cg-menu-item:first"));
  953. return;
  954. }
  955. var base = this.active.offset().top,
  956. height = this.element.height(),
  957. result = this.element.children(".cg-menu-item").filter(function() {
  958. var close = $(this).offset().top - base - height + $(this).height();
  959. // TODO improve approximation
  960. return close < 10 && close > -10;
  961. });
  962. // TODO try to catch this earlier when scrollTop indicates the last page anyway
  963. if (!result.length) {
  964. result = this.element.children(".cg-menu-item:last");
  965. }
  966. this.activate(event, result);
  967. } else {
  968. this.activate(event, this.element.children(".cg-menu-item")
  969. .filter(!this.active || this.last() ? ":first" : ":last"));
  970. }
  971. },
  972. // TODO merge with nextPage
  973. previousPage: function(event) {
  974. if (this.hasScroll()) {
  975. // TODO merge with no-scroll-else
  976. if (!this.active || this.first()) {
  977. this.activate(event, this.element.children(".cg-menu-item:last"));
  978. return;
  979. }
  980. var base = this.active.offset().top,
  981. height = this.element.height();
  982. result = this.element.children(".cg-menu-item").filter(function() {
  983. var close = $(this).offset().top - base + height - $(this).height();
  984. // TODO improve approximation
  985. return close < 10 && close > -10;
  986. });
  987. // TODO try to catch this earlier when scrollTop indicates the last page anyway
  988. if (!result.length) {
  989. result = this.element.children(".cg-menu-item:first");
  990. }
  991. this.activate(event, result);
  992. } else {
  993. this.activate(event, this.element.children(".cg-menu-item")
  994. .filter(!this.active || this.first() ? ":last" : ":first"));
  995. }
  996. },
  997. hasScroll: function() {
  998. return this.element.height() < this.element.attr("scrollHeight");
  999. },
  1000. select: function( event ) {
  1001. this._trigger("selected", event, { item: this.active });
  1002. }
  1003. });
  1004. }(jQuery));