grid.custom.js 66 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779
  1. /**
  2. * jqGrid extension for custom methods
  3. * Tony Tomov tony@trirand.com, http://trirand.com/blog/
  4. *
  5. * Wildraid wildraid@mail.ru
  6. * Oleg Kiriljuk oleg.kiriljuk@ok-soft-gmbh.com
  7. * Dual licensed under the MIT and GPL licenses:
  8. * http://www.opensource.org/licenses/mit-license.php
  9. * http://www.gnu.org/licenses/gpl-2.0.html
  10. **/
  11. /*jshint eqeqeq:false */
  12. /*jslint browser: true, devel: true, eqeq: true, nomen: true, plusplus: true, vars: true, unparam: true, white: true, todo: true */
  13. /*global jQuery, define, exports, module, require */
  14. (function (global, factory) {
  15. "use strict";
  16. if (typeof define === "function" && define.amd) {
  17. // AMD. Register as an anonymous module.
  18. //console.log("grid.custom AMD");
  19. define([
  20. "jquery",
  21. "./grid.base",
  22. "./jquery.fmatter",
  23. "./grid.common"
  24. ], function ($) {
  25. //console.log("grid.custom AMD: define callback");
  26. return factory($, global.document);
  27. });
  28. } else if (typeof module === "object" && module.exports) {
  29. // Node/CommonJS
  30. //console.log("grid.custom CommonJS, typeof define=" + typeof define + ", define=" + define);
  31. module.exports = function (root, $) {
  32. //console.log("grid.custom CommonJS: in module.exports");
  33. if (!root) {
  34. root = window;
  35. }
  36. //console.log("grid.custom CommonJS: before require('jquery')");
  37. if ($ === undefined) {
  38. // require("jquery") returns a factory that requires window to
  39. // build a jQuery instance, we normalize how we use modules
  40. // that require this pattern but the window provided is a noop
  41. // if it's defined (how jquery works)
  42. $ = typeof window !== "undefined" ?
  43. require("jquery") :
  44. require("jquery")(root);
  45. }
  46. //console.log("grid.custom CommonJS: before require('./grid.base')");
  47. require("./grid.base");
  48. //console.log("grid.custom CommonJS: before require('./jquery.fmatter')");
  49. require("./jquery.fmatter");
  50. //console.log("grid.custom CommonJS: before require('./grid.common')");
  51. require("./grid.common");
  52. factory($, root.document);
  53. return $;
  54. };
  55. } else {
  56. // Browser globals
  57. //console.log("grid.custom Browser: before factory");
  58. factory(jQuery, global.document);
  59. }
  60. }(typeof window !== "undefined" ? window : this, function ($, document) {
  61. "use strict";
  62. var jgrid = $.jgrid, jqID = jgrid.jqID, base = $.fn.jqGrid,
  63. getGuiStyles = base.getGuiStyles, getGridRes = base.getGridRes;
  64. // begin module grid.custom
  65. jgrid.extend({
  66. getColProp: function (colname) {
  67. var ret = {}, t = this[0], iCol;
  68. if (t != null && t.grid) {
  69. iCol = t.p.iColByName[colname];
  70. if (iCol !== undefined) {
  71. return t.p.colModel[iCol];
  72. }
  73. }
  74. return ret;
  75. },
  76. setColProp: function (colname, obj) {
  77. //do not set width will not work
  78. return this.each(function () {
  79. var self = this, p = self.p, iCol;
  80. if (self.grid && p != null && obj) {
  81. iCol = p.iColByName[colname];
  82. if (iCol !== undefined) {
  83. $.extend(true, p.colModel[iCol], obj);
  84. }
  85. }
  86. });
  87. },
  88. sortGrid: function (colname, reload, sor) {
  89. return this.each(function () {
  90. var self = this, grid = self.grid, p = self.p, colModel = p.colModel, l = colModel.length, cm, i, sobj = false, sort;
  91. if (!grid) { return; }
  92. if (!colname) { colname = p.sortname; }
  93. if (typeof reload !== "boolean") { reload = false; }
  94. for (i = 0; i < l; i++) {
  95. cm = colModel[i];
  96. if (cm.index === colname || cm.name === colname) {
  97. if (p.frozenColumns === true && cm.frozen === true) {
  98. sobj = grid.fhDiv.find("#" + jqID(p.id + "_" + colname));
  99. }
  100. if (!sobj || sobj.length === 0) {
  101. sobj = grid.headers[i].el;
  102. }
  103. sort = cm.sortable;
  104. if (typeof sort !== "boolean" || sort) {
  105. self.sortData(cm.index || cm.name, i, reload, sor, sobj);
  106. }
  107. break;
  108. }
  109. }
  110. });
  111. },
  112. clearBeforeUnload: function () {
  113. return this.each(function () {
  114. var self = this, p = self.p, grid = self.grid, propOrMethod, clearArray = jgrid.clearArray,
  115. hasOwnProperty = Object.prototype.hasOwnProperty;
  116. if ($.isFunction(grid.emptyRows)) {
  117. grid.emptyRows.call(self, true, true); // this work quick enough and reduce the size of memory leaks if we have someone
  118. }
  119. $(document).off("mousemove.jqGrid mouseup.jqGrid" + p.id);
  120. $(self).off();
  121. /*grid.dragEnd = null;
  122. grid.dragMove = null;
  123. grid.dragStart = null;
  124. grid.emptyRows = null;
  125. grid.populate = null;
  126. grid.populateVisible = null;
  127. grid.scrollGrid = null;
  128. grid.selectionPreserver = null;
  129. grid.bDiv = null;
  130. grid.fbRows = null;
  131. grid.cDiv = null;
  132. grid.hDiv = null;
  133. grid.cols = null;*/
  134. var i, l = grid.headers.length;
  135. for (i = 0; i < l; i++) {
  136. grid.headers[i].el = null;
  137. }
  138. for (propOrMethod in grid) {
  139. if (grid.hasOwnProperty(propOrMethod)) {
  140. grid.propOrMethod = null;
  141. }
  142. }
  143. /*self.formatCol = null;
  144. self.sortData = null;
  145. self.updatepager = null;
  146. self.refreshIndex = null;
  147. self.setHeadCheckBox = null;
  148. self.constructTr = null;
  149. self.formatter = null;
  150. self.addXmlData = null;
  151. self.addJSONData = null;
  152. self.grid = null;*/
  153. var propOrMethods = ["formatCol", "sortData", "updatepager", "refreshIndex", "setHeadCheckBox", "constructTr", "clearToolbar", "fixScrollOffsetAndhBoxPadding", "rebuildRowIndexes", "modalAlert", "toggleToolbar", "triggerToolbar", "formatter", "addXmlData", "addJSONData", "ftoolbar", "_inlinenav", "nav", "grid", "p"];
  154. l = propOrMethods.length;
  155. for (i = 0; i < l; i++) {
  156. if (hasOwnProperty.call(self, propOrMethods[i])) {
  157. self[propOrMethods[i]] = null;
  158. }
  159. }
  160. self._index = {};
  161. clearArray(p.data);
  162. clearArray(p.lastSelectedData);
  163. clearArray(p.selarrrow);
  164. clearArray(p.savedRow);
  165. p.editingInfo = {};
  166. });
  167. },
  168. GridDestroy: function () {
  169. return this.each(function () {
  170. var self = this, p = self.p;
  171. if (self.grid && p != null) {
  172. if (p.pager) { // if not part of grid
  173. $(p.pager).remove();
  174. }
  175. try {
  176. $("#alertmod_" + jqID(p.id)).remove();
  177. $(self).jqGrid("clearBeforeUnload");
  178. $(p.gBox).remove();
  179. } catch (ignore) { }
  180. }
  181. });
  182. },
  183. GridUnload: function () {
  184. return this.each(function () {
  185. var self = this, $self = $(self), p = self.p, $j = $.fn.jqGrid;
  186. if (!self.grid) { return; }
  187. $self.removeClass($j.getGuiStyles.call($self, "grid", "ui-jqgrid-btable"));
  188. // The multiple removeAttr can be replace to one after dropping of support of old jQuery
  189. if (p.pager) {
  190. if (p.pager.substr(1, $.jgrid.uidPref.length) === $.jgrid.uidPref) {
  191. $(p.pager).remove();
  192. } else {
  193. $(p.pager).empty()
  194. .removeClass($j.getGuiStyles.call($self, "pager.pager", "ui-jqgrid-pager " +
  195. $j.getGuiStyles.call($self, "bottom")))
  196. .removeAttr("style")
  197. .removeAttr("dir");
  198. }
  199. }
  200. $self.jqGrid("clearBeforeUnload");
  201. $self.removeAttr("style")
  202. .removeAttr("tabindex")
  203. .removeAttr("role")
  204. .removeAttr("aria-labelledby")
  205. .removeAttr("style");
  206. $self.empty(); // remove the first line
  207. $self.insertBefore(p.gBox).show();
  208. $(p.pager).insertBefore(p.gBox).show();
  209. $(p.gBox).remove();
  210. });
  211. },
  212. setGridState: function (state) {
  213. return this.each(function () {
  214. var $t = this, p = $t.p, grid = $t.grid, cDiv = grid.cDiv, $uDiv = $(grid.uDiv), $ubDiv = $(grid.ubDiv);
  215. if (!grid || p == null) { return; }
  216. var getMinimizeIcon = function (path) {
  217. return base.getIconRes.call($t, "gridMinimize." + path);
  218. },
  219. visibleGridIcon = getMinimizeIcon("visible"),
  220. hiddenGridIcon = getMinimizeIcon("hidden");
  221. if (state === "hidden") {
  222. $(".ui-jqgrid-bdiv, .ui-jqgrid-hdiv", p.gView).slideUp("fast");
  223. if (p.pager) { $(p.pager).slideUp("fast"); }
  224. if (p.toppager) { $(p.toppager).slideUp("fast"); }
  225. if (p.toolbar[0] === true) {
  226. if (p.toolbar[1] === "both") {
  227. $ubDiv.slideUp("fast");
  228. }
  229. $uDiv.slideUp("fast");
  230. }
  231. if (p.footerrow) { $(".ui-jqgrid-sdiv", p.gBox).slideUp("fast"); }
  232. $(".ui-jqgrid-titlebar-close span", cDiv).removeClass(visibleGridIcon).addClass(hiddenGridIcon);
  233. p.gridstate = "hidden";
  234. } else if (state === "visible") {
  235. $(".ui-jqgrid-hdiv, .ui-jqgrid-bdiv", p.gView).slideDown("fast");
  236. if (p.pager) { $(p.pager).slideDown("fast"); }
  237. if (p.toppager) { $(p.toppager).slideDown("fast"); }
  238. if (p.toolbar[0] === true) {
  239. if (p.toolbar[1] === "both") {
  240. $ubDiv.slideDown("fast");
  241. }
  242. $uDiv.slideDown("fast");
  243. }
  244. if (p.footerrow) { $(".ui-jqgrid-sdiv", p.gBox).slideDown("fast"); }
  245. $(".ui-jqgrid-titlebar-close span", cDiv).removeClass(hiddenGridIcon).addClass(visibleGridIcon);
  246. p.gridstate = "visible";
  247. }
  248. });
  249. },
  250. filterToolbar: function (oMuligrid) {
  251. // if one uses jQuery wrapper with multiple grids, then oMultiple specify the object with common options
  252. return this.each(function () {
  253. var $t = this, grid = $t.grid, $self = $($t), p = $t.p, infoDialog = jgrid.info_dialog, htmlEncode = jgrid.htmlEncode;
  254. if (this.ftoolbar) { return; }
  255. // make new copy of the options and use it for ONE specific grid.
  256. // p.searching can contains grid specific options
  257. // we will don't modify the input options oMuligrid
  258. var o = $.extend(true, {
  259. autosearch: true,
  260. autosearchDelay: 500,
  261. searchOnEnter: true,
  262. beforeSearch: null,
  263. afterSearch: null,
  264. beforeClear: null,
  265. afterClear: null,
  266. searchurl: "",
  267. sField: "searchField",
  268. sValue: "searchString",
  269. sOper: "searchOper",
  270. sFilter: p.prmNames.filters,
  271. stringResult: false,
  272. groupOp: "AND",
  273. defaultSearch: "bw",
  274. idMode: "new", // support "old", "compatibility", "new"
  275. searchOperators: false,
  276. resetIcon: "&times;",
  277. applyLabelClasses: true,
  278. loadFilterDefaults: true, // this options activates loading of default filters from grid's postData for Multipe Search only.
  279. operands: { "eq": "==", "ne": "!", "lt": "<", "le": "<=", "gt": ">", "ge": ">=", "bw": "^", "bn": "!^", "in": "=", "ni": "!=", "ew": "|", "en": "!@", "cn": "~", "nc": "!~", "nu": "#", "nn": "!#" }
  280. }, jgrid.search, p.searching || {}, oMuligrid || {}),
  281. colModel = p.colModel,
  282. getRes = function (path) {
  283. return getGridRes.call($self, path);
  284. },
  285. errcap = getRes("errors.errcap"),
  286. bClose = getRes("edit.bClose"),
  287. editMsg = getRes("edit.msg"),
  288. hoverClasses = getGuiStyles.call($t, "states.hover"),
  289. highlightClass = getGuiStyles.call($t, "states.select"),
  290. dataFieldClass = getGuiStyles.call($t, "filterToolbar.dataField"),
  291. currentFilters = {},
  292. getId = function (cmName) {
  293. var prefix = "gs_";
  294. switch (o.idMode) {
  295. case "compatibility":
  296. prefix += p.idPrefix;
  297. break;
  298. case "new":
  299. prefix += p.id + "_";
  300. break;
  301. default: // "old"
  302. break;
  303. }
  304. return prefix + cmName;
  305. },
  306. getIdSel = function (cmName) {
  307. return "#" + jqID(getId(cmName));
  308. },
  309. getOnOffValue = function (soptions) {
  310. var checkboxValue = (soptions.value || "").split(":");
  311. return {
  312. on: checkboxValue[0] || "on",
  313. off: checkboxValue[1] || "off"
  314. };
  315. },
  316. parseFilter = function (fillAll) {
  317. var i, j, filters = p.postData[o.sFilter], filter = {}, rules, rule,
  318. iColByName = p.iColByName, cm, soptions;
  319. if (fillAll) {
  320. for (j = 0; j < colModel.length; j++) {
  321. cm = colModel[j];
  322. if (cm.search !== false) {
  323. soptions = cm.searchoptions || {};
  324. filter[cm.name] = {
  325. op: soptions.sopt ?
  326. soptions.sopt[0] :
  327. (cm.stype === "select" || cm.stype === "checkbox") ? "eq" : o.defaultSearch,
  328. data: soptions.defaultValue !== undefined ? soptions.defaultValue : ""
  329. };
  330. }
  331. }
  332. }
  333. // TODO: test for !o.stringResult && !o.searchOperators && p.datatype !== "local"
  334. // and use p.postData[cm.index || cm.name] instead of filter[cm.name]
  335. if (!filters || !p.search) { return filter; }
  336. if (typeof filters === "string") {
  337. try {
  338. filters = $.parseJSON(filters);
  339. } catch (ignore) {
  340. filters = {};
  341. }
  342. } else {
  343. filters = filters || {};
  344. }
  345. rules = filters.rules || {};
  346. if (filters == null ||
  347. (filters.groupOp != null && o.groupOp != null && filters.groupOp.toUpperCase() !== o.groupOp.toUpperCase()) ||
  348. rules == null || rules.length === 0 ||
  349. (filters.groups != null && filters.groups.length > 0)) {
  350. return filter;
  351. }
  352. for (j = 0; j < rules.length; j++) {
  353. rule = rules[j];
  354. // find all columns in colModel, where
  355. // colModel[i].index || colModel[i].name === rule.field
  356. cm = colModel[iColByName[rule.field]];
  357. for (i = 0; i < colModel.length; i++) {
  358. cm = colModel[i];
  359. if ((cm.index || cm.name) !== rule.field || cm.search === false) {
  360. continue;
  361. }
  362. soptions = cm.searchoptions || {};
  363. if (soptions.sopt) {
  364. if ($.inArray(rule.op, soptions.sopt) < 0) {
  365. continue;
  366. }
  367. } else if (cm.stype === "select" || cm.stype === "checkbox") {
  368. if (rule.op !== "eq") {
  369. continue;
  370. }
  371. } else if (rule.op !== o.defaultSearch) {
  372. continue;
  373. }
  374. filter[cm.name] = { op: rule.op, data: rule.data };
  375. }
  376. }
  377. return filter;
  378. },
  379. setThreeStateCheckbox = function ($checkbox, state) {
  380. switch (state) {
  381. case 1: // make checked
  382. $checkbox.data("state", 1)
  383. .prop({
  384. checked: true,
  385. indeterminate: false
  386. });
  387. break;
  388. case 0: // make unchecked
  389. $checkbox.data("state", 0)
  390. .prop({
  391. checked: false,
  392. indeterminate: false
  393. });
  394. break;
  395. default: // make indeterminate
  396. $checkbox.data("state", -1)
  397. .prop({
  398. checked: false,
  399. indeterminate: true
  400. });
  401. break;
  402. }
  403. },
  404. triggerToolbar = function () {
  405. var sdata = {}, j = 0, sopt = {};
  406. $.each(colModel, function () {
  407. var cm = this, nm = cm.index || cm.name, v, so, searchoptions = cm.searchoptions || {},
  408. $elem = $(getIdSel(cm.name), (cm.frozen === true && p.frozenColumns === true) ? grid.fhDiv : grid.hDiv),
  409. getFormaterOption = function (optionName, formatter) {
  410. var formatoptions = cm.formatoptions || {};
  411. return formatoptions[optionName] !== undefined ?
  412. formatoptions[optionName] :
  413. getRes("formatter." + (formatter || cm.formatter) + "." + optionName);
  414. },
  415. cutThousandsSeparator = function (val) {
  416. var separator = getFormaterOption("thousandsSeparator")
  417. .replace(/([\.\*\_\'\(\)\{\}\+\?\\])/g, "\\$1");
  418. return val.replace(new RegExp(separator, "g"), "");
  419. };
  420. if (o.searchOperators) {
  421. so = $elem.parent().prev().children("a").data("soper") || o.defaultSearch;
  422. } else {
  423. so = searchoptions.sopt ? searchoptions.sopt[0] : (cm.stype === "select" || cm.stype === "checkbox") ? "eq" : o.defaultSearch;
  424. }
  425. /* the format of element of the searching toolbar if ANOTHER
  426. * as the format of cells in the grid. So one can't use
  427. * value = $.unformat.call($t, $elem, { colModel: cm }, iCol)
  428. * to get the value. Even the access to the value should be
  429. * $elem.val() instead of $elem.text() used in the common case of
  430. * formatter. So we have to make manual conversion of searching filed
  431. * used for integer/number/currency. The code will be duplicate */
  432. if (cm.stype === "custom" && $.isFunction(searchoptions.custom_value) && $elem.length > 0 && $elem[0].nodeName.toUpperCase() === "SPAN") {
  433. v = searchoptions.custom_value.call($t, $elem.children(".customelement").first(), "get");
  434. } else if (cm.stype === "select") {
  435. if ($elem.prop("multiple")) {
  436. v = $elem.val();
  437. if (v == null || v.length === 0) {
  438. v = "";
  439. } else {
  440. v = v.join(p.inFilterSeparator || ",");
  441. }
  442. } else {
  443. v = $elem.val();
  444. }
  445. } else if (cm.stype === "checkbox") {
  446. var onOffValue = getOnOffValue(searchoptions);
  447. switch ($elem.data("state")) {
  448. case -1: // has indeterminate state
  449. v = "";
  450. break;
  451. case 0: // is unchecked
  452. // make indeterminate
  453. v = onOffValue.off;
  454. break;
  455. default: // is checked
  456. // make unchecked
  457. v = onOffValue.on;
  458. break;
  459. }
  460. } else {
  461. v = $.trim($elem.val());
  462. switch (cm.formatter) {
  463. case "integer":
  464. v = cutThousandsSeparator(v)
  465. .replace(getFormaterOption("decimalSeparator", "number"), ".");
  466. if (v !== "") {
  467. // normalize the strings like "010.01" to "10"
  468. v = String(parseInt(v, 10));
  469. }
  470. break;
  471. case "number":
  472. v = cutThousandsSeparator(v)
  473. .replace(getFormaterOption("decimalSeparator"), ".");
  474. if (v !== "" && String(v).charAt(0) === "0") {
  475. // normalize the strings like "010.00" to "10"
  476. // and "010.12" to "10.12"
  477. v = String(parseFloat(v));
  478. }
  479. break;
  480. case "currency":
  481. var prefix = getFormaterOption("prefix"),
  482. suffix = getFormaterOption("suffix");
  483. if (prefix && prefix.length && v.substr(0, prefix.length) === prefix) {
  484. v = v.substr(prefix.length);
  485. }
  486. if (suffix && suffix.length && v.length > suffix.length && v.substr(v.length - suffix.length, suffix.length) === suffix) {
  487. v = v.substr(0, v.length - suffix.length);
  488. }
  489. v = cutThousandsSeparator(v)
  490. .replace(getFormaterOption("decimalSeparator"), ".");
  491. if (v !== "") {
  492. // normalize the strings like "010.00" to "10"
  493. // and "010.12" to "10.12"
  494. v = String(parseFloat(v));
  495. }
  496. break;
  497. default:
  498. // TODO: call unformatter if it's defined.
  499. break;
  500. }
  501. }
  502. if (v || so === "nu" || so === "nn" || $.inArray(so, p.customUnaryOperations) >= 0) {
  503. sdata[nm] = v;
  504. sopt[nm] = so;
  505. j++;
  506. } else {
  507. if (sdata.hasOwnProperty(nm)) {
  508. delete sdata[nm];
  509. }
  510. if (!(o.stringResult || o.searchOperators || p.datatype === "local")) {
  511. try {
  512. if (p.postData != null && p.postData.hasOwnProperty(nm)) {
  513. delete p.postData[nm];
  514. }
  515. } catch (ignore) { }
  516. }
  517. }
  518. });
  519. var sd = j > 0 ? true : false;
  520. if (o.stringResult || o.searchOperators || p.datatype === "local") {
  521. var ruleGroup = '{"groupOp":"' + o.groupOp + '","rules":[';
  522. var gi = 0;
  523. $.each(sdata, function (cmName, n) {
  524. //var iCol = p.iColByName[cmName], cm = p.colModel[iCol],
  525. // value = $.unformat.call($t, $("<span></span>").text(n), { colModel: cm }, iCol);
  526. if (gi > 0) { ruleGroup += ","; }
  527. ruleGroup += '{"field":"' + cmName + '",';
  528. ruleGroup += '"op":"' + sopt[cmName] + '",';
  529. n += "";
  530. ruleGroup += '"data":"' + n.replace(/\\/g, "\\\\").replace(/\"/g, '\\"') + '"}';
  531. gi++;
  532. });
  533. ruleGroup += "]}";
  534. p.postData[o.sFilter] = ruleGroup;
  535. $.each([o.sField, o.sValue, o.sOper], function (i, n) {
  536. if (p.postData.hasOwnProperty(n)) { delete p.postData[n]; }
  537. });
  538. } else {
  539. $.extend(p.postData, sdata);
  540. }
  541. var saveurl;
  542. if (p.searchurl) {
  543. saveurl = p.url;
  544. $self.jqGrid("setGridParam", { url: p.searchurl });
  545. }
  546. var bsr = $self.triggerHandler("jqGridToolbarBeforeSearch") === "stop" ? true : false;
  547. if (!bsr && $.isFunction(o.beforeSearch)) { bsr = o.beforeSearch.call($t); }
  548. if (!bsr) {
  549. $self.jqGrid("setGridParam", { search: sd })
  550. .trigger("reloadGrid", [$.extend({ page: 1 }, o.reloadGridSearchOptions || {})]);
  551. }
  552. if (saveurl) { $self.jqGrid("setGridParam", { url: saveurl }); }
  553. $self.triggerHandler("jqGridToolbarAfterSearch");
  554. if ($.isFunction(o.afterSearch)) { o.afterSearch.call($t); }
  555. },
  556. clearToolbar = function (trigger) {
  557. var sdata = {}, j = 0, nm;
  558. trigger = (typeof trigger !== "boolean") ? true : trigger;
  559. $.each(colModel, function () {
  560. var v, cm = this, $elem = $(getIdSel(cm.name), (cm.frozen === true && p.frozenColumns === true) ? grid.fhDiv : grid.hDiv),
  561. isSindleSelect, searchoptions = cm.searchoptions || {};
  562. if (searchoptions.defaultValue !== undefined) { v = searchoptions.defaultValue; }
  563. nm = cm.index || cm.name;
  564. switch (cm.stype) {
  565. case "checkbox":
  566. // set indeterminate state
  567. setThreeStateCheckbox($elem, -1);
  568. break;
  569. case "select":
  570. isSindleSelect = $elem.length > 0 ? !$elem[0].multiple : true;
  571. $elem.find("option").each(function (i) {
  572. this.selected = i === 0 && isSindleSelect;
  573. if ($(this).val() === v) {
  574. this.selected = true;
  575. return false;
  576. }
  577. });
  578. if (v !== undefined) {
  579. // post the key and not the text
  580. sdata[nm] = v;
  581. j++;
  582. } else {
  583. try {
  584. delete p.postData[nm];
  585. } catch (ignore) { }
  586. }
  587. break;
  588. case "text":
  589. $elem.val(v || "");
  590. if (v !== undefined) {
  591. sdata[nm] = v;
  592. j++;
  593. } else {
  594. try {
  595. delete p.postData[nm];
  596. } catch (ignore) { }
  597. }
  598. break;
  599. case "custom":
  600. if ($.isFunction(searchoptions.custom_value) && $elem.length > 0 && $elem[0].nodeName.toUpperCase() === "SPAN") {
  601. if (v === undefined) {
  602. v = "";
  603. }
  604. searchoptions.custom_value.call($t, $elem.children(".customelement").first(), "set", v);
  605. }
  606. break;
  607. }
  608. });
  609. var sd = j > 0 ? true : false;
  610. p.resetsearch = true;
  611. if (o.stringResult || o.searchOperators || p.datatype === "local") {
  612. var ruleGroup = '{"groupOp":"' + o.groupOp + '","rules":[';
  613. var gi = 0;
  614. $.each(sdata, function (i, n) {
  615. if (gi > 0) { ruleGroup += ","; }
  616. ruleGroup += '{"field":"' + i + '",';
  617. ruleGroup += '"op":"' + "eq" + '",';
  618. n += "";
  619. ruleGroup += '"data":"' + n.replace(/\\/g, "\\\\").replace(/\"/g, '\\"') + '"}';
  620. gi++;
  621. });
  622. ruleGroup += "]}";
  623. p.postData[o.sFilter] = ruleGroup;
  624. $.each([o.sField, o.sValue, o.sOper], function (i, n) {
  625. if (p.postData.hasOwnProperty(n)) { delete p.postData[n]; }
  626. });
  627. } else {
  628. $.extend(p.postData, sdata);
  629. }
  630. var saveurl;
  631. if (p.searchurl) {
  632. saveurl = p.url;
  633. $self.jqGrid("setGridParam", { url: p.searchurl });
  634. }
  635. var bcv = $self.triggerHandler("jqGridToolbarBeforeClear") === "stop" ? true : false;
  636. if (!bcv && $.isFunction(o.beforeClear)) { bcv = o.beforeClear.call($t); }
  637. if (!bcv) {
  638. if (trigger) {
  639. $self.jqGrid("setGridParam", { search: sd })
  640. .trigger("reloadGrid", [$.extend({ page: 1 }, o.reloadGridResetOptions || {})]);
  641. }
  642. }
  643. if (saveurl) { $self.jqGrid("setGridParam", { url: saveurl }); }
  644. $self.triggerHandler("jqGridToolbarAfterClear");
  645. if ($.isFunction(o.afterClear)) { o.afterClear.call($t); }
  646. },
  647. toggleToolbar = function () {
  648. var trow = $("tr.ui-search-toolbar", grid.hDiv),
  649. trow2 = p.frozenColumns === true ? $("tr.ui-search-toolbar", grid.fhDiv) : false;
  650. if (trow.css("display") === "none") {
  651. trow.show();
  652. if (trow2) {
  653. trow2.show();
  654. }
  655. } else {
  656. trow.hide();
  657. if (trow2) {
  658. trow2.hide();
  659. }
  660. }
  661. if (p.frozenColumns === true) {
  662. $self.jqGrid("destroyFrozenColumns");
  663. $self.jqGrid("setFrozenColumns");
  664. }
  665. },
  666. odata = getRes("search.odata") || [],
  667. customSortOperations = p.customSortOperations,
  668. buildRuleMenu = function (elem, left, top) {
  669. $("#sopt_menu").remove();
  670. left = parseInt(left, 10);
  671. top = parseInt(top, 10) + 18;
  672. var selclass, ina, i = 0, aoprs = [], selected = $(elem).data("soper"), nm = $(elem).data("colname"),
  673. fs = $(".ui-jqgrid-view").css("font-size") || "11px",
  674. str = "<ul id='sopt_menu' class='" +
  675. getGuiStyles.call($t, "searchToolbar.menu", "ui-search-menu") +
  676. "' role='menu' tabindex='0' style='z-index:9999;display:block;font-size:" + fs + ";left:" + left + "px;top:" + top + "px;'>";
  677. i = p.iColByName[nm];
  678. if (i === undefined) { return; }
  679. var cm = colModel[i], options = $.extend({}, cm.searchoptions), odataItem, item, itemOper, itemOperand, itemText;
  680. if (!options.sopt) {
  681. options.sopt = [];
  682. options.sopt[0] = (cm.stype === "select" || cm.stype === "checkbox") ? "eq" : o.defaultSearch;
  683. }
  684. $.each(odata, function () { aoprs.push(this.oper); });
  685. // append aoprs array with custom operations defined in customSortOperations parameter jqGrid
  686. if (customSortOperations != null) {
  687. $.each(customSortOperations, function (propertyName) { aoprs.push(propertyName); });
  688. }
  689. for (i = 0; i < options.sopt.length; i++) {
  690. itemOper = options.sopt[i];
  691. ina = $.inArray(itemOper, aoprs);
  692. if (ina !== -1) {
  693. odataItem = odata[ina];
  694. if (odataItem !== undefined) {
  695. // standard operation
  696. itemOperand = o.operands[itemOper];
  697. itemText = odataItem.text;
  698. } else if (customSortOperations != null) {
  699. // custom operation
  700. item = customSortOperations[itemOper];
  701. itemOperand = item.operand;
  702. itemText = item.text;
  703. }
  704. selclass = selected === itemOper ? highlightClass : "";
  705. str += '<li class="' +
  706. getGuiStyles.call($t, "searchToolbar.menuItem", "ui-jqgrid-menu-item " + selclass) +
  707. '" role="presentation"><a class="' +
  708. getGuiStyles.call($t, "searchToolbar.menuItemButton", "g-menu-item") +
  709. '" tabindex="0" role="menuitem" value="' + htmlEncode(itemOper) + '" data-oper="' + htmlEncode(itemOperand) + '"><table><tr><td style="width:25px">' + htmlEncode(itemOperand) + '</td><td>' + htmlEncode(itemText) + '</td></tr></table></a></li>';
  710. }
  711. }
  712. str += "</ul>";
  713. $("body").append(str);
  714. $("#sopt_menu > li > a").hover(
  715. function () { $(this).addClass(hoverClasses); },
  716. function () { $(this).removeClass(hoverClasses); }
  717. ).click(function () {
  718. var v = $(this).attr("value"),
  719. oper = $(this).data("oper");
  720. $self.triggerHandler("jqGridToolbarSelectOper", [v, oper, elem]);
  721. $("#sopt_menu").hide();
  722. $(elem).data("soper", v).text(oper);
  723. if (o.autosearch === true) {
  724. var inpelm = $(elem).parent().next().children()[0];
  725. if ($(inpelm).val() || v === "nu" || v === "nn" || $.inArray(v, p.customUnaryOperations) >= 0) {
  726. triggerToolbar();
  727. }
  728. }
  729. });
  730. },
  731. timeoutHnd,
  732. bindings = [],
  733. $tr = $("<tr></tr>", { "class": "ui-search-toolbar", role: "row form" });
  734. if (o.loadFilterDefaults) {
  735. currentFilters = parseFilter() || {};
  736. }
  737. // create the row
  738. $.each(colModel, function (ci) {
  739. var cm = this, soptions, mode = "filter", surl, sot, so, i, searchoptions = cm.searchoptions || {}, editoptions = cm.editoptions || {},
  740. $th = $("<th></th>", {
  741. "class": getGuiStyles.call($t, "colHeaders", "ui-th-column ui-th-" + p.direction + " " + (o.applyLabelClasses ? cm.labelClasses || "" : "")),
  742. role: "gridcell",
  743. "aria-describedby": p.id + "_" + cm.name
  744. }),
  745. $thd = $("<div></div>"), elem, $elem,
  746. $stable = $("<table class='ui-search-table'><tbody><tr><td class='ui-search-oper'></td><td class='ui-search-input'></td><td class='ui-search-clear' style='width:1px'></td></tr></tbody></table>"),
  747. $tds = $stable.children("tbody").children("tr").children("td"),
  748. $tdOper = $tds.eq(0),
  749. $tdInput = $tds.eq(1),
  750. $tdClear = $tds.eq(2);
  751. if (this.hidden === true) { $th.css("display", "none"); }
  752. this.search = this.search === false ? false : true;
  753. if (this.stype === undefined) { this.stype = "text"; }
  754. soptions = $.extend({ mode: mode, name: cm.name, cm: cm, iCol: ci, id: getId(cm.name) }, searchoptions);
  755. if (this.search) {
  756. if (o.searchOperators) {
  757. if (p.search && currentFilters[this.name] != null) {
  758. so = currentFilters[this.name].op;
  759. } else {
  760. so = (soptions.sopt) ? soptions.sopt[0] : (cm.stype === "select" || cm.stype === "checkbox") ? "eq" : o.defaultSearch;
  761. }
  762. for (i = 0; i < odata.length; i++) {
  763. if (odata[i].oper === so) {
  764. sot = o.operands[so] || "";
  765. break;
  766. }
  767. }
  768. if (sot === undefined && customSortOperations != null) {
  769. var customOp;
  770. for (customOp in customSortOperations) {
  771. if (customSortOperations.hasOwnProperty(customOp) && customOp === so) {
  772. sot = customSortOperations[customOp].operand;
  773. break;
  774. //soptions.searchtitle = customSortOperations[customOp].title;
  775. }
  776. }
  777. }
  778. if (sot === undefined) { sot = "="; }
  779. $tdOper.append("<a title='" +
  780. (soptions.searchtitle != null ? soptions.searchtitle : getRes("search.operandTitle")) +
  781. "' data-soper='" + so + "' class='" +
  782. getGuiStyles.call($t, "searchToolbar.operButton", "soptclass") +
  783. "' data-colname='" + this.name + "'>" + htmlEncode(sot) + "</a>");
  784. }
  785. $tdOper.data("colindex", ci);
  786. if (soptions.sopt == null || soptions.sopt.length === 1) {
  787. $tdOper.hide();
  788. }
  789. if (p.search && currentFilters[this.name] != null) {
  790. soptions.defaultValue = currentFilters[this.name].data;
  791. }
  792. if (soptions.clearSearch === undefined) {
  793. soptions.clearSearch = this.stype === "text" ? true : false;
  794. }
  795. if (soptions.clearSearch) {
  796. var csv = $.isFunction(o.resetTitle) ?
  797. o.resetTitle.call($t, {
  798. options: o,
  799. cm: cm,
  800. cmName: cm.name,
  801. iCol: ci
  802. }) :
  803. (getRes("search.resetTitle") || "Clear Search Value") + " " + jgrid.stripHtml(p.colNames[ci]);
  804. $tdClear.append("<a title='" + csv + "' aria-label='" + csv + "' class='" +
  805. getGuiStyles.call($t, "searchToolbar.clearButton", "clearsearchclass") +
  806. "'><span>" + o.resetIcon + "</span></a>");
  807. } else {
  808. $tdClear.hide();
  809. }
  810. $thd.append($stable);
  811. switch (this.stype) {
  812. case "checkbox":
  813. var state = soptions.defaultValue !== undefined ? soptions.defaultValue : "-1";
  814. $elem = $("<input role='search' type='checkbox' class='" + dataFieldClass +
  815. "' name='" + (cm.index || cm.name) +
  816. "' id='" + getId(cm.name) +
  817. "' aria-labelledby='" + "jqgh_" + p.id + "_" + cm.name +
  818. "' data-state='" + state + "'/>");
  819. if (state === "-1") {
  820. $elem.prop("indeterminate", true);
  821. } else if (state === "1") {
  822. $elem.prop("checked", true);
  823. }
  824. $elem.click(function () {
  825. var $checkbox = $(this);
  826. switch ($checkbox.data("state")) {
  827. case -1: // has indeterminate state
  828. // make checked
  829. setThreeStateCheckbox($checkbox, 1);
  830. break;
  831. case 0: // is unchecked
  832. // set indeterminate state
  833. setThreeStateCheckbox($checkbox, -1);
  834. break;
  835. default: // is checked
  836. // make unchecked
  837. setThreeStateCheckbox($checkbox, 0);
  838. break;
  839. }
  840. if (o.autosearch === true) {
  841. triggerToolbar();
  842. }
  843. });
  844. $tdInput.append($elem);
  845. if (soptions.attr) { $elem.attr(soptions.attr); }
  846. bindings.push({ elem: $elem[0], options: soptions }); break;
  847. case "select":
  848. surl = this.surl || soptions.dataUrl;
  849. if (surl) {
  850. // data returned should have already constructed html select
  851. // primitive jQuery load
  852. $.ajax($.extend({
  853. url: surl,
  854. context: { $tdInput: $tdInput, options: soptions, cm: cm, iCol: ci },
  855. dataType: "html",
  856. success: function (data, textStatus, jqXHR) {
  857. var cm1 = this.cm, iCol1 = this.iCol, soptions1 = this.options, d, ov1,
  858. $td = this.$tdInput, $select;
  859. if (soptions1.buildSelect !== undefined) {
  860. d = soptions1.buildSelect.call($t, data, jqXHR, cm1, iCol1);
  861. if (d) {
  862. $td.append(d);
  863. }
  864. } else {
  865. $td.append(data);
  866. }
  867. $select = $td.children("select");
  868. $select.attr({ name: cm1.index || cm1.name, id: getId(cm1.name) });
  869. if (soptions1.attr) { $select.attr(soptions1.attr); }
  870. $select.addClass(dataFieldClass);
  871. $select.css({ width: "100%" });
  872. if ($select.find("option[value='']").length === 0 && typeof soptions.noFilterText === "string") {
  873. ov1 = document.createElement("option");
  874. ov1.value = "";
  875. ov1.innerHTML = soptions.noFilterText;
  876. $select.prepend(ov1);
  877. if ($($select[0].options[$select[0].selectedIndex]).attr("selected") == null && !$select[0].multiple) {
  878. $select[0].selectedIndex = 0;
  879. }
  880. }
  881. if ($select[0].multiple && $select.find("option[selected]").length === 0 && $select[0].selectedIndex !== -1) {
  882. // It can be that multiselect (select with multiple attribute) will be returned from surl
  883. // or build with respect of buildSelect WITHOUT having multiple attribute
  884. // (just as <select>...</select> instead of <select multiple="multiple">...</select>)
  885. // and the multiple attribute will be assigned via soptions.attr (attr: { multiple: "multiple" }).
  886. // One will have non-multiple select initially, where the first element will be automatically selected.
  887. // After assigning the attribute multiple="multiple" the select will be able to have no selected elements,
  888. // but it will be too late. To fix the case we will unselect the first element in the special case.
  889. $select[0].options[$select[0].selectedIndex].selected = false;
  890. }
  891. if (soptions1.defaultValue !== undefined) { $select.val(soptions1.defaultValue); }
  892. // preserve autoserch
  893. jgrid.bindEv.call($t, $select[0], soptions1);
  894. jgrid.fullBoolFeedback.call($t, soptions1.selectFilled, "jqGridSelectFilled", {
  895. elem: $select[0],
  896. options: soptions1,
  897. cm: cm1,
  898. cmName: cm1.name,
  899. iCol: iCol1,
  900. mode: mode
  901. });
  902. if (o.autosearch === true) {
  903. $select.change(function () {
  904. triggerToolbar();
  905. return false;
  906. });
  907. }
  908. }
  909. }, jgrid.ajaxOptions, p.ajaxSelectOptions || {}));
  910. } else {
  911. var oSv, sep, delim;
  912. if (cm.searchoptions) {
  913. oSv = searchoptions.value === undefined ? editoptions.value || "" : searchoptions.value;
  914. sep = searchoptions.separator === undefined ? editoptions.separator || ":" : searchoptions.separator;
  915. delim = searchoptions.delimiter === undefined ? editoptions.delimiter || ";" : searchoptions.delimiter;
  916. } else if (cm.editoptions) {
  917. oSv = editoptions.value === undefined ? "" : editoptions.value;
  918. sep = editoptions.separator === undefined ? ":" : editoptions.separator;
  919. delim = editoptions.delimiter === undefined ? ";" : editoptions.delimiter;
  920. }
  921. if (searchoptions.generateValue && p.indexByColumnData[cm.name] != null) {
  922. oSv = $t.generateValueFromColumnIndex(cm.name, sep, delim);
  923. }
  924. if (oSv) {
  925. elem = document.createElement("select");
  926. elem.style.width = "100%";
  927. $elem = $(elem).attr({
  928. name: cm.index || cm.name,
  929. role: "search",
  930. id: getId(cm.name),
  931. "aria-describedby": p.id + "_" + cm.name
  932. });
  933. if (soptions.attr) { $elem.attr(soptions.attr); }
  934. var isNoFilterValueExist = jgrid.fillSelectOptions(
  935. elem,
  936. oSv,
  937. sep,
  938. delim,
  939. soptions.attr != null && soptions.attr.multiple
  940. );
  941. if (!isNoFilterValueExist && typeof soptions.noFilterText === "string") {
  942. var ov = document.createElement("option");
  943. ov.value = "";
  944. ov.innerHTML = soptions.noFilterText;
  945. ov.selected = true;
  946. $elem.prepend(ov);
  947. }
  948. if (soptions.defaultValue !== undefined) { $elem.val(soptions.defaultValue); }
  949. $elem.addClass(dataFieldClass);
  950. //bindEv.call($t, elem, soptions);
  951. bindings.push({ elem: elem, options: soptions });
  952. $tdInput.append(elem);
  953. jgrid.fullBoolFeedback.call($t, soptions.selectFilled, "jqGridSelectFilled", {
  954. elem: elem,
  955. options: cm.searchoptions || editoptions,
  956. cm: cm,
  957. cmName: cm.name,
  958. iCol: ci,
  959. mode: mode
  960. });
  961. if (o.autosearch === true) {
  962. $elem.change(function () {
  963. triggerToolbar();
  964. return false;
  965. });
  966. }
  967. }
  968. }
  969. break;
  970. case "text":
  971. $elem = $("<input role='search' type='text' class='" + dataFieldClass +
  972. "' name='" + (cm.index || cm.name) +
  973. "' id='" + getId(cm.name) +
  974. "' aria-labelledby='" + "jqgh_" + p.id + "_" + cm.name +
  975. "' value='" + (soptions.defaultValue !== undefined ? soptions.defaultValue : "") + "'/>");
  976. $tdInput.append($elem);
  977. if (cm.createColumnIndex && soptions.generateDatalist) {
  978. var dataListId = "dl_" + getId(cm.name),
  979. $datalist = $self.jqGrid("generateDatalistFromColumnIndex", cm.name);
  980. if ($datalist != null && $datalist.length > 0) {
  981. $elem.attr("list", dataListId);
  982. $tdInput.append($datalist.attr("id", dataListId));
  983. }
  984. }
  985. if (soptions.attr) { $elem.attr(soptions.attr); }
  986. bindings.push({ elem: $elem[0], options: soptions });
  987. if (o.autosearch === true) {
  988. if (o.searchOnEnter) {
  989. $elem.keypress(function (e) {
  990. var key1 = e.charCode || e.keyCode || 0;
  991. if (key1 === 13) {
  992. triggerToolbar();
  993. return false;
  994. }
  995. return this;
  996. });
  997. } else {
  998. $elem.keydown(function (e) {
  999. var key1 = e.which;
  1000. switch (key1) {
  1001. case 13:
  1002. return false;
  1003. case 9:
  1004. case 16:
  1005. case 37:
  1006. case 38:
  1007. case 39:
  1008. case 40:
  1009. case 27:
  1010. break;
  1011. default:
  1012. if (timeoutHnd) { clearTimeout(timeoutHnd); }
  1013. timeoutHnd = setTimeout(function () { triggerToolbar(); }, o.autosearchDelay);
  1014. }
  1015. });
  1016. }
  1017. }
  1018. break;
  1019. case "custom":
  1020. $tdInput.append("<span style='width:100%;padding:0;box-sizing:border-box;' name='" + (cm.index || cm.name) + "' id='" + getId(cm.name) + "'/>");
  1021. try {
  1022. if ($.isFunction(soptions.custom_element)) {
  1023. var celm = soptions.custom_element.call($t, soptions.defaultValue !== undefined ? soptions.defaultValue : "", soptions);
  1024. if (celm) {
  1025. celm = $(celm).addClass("customelement");
  1026. $thd.find("span[name='" + (cm.index || cm.name) + "']").append(celm);
  1027. } else {
  1028. throw "e2";
  1029. }
  1030. } else {
  1031. throw "e1";
  1032. }
  1033. } catch (ex) {
  1034. if (ex === "e1") {
  1035. infoDialog.call($t, errcap, "function 'custom_element' " + editMsg.nodefined, bClose);
  1036. }
  1037. if (ex === "e2") {
  1038. infoDialog.call($t, errcap, "function 'custom_element' " + editMsg.novalue, bClose);
  1039. } else {
  1040. infoDialog.call($t, errcap, typeof ex === "string" ? ex : ex.message, bClose);
  1041. }
  1042. }
  1043. break;
  1044. }
  1045. }
  1046. $th.append($thd);
  1047. $th.find(".ui-search-oper .soptclass,.ui-search-clear .clearsearchclass")
  1048. .hover(function () {
  1049. $(this).addClass(hoverClasses);
  1050. }, function () {
  1051. $(this).removeClass(hoverClasses);
  1052. });
  1053. $tr.append($th);
  1054. if (!o.searchOperators) {
  1055. $tdOper.hide();
  1056. }
  1057. });
  1058. $(grid.hDiv).find(">div>.ui-jqgrid-htable>thead").append($tr);
  1059. $.each(bindings, function () {
  1060. jgrid.bindEv.call($t, this.elem, this.options);
  1061. });
  1062. if (o.searchOperators) {
  1063. $(".soptclass", $tr).click(function (e) {
  1064. var offset = $(this).offset(),
  1065. left = (offset.left),
  1066. top = (offset.top);
  1067. buildRuleMenu(this, left, top);
  1068. e.stopPropagation();
  1069. });
  1070. $("body").on("click", function (e) {
  1071. if (e.target.className !== "soptclass") {
  1072. $("#sopt_menu").hide();
  1073. }
  1074. });
  1075. }
  1076. $(".clearsearchclass", $tr).click(function () {
  1077. var $tdOper = $(this).closest(".ui-search-clear"),
  1078. $tdSearchOper = $tdOper.siblings(".ui-search-oper"),
  1079. $oper = $tdSearchOper.children("a"),
  1080. soper = $oper.data("soper"), v, operText,
  1081. coli = parseInt($tdSearchOper.data("colindex"), 10),
  1082. $tdInput = $tdOper.siblings(".ui-search-input"),
  1083. cm = colModel[coli],
  1084. sval = $.extend({}, cm.searchoptions || {}),
  1085. dval = sval.defaultValue || "";
  1086. switch (cm.stype) {
  1087. case "select":
  1088. if (dval) {
  1089. $tdInput.find("select").val(dval);
  1090. } else {
  1091. $tdInput.find("select")[0].selectedIndex = 0;
  1092. }
  1093. break;
  1094. case "checkbox":
  1095. // set indeterminate state
  1096. setThreeStateCheckbox($tdInput.find("input[type=checkbox]"), -1);
  1097. break;
  1098. default:
  1099. $tdInput.find("input").val(dval);
  1100. break;
  1101. }
  1102. if (soper === "nu" || soper === "nn" || $.inArray(soper, p.customUnaryOperations) >= 0) {
  1103. // one need reset an unary operation to default search operation
  1104. v = sval.sopt ?
  1105. sval.sopt[0] :
  1106. (cm.stype === "select" || cm.stype === "checkbox") ?
  1107. "eq" :
  1108. o.defaultSearch;
  1109. operText = customSortOperations != null && customSortOperations[v] != null ?
  1110. customSortOperations[v].operand :
  1111. o.operands[v] || "";
  1112. $oper.data("soper", v).text(operText);
  1113. }
  1114. // ToDo custom search type
  1115. if (o.autosearch === true) {
  1116. triggerToolbar();
  1117. }
  1118. });
  1119. $t.ftoolbar = true;
  1120. $t.triggerToolbar = triggerToolbar;
  1121. $t.clearToolbar = clearToolbar;
  1122. $t.toggleToolbar = toggleToolbar;
  1123. if (p.frozenColumns === true) {
  1124. $self.jqGrid("destroyFrozenColumns");
  1125. $self.jqGrid("setFrozenColumns");
  1126. }
  1127. $self.on(
  1128. "jqGridRefreshFilterValues.filterToolbar" + (o.loadFilterDefaults ? " jqGridAfterLoadComplete.filterToolbar" : ""),
  1129. function () {
  1130. var cmName, filter, newFilters = parseFilter(true) || {}, $input, $searchOper, i, $th, searchoptions;
  1131. if (!o.stringResult && !o.searchOperators && p.datatype !== "local" && p.search) {
  1132. return; // do nothing on legacy searching
  1133. }
  1134. for (cmName in newFilters) {
  1135. if (newFilters.hasOwnProperty(cmName)) {
  1136. filter = newFilters[cmName];
  1137. $input = $(getIdSel(cmName));
  1138. $th = $input.closest("th.ui-th-column");
  1139. if ($input.length > 0 && $th.length > 0) {
  1140. searchoptions = (p.colModel[$th[0].cellIndex] || {}).searchoptions || {};
  1141. if ($input[0].tagName.toUpperCase() === "SELECT" && $input[0].multiple) {
  1142. $input.val(filter.data.split(p.inFilterSeparator || ","));
  1143. } else if ($input.is("input[type=checkbox]")) {
  1144. var onOffValue = getOnOffValue(searchoptions);
  1145. setThreeStateCheckbox(
  1146. $input,
  1147. filter.data === onOffValue.on ?
  1148. 1 :
  1149. (filter.data === onOffValue.off ? 0 : -1)
  1150. );
  1151. } else if ($input.find(".customelement").length > 0 && $.isFunction(searchoptions.custom_value)) {
  1152. var oldValue = searchoptions.custom_value.call($t, $input.find(".customelement").first(), "get");
  1153. if (filter.data === "" && searchoptions.defaultValue !== undefined) {
  1154. filter.data = searchoptions.defaultValue;
  1155. }
  1156. if (oldValue === undefined) {
  1157. oldValue = "";
  1158. }
  1159. if (filter.data !== oldValue && String(filter.data) !== String(oldValue)) {
  1160. searchoptions.custom_value.call($t, $input.find(".customelement").first(), "set", filter.data);
  1161. }
  1162. } else {
  1163. if (filter.data === "" && searchoptions.defaultValue !== undefined) {
  1164. filter.data = searchoptions.defaultValue;
  1165. }
  1166. if ($.trim($input.val()) !== String(filter.data)) {
  1167. $input.val(filter.data);
  1168. }
  1169. }
  1170. $searchOper = $input.closest(".ui-search-input")
  1171. .siblings(".ui-search-oper")
  1172. .children(".soptclass");
  1173. $searchOper.data("soper", filter.op);
  1174. $searchOper.text(o.operands[filter.op] || (p.customSortOperations[filter.op] || {}).operand);
  1175. }
  1176. }
  1177. }
  1178. for (i = 0; i < p.colModel.length; i++) {
  1179. cmName = p.colModel[i].name;
  1180. if (!newFilters.hasOwnProperty(cmName)) {
  1181. $(getIdSel(cmName)).val("");
  1182. }
  1183. }
  1184. }
  1185. );
  1186. });
  1187. },
  1188. destroyFilterToolbar: function () {
  1189. return this.each(function () {
  1190. var self = this;
  1191. if (!self.ftoolbar) {
  1192. return;
  1193. }
  1194. self.triggerToolbar = null;
  1195. self.clearToolbar = null;
  1196. self.toggleToolbar = null;
  1197. self.ftoolbar = false;
  1198. $(self.grid.hDiv).find("table thead tr.ui-search-toolbar").remove();
  1199. if (self.p.frozenColumns === true) {
  1200. $(self).jqGrid("destroyFrozenColumns")
  1201. .jqGrid("setFrozenColumns");
  1202. }
  1203. });
  1204. },
  1205. destroyGroupHeader: function (nullHeader) {
  1206. if (nullHeader === undefined) {
  1207. nullHeader = true;
  1208. }
  1209. return this.each(function () {
  1210. var $t = this, i, l, $th, $resizing, grid = $t.grid, cm = $t.p.colModel, hc,
  1211. thead = $("table.ui-jqgrid-htable thead", grid.hDiv);
  1212. if (!grid) { return; }
  1213. $($t).off(".setGroupHeaders");
  1214. var $tr = $("<tr>", { role: "row" }).addClass("ui-jqgrid-labels");
  1215. var headers = grid.headers;
  1216. for (i = 0, l = headers.length; i < l; i++) {
  1217. hc = cm[i].hidden ? "none" : "";
  1218. $th = $(headers[i].el)
  1219. .width(headers[i].width)
  1220. .css("display", hc);
  1221. try {
  1222. $th.removeAttr("rowSpan");
  1223. } catch (rs) {
  1224. //IE 6/7
  1225. $th.attr("rowSpan", 1);
  1226. }
  1227. $tr.append($th);
  1228. $resizing = $th.children("span.ui-jqgrid-resize");
  1229. if ($resizing.length > 0) {// resizable column
  1230. $resizing[0].style.height = "";
  1231. }
  1232. $th.children("div")[0].style.top = "";
  1233. }
  1234. $(thead).children("tr.ui-jqgrid-labels").remove();
  1235. $(thead).prepend($tr);
  1236. if (nullHeader === true) {
  1237. $($t).jqGrid("setGridParam", { "groupHeader": null });
  1238. }
  1239. });
  1240. },
  1241. setGroupHeaders: function (o) {
  1242. o = $.extend({
  1243. useColSpanStyle: false,
  1244. applyLabelClasses: true,
  1245. groupHeaders: []
  1246. }, o || {});
  1247. return this.each(function () {
  1248. this.p.groupHeader = o;
  1249. var ts = this, i, cmi, skip = 0, $tr, $colHeader, th, $th, thStyle, iCol, cghi, numberOfColumns, titleText, cVisibleColumns,
  1250. p = ts.p, colModel = p.colModel, cml = colModel.length, ths = ts.grid.headers, $theadInTable, thClasses,
  1251. $htable = $("table.ui-jqgrid-htable", ts.grid.hDiv), isCellClassHidden = jgrid.isCellClassHidden,
  1252. $trLabels = $htable.children("thead").children("tr.ui-jqgrid-labels"),
  1253. $trLastWithLabels = $trLabels.last().addClass("jqg-second-row-header"),
  1254. $thead = $htable.children("thead"),
  1255. $firstHeaderRow = $htable.find(".jqg-first-row-header");
  1256. if ($firstHeaderRow[0] === undefined) {
  1257. $firstHeaderRow = $("<tr>", { role: "row", "aria-hidden": "true" }).addClass("jqg-first-row-header").css("height", "auto");
  1258. } else {
  1259. $firstHeaderRow.empty();
  1260. }
  1261. var inColumnHeader = function (cmName, columnHeaders) {
  1262. var j;
  1263. for (j = 0; j < columnHeaders.length; j++) {
  1264. if (columnHeaders[j].startColumnName === cmName) {
  1265. return columnHeaders[j];
  1266. }
  1267. }
  1268. return 0; // falsy value
  1269. };
  1270. $(ts).prepend($thead);
  1271. $tr = $("<tr>", { role: "row" }).addClass("ui-jqgrid-labels jqg-third-row-header");
  1272. for (i = 0; i < cml; i++) {
  1273. th = ths[i].el;
  1274. $th = $(th);
  1275. cmi = colModel[i];
  1276. // build the next cell for the first header row
  1277. // ??? cmi.hidden || isCellClassHidden(cmi.classes) || $th.is(":hidden")
  1278. thStyle = { height: "0", width: ths[i].width + "px", display: (cmi.hidden ? "none" : "") };
  1279. $("<th>", { role: "gridcell" }).css(thStyle).addClass("ui-first-th-" + p.direction + (o.applyLabelClasses ? " " + (cmi.labelClasses || "") : "")).appendTo($firstHeaderRow);
  1280. th.style.width = ""; // remove unneeded style
  1281. thClasses = getGuiStyles.call(ts, "colHeaders", "ui-th-column-header ui-th-" + p.direction + " " + (o.applyLabelClasses ? cmi.labelClasses || "" : ""));
  1282. cghi = inColumnHeader(cmi.name, o.groupHeaders);
  1283. if (cghi) {
  1284. numberOfColumns = cghi.numberOfColumns;
  1285. titleText = cghi.titleText;
  1286. // caclulate the number of visible columns from the next numberOfColumns columns
  1287. for (cVisibleColumns = 0, iCol = 0; iCol < numberOfColumns && (i + iCol < cml); iCol++) {
  1288. if (!colModel[i + iCol].hidden && !isCellClassHidden(colModel[i + iCol].classes)) {
  1289. cVisibleColumns++;
  1290. }
  1291. }
  1292. // The next numberOfColumns headers will be moved in the next row
  1293. // in the current row will be placed the new column header with the titleText.
  1294. // The text will be over the cVisibleColumns columns
  1295. $colHeader = $("<th>")
  1296. .addClass(thClasses)
  1297. .html(titleText || "&nbsp;");
  1298. if (cVisibleColumns > 0) {
  1299. $colHeader.attr("colspan", String(cVisibleColumns));
  1300. }
  1301. if (p.headertitles) {
  1302. $colHeader.attr("title", $colHeader.text());
  1303. }
  1304. // hide if not a visible cols
  1305. if (cVisibleColumns === 0) {
  1306. $colHeader.hide();
  1307. }
  1308. $th.before($colHeader); // insert new column header before the current
  1309. $tr.append(th); // move the current header in the next row
  1310. // set the counter of headers which will be moved in the next row
  1311. skip = numberOfColumns - 1;
  1312. } else {
  1313. if (skip === 0) {
  1314. if (o.useColSpanStyle) {
  1315. // expand the header height to two rows
  1316. $th.attr("rowspan", $trLabels.length + 1); // consider to use ($th.attr("rowspan") || 1) instead of $trLabels.length
  1317. } else {
  1318. $("<th>")
  1319. .addClass(thClasses)
  1320. .css({ "display": cmi.hidden ? "none" : "", "border-top": "0 none" })
  1321. .insertBefore($th);
  1322. $tr.append(th);
  1323. }
  1324. } else {
  1325. // move the header to the next row
  1326. $tr.append(th);
  1327. skip--;
  1328. }
  1329. }
  1330. }
  1331. $theadInTable = $(ts).children("thead");
  1332. $theadInTable.prepend($firstHeaderRow);
  1333. $tr.insertAfter($trLastWithLabels);
  1334. $htable.prepend($theadInTable);
  1335. $(ts).triggerHandler("jqGridAfterSetGroupHeaders");
  1336. });
  1337. },
  1338. getNumberOfFrozenColumns: function () {
  1339. var $t = this;
  1340. if ($t.length === 0) {
  1341. return 0;
  1342. }
  1343. $t = $t[0];
  1344. var colModel = $t.p.colModel, len = colModel.length, maxfrozen = -1, i;
  1345. // get the max index of frozen col
  1346. for (i = 0; i < len; i++) {
  1347. // from left, no breaking frozen
  1348. if (colModel[i].frozen !== true) {
  1349. break;
  1350. }
  1351. maxfrozen = i;
  1352. }
  1353. return maxfrozen + 1;
  1354. },
  1355. setFrozenColumns: function (o) {
  1356. o = o || {};
  1357. return this.each(function () {
  1358. var $t = this, $self = $($t), p = $t.p, grid = $t.grid;
  1359. if (!grid || p == null || p.frozenColumns === true) { return; }
  1360. var cm = p.colModel, i, len = cm.length, maxfrozen = -1, frozen = false, frozenIds = [], $colHeaderRow,// nonFrozenIds = [],
  1361. tid = jqID(p.id), // one can use p.idSel and remove "#"
  1362. hoverClasses = getGuiStyles.call($t, "states.hover");
  1363. // TODO treeGrid and grouping Support
  1364. // TODO: allow to edit columns AFTER frozen columns
  1365. if (p.subGrid === true || p.treeGrid === true || p.scroll) {
  1366. return;
  1367. }
  1368. // get the max index of frozen col
  1369. for (i = 0; i < len; i++) {
  1370. // from left, no breaking frozen
  1371. if (cm[i].frozen !== true) {
  1372. break;
  1373. //nonFrozenIds.push("#jqgh_" + tid + "_" + jqID(cm[i].name));
  1374. }
  1375. frozen = true;
  1376. maxfrozen = i;
  1377. frozenIds.push("#jqgh_" + tid + "_" + jqID(cm[i].name));
  1378. }
  1379. if (p.sortable) {
  1380. $colHeaderRow = $(grid.hDiv).find(".ui-jqgrid-htable .ui-jqgrid-labels");
  1381. try {
  1382. $colHeaderRow.sortable("destroy");
  1383. } catch (ignore) { }
  1384. $self.jqGrid("setGridParam", {
  1385. sortable: {
  1386. options: {
  1387. items: frozenIds.length > 0 ?
  1388. ">th:not(:has(" + frozenIds.join(",") + "),:hidden)" :
  1389. ">th:not(:hidden)"
  1390. }
  1391. }
  1392. });
  1393. $self.jqGrid("sortableColumns", $colHeaderRow);
  1394. }
  1395. if (maxfrozen >= 0 && frozen) {
  1396. var top = p.caption ? $(grid.cDiv).outerHeight() : 0,
  1397. hth = $(".ui-jqgrid-htable", p.gView).height();
  1398. //headers
  1399. if (p.toppager) {
  1400. top = top + $(grid.topDiv).outerHeight();
  1401. }
  1402. if (p.toolbar[0] === true) {
  1403. if (p.toolbar[1] !== "bottom") {
  1404. top = top + $(grid.uDiv).outerHeight();
  1405. }
  1406. }
  1407. grid.fhDiv = $("<div style='position:absolute;overflow:hidden;" +
  1408. (p.direction === "rtl" ? "right:0;border-top-left-radius:0;" : "left:0;border-top-right-radius:0;") +
  1409. "top:" + top + "px;height:" + hth +
  1410. "px;' class='" + getGuiStyles.call($t, "hDiv", "frozen-div ui-jqgrid-hdiv") + "'></div>");
  1411. grid.fbDiv = $("<div style='position:absolute;overflow:hidden;" +
  1412. (p.direction === "rtl" ? "right:0;" : "left:0;") +
  1413. "top:" + (parseInt(top, 10) + parseInt(hth, 10) + 1) +
  1414. "px;overflow:hidden;' class='frozen-bdiv ui-jqgrid-bdiv'></div>");
  1415. $(p.gView).append(grid.fhDiv);
  1416. var htbl = $(".ui-jqgrid-htable", p.gView).clone(true),
  1417. tHeadRows = htbl[0].tHead.rows;
  1418. // groupheader support - only if useColSpanstyle is false
  1419. if (p.groupHeader) {
  1420. // TODO: remove all th which corresponds non-frozen columns. One can identify there by id
  1421. // for example. Consider to use name attribute of th on column headers. It simplifies
  1422. // identifying of the columns.
  1423. $(tHeadRows[0].cells).filter(":gt(" + maxfrozen + ")").remove();
  1424. $(tHeadRows).filter(".jqg-third-row-header").each(function () {
  1425. $(this).children("th[id]")
  1426. .each(function () {
  1427. var id = $(this).attr("id"), colName;
  1428. if (id && id.substr(0, $t.id.length + 1) === $t.id + "_") {
  1429. colName = id.substr($t.id.length + 1);
  1430. if (p.iColByName[colName] > maxfrozen) {
  1431. $(this).remove();
  1432. }
  1433. }
  1434. });
  1435. });
  1436. var swapfroz = -1, fdel = -1, cs, rs;
  1437. // TODO: test carefully processing of hidden columns
  1438. $(tHeadRows).filter(".jqg-second-row-header").children("th").each(function () {
  1439. cs = parseInt($(this).attr("colspan") || 1, 10);
  1440. rs = parseInt($(this).attr("rowspan") || 1, 10);
  1441. if (rs > 1) {
  1442. swapfroz++;
  1443. fdel++;
  1444. } else if (cs) {
  1445. swapfroz = swapfroz + cs;
  1446. fdel++;
  1447. }
  1448. if (swapfroz === maxfrozen) {
  1449. return false;
  1450. }
  1451. });
  1452. if (swapfroz !== maxfrozen) {
  1453. fdel = maxfrozen;
  1454. }
  1455. $(tHeadRows).filter(".jqg-second-row-header,.ui-search-toolbar").each(function () {
  1456. $(this).children(":gt(" + fdel + ")").remove();
  1457. });
  1458. } else {
  1459. $(tHeadRows).each(function () {
  1460. $(this).children(":gt(" + maxfrozen + ")").remove();
  1461. });
  1462. }
  1463. // htable, bdiv and ftable uses table-layout:fixed; style
  1464. // to make it working one have to set ANY width value on table.
  1465. // The value of the width will be ignored, the sum of widths
  1466. // of the first column will be used as the width of tables
  1467. // and all columns will have the same width like the first row.
  1468. // We set below just width=1 of the tables.
  1469. $(htbl).width(1);
  1470. // resizing stuff
  1471. $(grid.fhDiv).append(htbl)
  1472. .scroll(function () {
  1473. // the fhDiv can be scrolled because of tab keyboard navigation
  1474. // we prevent horizontal scrolling of fhDiv
  1475. this.scrollLeft = 0;
  1476. });
  1477. if (p.footerrow) {
  1478. var hbd = $(".ui-jqgrid-bdiv", p.gView).height();
  1479. grid.fsDiv = $("<div style='position:absolute;" + (p.direction === "rtl" ? "right:0;" : "left:0;") + "top:" + (parseInt(top, 10) + parseInt(hth, 10) + parseInt(hbd, 10) + 1) + "px;' class='frozen-sdiv ui-jqgrid-sdiv'></div>");
  1480. $(p.gView).append(grid.fsDiv);
  1481. var ftbl = $(".ui-jqgrid-ftable", p.gView).clone(true);
  1482. $("tr", ftbl).each(function () {
  1483. $("td:gt(" + maxfrozen + ")", this).remove();
  1484. });
  1485. $(ftbl).width(1);
  1486. $(grid.fsDiv).append(ftbl);
  1487. }
  1488. // data stuff
  1489. //TODO support for setRowData
  1490. $(p.gView).append(grid.fbDiv);
  1491. $(grid.bDiv).scroll(function () {
  1492. $(grid.fbDiv).scrollTop($(this).scrollTop());
  1493. });
  1494. $(grid.fbDiv).on("mousewheel.setFrozenColumns DOMMouseScroll.setFrozenColumns", function (e) {
  1495. grid.bDiv.scrollTop += $.isFunction(o.mouseWheel) ?
  1496. o.mouseWheel.call($t, e) :
  1497. e.type === "mousewheel" ?
  1498. -e.originalEvent.wheelDelta / 10 :
  1499. e.originalEvent.detail * 6;
  1500. });
  1501. if (p.hoverrows === true) {
  1502. $(p.idSel).off("mouseover.jqGrid mouseout.jqGrid");
  1503. }
  1504. var safeHeightSet = function ($elem, newHeight) {
  1505. var height = $elem.height();
  1506. if (Math.abs(height - newHeight) >= 1 && newHeight > 0) {
  1507. $elem.height(newHeight);
  1508. height = $elem.height();
  1509. if (Math.abs(newHeight - height) >= 1) {
  1510. $elem.height(newHeight + Math.round((newHeight - height)));
  1511. }
  1512. }
  1513. },
  1514. safeWidthSet = function ($elem, newWidth) {
  1515. var width = $elem.width();
  1516. if (Math.abs(width - newWidth) >= 1) {
  1517. $elem.width(newWidth);
  1518. width = $elem.width();
  1519. if (Math.abs(newWidth - width) >= 1) {
  1520. $elem.width(newWidth + Math.round((newWidth - width)));
  1521. }
  1522. }
  1523. },
  1524. fixDiv = function ($hDiv, hDivBase, iRowStart, iRowEnd) {
  1525. var iRow, n, $frozenRows, $rows, $row, $frozenRow, posFrozenTop, height, newHeightFrozen, td,
  1526. posTop = $(hDivBase).position().top, frozenTableTop, tableTop, cells;
  1527. if ($hDiv != null && $hDiv.length > 0) {
  1528. $hDiv[0].scrollTop = hDivBase.scrollTop;
  1529. $hDiv.css(p.direction === "rtl" ?
  1530. { top: posTop, right: 0 } :
  1531. { top: posTop, left: 0 }
  1532. );
  1533. // first try with thead for the hdiv
  1534. $frozenRows = $hDiv.children("table").children("thead").children("tr");
  1535. $rows = $(hDivBase).children("div").children("table").children("thead").children("tr");
  1536. if ($rows.length === 0 && $hDiv.children("table").length > 0) {
  1537. // then use tbody for bdiv
  1538. $frozenRows = $($hDiv.children("table")[0].rows);
  1539. $rows = $($(hDivBase).children("div").children("table")[0].rows);
  1540. }
  1541. n = Math.min($frozenRows.length, $rows.length);
  1542. frozenTableTop = n > 0 ? $($frozenRows[0]).position().top : 0;
  1543. tableTop = n > 0 ? $($rows[0]).position().top : 0; // typically 0
  1544. if (iRowStart >= 0) { // negative iRowStart means no changing of the height of individual rows
  1545. if (iRowEnd >= 0) { // negative iRowEnd means all rows
  1546. n = Math.min(iRowEnd + 1, n);
  1547. }
  1548. for (iRow = iRowStart; iRow < n; iRow++) {
  1549. // but after that one have to verify all scenarios
  1550. $row = $($rows[iRow]);
  1551. if ($row.css("display") !== "none" && $row.is(":visible")) {
  1552. posTop = $row.position().top;
  1553. $frozenRow = $($frozenRows[iRow]);
  1554. posFrozenTop = $frozenRow.position().top;
  1555. height = $row.height();
  1556. if (p.groupHeader != null && p.groupHeader.useColSpanStyle) {
  1557. cells = $row[0].cells;
  1558. for (i = 0; i < cells.length; i++) { // maxfrozen
  1559. td = cells[i];
  1560. if (td != null && td.nodeName.toUpperCase() === "TH") {
  1561. height = Math.max(height, $(td).height());
  1562. }
  1563. }
  1564. }
  1565. newHeightFrozen = height + (posTop - tableTop) + (frozenTableTop - posFrozenTop);
  1566. safeHeightSet($frozenRow, newHeightFrozen);
  1567. }
  1568. }
  1569. }
  1570. safeHeightSet($hDiv, hDivBase.clientHeight);
  1571. }
  1572. },
  1573. /** @const */
  1574. resizeAll = {
  1575. resizeDiv: true,
  1576. resizedRows: {
  1577. iRowStart: 0,
  1578. iRowEnd: -1 // -1 means "till the end"
  1579. }
  1580. },
  1581. /** @const */
  1582. fullResize = {
  1583. header: resizeAll,
  1584. resizeFooter: true,
  1585. body: resizeAll
  1586. };
  1587. $self.on("jqGridAfterGridComplete.setFrozenColumns", function () {
  1588. $(p.idSel + "_frozen").remove();
  1589. $(grid.fbDiv).height(grid.hDiv.clientHeight);
  1590. // clone with data and events !!!
  1591. var $frozenBTable = $(this).clone(true),
  1592. frozenRows = $frozenBTable[0].rows,
  1593. rows = $self[0].rows;
  1594. $(frozenRows).filter("tr[role=row]").each(function () {
  1595. $(this.cells).filter("td[role=gridcell]:gt(" + maxfrozen + ")").remove();
  1596. /*if (this.id) {
  1597. $(this).attr("id", this.id + "_frozen");
  1598. }*/
  1599. });
  1600. grid.fbRows = frozenRows;
  1601. $frozenBTable.width(1).attr("id", p.id + "_frozen");
  1602. $frozenBTable.appendTo(grid.fbDiv);
  1603. if (p.hoverrows === true) {
  1604. var hoverRows = function (tr, method, additionalRows) {
  1605. $(tr)[method](hoverClasses);
  1606. $(additionalRows[tr.rowIndex])[method](hoverClasses);
  1607. };
  1608. $(frozenRows).filter(".jqgrow").hover(
  1609. function () {
  1610. hoverRows(this, "addClass", rows);
  1611. },
  1612. function () {
  1613. hoverRows(this, "removeClass", rows);
  1614. }
  1615. );
  1616. $(rows).filter(".jqgrow").hover(
  1617. function () {
  1618. hoverRows(this, "addClass", frozenRows);
  1619. },
  1620. function () {
  1621. hoverRows(this, "removeClass", frozenRows);
  1622. }
  1623. );
  1624. }
  1625. fixDiv(grid.fhDiv, grid.hDiv, 0, -1);
  1626. fixDiv(grid.fbDiv, grid.bDiv, 0, -1);
  1627. if (grid.sDiv) { fixDiv(grid.fsDiv, grid.sDiv, 0, -1); }
  1628. });
  1629. var myResize = function (resizeOptions) {
  1630. $(grid.fbDiv).scrollTop($(grid.bDiv).scrollTop());
  1631. // TODO: the width of all column headers can be changed
  1632. // so one should recalculate frozenWidth in other way.
  1633. if (resizeOptions.header.resizeDiv) {
  1634. fixDiv(grid.fhDiv, grid.hDiv, resizeOptions.header.resizedRows.iRowStart, resizeOptions.header.resizedRows.iRowEnd);
  1635. }
  1636. if (resizeOptions.body.resizeDiv) {
  1637. fixDiv(grid.fbDiv, grid.bDiv, resizeOptions.body.resizedRows.iRowStart, resizeOptions.body.resizedRows.iRowEnd);
  1638. }
  1639. if (resizeOptions.resizeFooter && grid.sDiv && resizeOptions.resizeFooter) {
  1640. fixDiv(grid.fsDiv, grid.sDiv, 0, -1);
  1641. }
  1642. var frozenWidth = grid.fhDiv[0].clientWidth;
  1643. if (resizeOptions.header.resizeDiv && grid.fhDiv != null && grid.fhDiv.length >= 1) {
  1644. safeHeightSet($(grid.fhDiv), grid.hDiv.clientHeight);
  1645. }
  1646. if (resizeOptions.body.resizeDiv && grid.fbDiv != null && grid.fbDiv.length > 0) {
  1647. safeWidthSet($(grid.fbDiv), frozenWidth);
  1648. }
  1649. if (resizeOptions.resizeFooter && grid.fsDiv != null && grid.fsDiv.length >= 0) {
  1650. safeWidthSet($(grid.fsDiv), frozenWidth);
  1651. }
  1652. };
  1653. $(p.gBox).on("resizestop.setFrozenColumns", function () {
  1654. setTimeout(function () {
  1655. myResize(fullResize);
  1656. }, 50);
  1657. });
  1658. $self.on("jqGridInlineEditRow.setFrozenColumns jqGridInlineAfterRestoreRow.setFrozenColumns jqGridInlineAfterSaveRow.setFrozenColumns jqGridAfterEditCell.setFrozenColumns jqGridAfterRestoreCell.setFrozenColumns jqGridAfterSaveCell.setFrozenColumns jqGridResizeStop.setFrozenColumns", function (e, rowid) {
  1659. // TODO: probably one should handle additional events like afterSetRow
  1660. // and remove jqGridInlineAfterSaveRow and jqGridInlineAfterRestoreRow
  1661. var iRow = $self.jqGrid("getInd", rowid);
  1662. myResize({
  1663. header: {
  1664. resizeDiv: false, // don't recalculate the position and the height of hDiv
  1665. resizedRows: {
  1666. iRowStart: -1, // -1 means don't recalculate heights or rows
  1667. iRowEnd: -1
  1668. }
  1669. },
  1670. resizeFooter: true, // recalculate the position and the height of sDiv
  1671. body: {
  1672. resizeDiv: true, // recalculate the position and the height of bDiv
  1673. resizedRows: {
  1674. // recalculate the height of only one row inside of bDiv
  1675. iRowStart: iRow,
  1676. iRowEnd: iRow
  1677. }
  1678. }
  1679. });
  1680. });
  1681. $self.on("jqGridResizeStop.setFrozenColumns", function () {
  1682. myResize(fullResize);
  1683. });
  1684. $self.on("jqGridResetFrozenHeights.setFrozenColumns", function (e, o) {
  1685. myResize(o || fullResize);
  1686. });
  1687. if (!grid.hDiv.loading) {
  1688. setTimeout(function () {
  1689. $self.triggerHandler("jqGridAfterGridComplete");
  1690. }, 0);
  1691. }
  1692. p.frozenColumns = true;
  1693. }
  1694. });
  1695. },
  1696. destroyFrozenColumns: function () {
  1697. return this.each(function () {
  1698. var $t = this, $self = $($t), grid = $t.grid, p = $t.p, tid = jqID(p.id);
  1699. if (!grid) { return; }
  1700. if (p.frozenColumns === true) {
  1701. $(grid.fhDiv).remove();
  1702. $(grid.fbDiv).off(".setFrozenColumns");
  1703. $(grid.fbDiv).remove();
  1704. grid.fhDiv = null;
  1705. grid.fbDiv = null;
  1706. grid.fbRows = null;
  1707. if (p.footerrow) {
  1708. $(grid.fsDiv).remove();
  1709. grid.fsDiv = null;
  1710. }
  1711. $self.off(".setFrozenColumns");
  1712. if (p.hoverrows === true) {
  1713. var ptr, hoverClasses = getGuiStyles.call($t, "states.hover");
  1714. $self.on("mouseover.jqGrid", function (e) {
  1715. ptr = $(e.target).closest("tr.jqgrow");
  1716. if ($(ptr).attr("class") !== "ui-subgrid") {
  1717. $(ptr).addClass(hoverClasses);
  1718. }
  1719. }).on("mouseout.jqGrid", function (e) {
  1720. ptr = $(e.target).closest("tr.jqgrow");
  1721. $(ptr).removeClass(hoverClasses);
  1722. });
  1723. }
  1724. p.frozenColumns = false;
  1725. if (p.sortable) {
  1726. var $colHeaderRow = $(grid.hDiv).find(".ui-jqgrid-htable .ui-jqgrid-labels");
  1727. $colHeaderRow.sortable("destroy");
  1728. $self.jqGrid("setGridParam", {
  1729. sortable: {
  1730. options: {
  1731. items: ">th:not(:has(#jqgh_" + tid + "_cb" + ",#jqgh_" + tid + "_rn" + ",#jqgh_" + tid + "_subgrid),:hidden)"
  1732. }
  1733. }
  1734. });
  1735. $self.jqGrid("sortableColumns", $colHeaderRow);
  1736. }
  1737. }
  1738. });
  1739. }
  1740. });
  1741. // end module grid.custom
  1742. }));