grid.subgrid.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /**
  2. * jqGrid extension for SubGrid Data
  3. * Copyright (c) 2008-2014, Tony Tomov, tony@trirand.com
  4. * Copyright (c) 2014-2018, Oleg Kiriljuk, oleg.kiriljuk@ok-soft-gmbh.com
  5. * Dual licensed under the MIT and GPL licenses:
  6. * http://www.opensource.org/licenses/mit-license.php
  7. * http://www.gnu.org/licenses/gpl-2.0.html
  8. **/
  9. /*jshint eqeqeq:false */
  10. /*global jQuery, define, exports, module, require */
  11. /*jslint eqeq: true, nomen: true, plusplus: true, unparam: true, white: true */
  12. (function (factory) {
  13. "use strict";
  14. if (typeof define === "function" && define.amd) {
  15. // AMD. Register as an anonymous module.
  16. define([
  17. "jquery",
  18. "./grid.base"
  19. ], factory);
  20. } else if (typeof module === "object" && module.exports) {
  21. // Node/CommonJS
  22. module.exports = function (root, $) {
  23. if (!root) {
  24. root = window;
  25. }
  26. if ($ === undefined) {
  27. // require("jquery") returns a factory that requires window to
  28. // build a jQuery instance, we normalize how we use modules
  29. // that require this pattern but the window provided is a noop
  30. // if it's defined (how jquery works)
  31. $ = typeof window !== "undefined" ?
  32. require("jquery") :
  33. require("jquery")(root);
  34. }
  35. require("./grid.base");
  36. factory($);
  37. return $;
  38. };
  39. } else {
  40. // Browser globals
  41. factory(jQuery);
  42. }
  43. }(function ($) {
  44. "use strict";
  45. var jgrid = $.jgrid, jqID = jgrid.jqID, base = $.fn.jqGrid;
  46. // begin module grid.subgrid
  47. var subGridFeedback = function () {
  48. var args = $.makeArray(arguments);
  49. args[0] = "subGrid" + args[0].charAt(0).toUpperCase() + args[0].substring(1);
  50. args.unshift("");
  51. args.unshift("");
  52. args.unshift(this.p);
  53. return jgrid.feedback.apply(this, args);
  54. },
  55. collapseOrExpand = function (rowid, className) {
  56. return this.each(function () {
  57. if (this.grid && rowid != null && this.p.subGrid === true) {
  58. var tr = $(this).jqGrid("getInd", rowid, true);
  59. $(tr).find(">td." + className).trigger("click");
  60. }
  61. });
  62. };
  63. jgrid.extend({
  64. setSubGrid: function () {
  65. return this.each(function () {
  66. var p = this.p, $self = $(this), cm = p.subGridModel[0], i,
  67. getIcon = function (path) {
  68. return $self.jqGrid("getIconRes", path);
  69. };
  70. p.subGridOptions = $.extend({
  71. commonIconClass: getIcon("subgrid.common"),
  72. plusicon: getIcon("subgrid.plus"),
  73. minusicon: getIcon("subgrid.minus"),
  74. openicon: (p.direction === "rtl" ? getIcon("subgrid.openRtl") : getIcon("subgrid.openLtr")),
  75. expandOnLoad: false,
  76. delayOnLoad: 50,
  77. selectOnExpand: false,
  78. selectOnCollapse: false,
  79. reloadOnExpand: true
  80. }, p.subGridOptions || {});
  81. p.colNames.unshift("");
  82. p.colModel.unshift({
  83. name: "subgrid",
  84. width: jgrid.cell_width ? p.subGridWidth + p.cellLayout : p.subGridWidth,
  85. labelClasses: "jqgh_subgrid",
  86. sortable: false,
  87. resizable: false,
  88. hidedlg: true,
  89. search: false,
  90. fixed: true,
  91. frozen: true
  92. });
  93. if (cm) {
  94. cm.align = $.extend([], cm.align || []);
  95. for (i = 0; i < cm.name.length; i++) {
  96. cm.align[i] = cm.align[i] || "left";
  97. }
  98. }
  99. });
  100. },
  101. addSubGridCell: function (pos, iRow, rowid, item) {
  102. var self = this[0], subGridOptions = self.p.subGridOptions,
  103. hasSubgrid = $.isFunction(subGridOptions.hasSubgrid) ?
  104. subGridOptions.hasSubgrid.call(self, { rowid: rowid, iRow: iRow, iCol: pos, data: item }) :
  105. true;
  106. return self.p == null ? "" :
  107. "<td role='gridcell' class='" + base.getGuiStyles.call(this, "subgrid.tdStart", hasSubgrid ? "ui-sgcollapsed sgcollapsed" : "") + "' " +
  108. self.formatCol(pos, iRow) + ">" +
  109. (hasSubgrid ? "<div class='" + base.getGuiStyles.call(this, "subgrid.buttonDiv", "sgbutton-div") +
  110. "'><a role='button' class='" + base.getGuiStyles.call(this, "subgrid.button", "sgbutton") +
  111. "'><span class='" + jgrid.mergeCssClasses(subGridOptions.commonIconClass, subGridOptions.plusicon) + "'></span></a></div>" : "&nbsp;") +
  112. "</td>";
  113. },
  114. addSubGrid: function (pos, sind) {
  115. return this.each(function () {
  116. var ts = this, p = ts.p, cm = p.subGridModel[0],
  117. getSubgridStyle = function (name, calsses) {
  118. return base.getGuiStyles.call(ts, "subgrid." + name, calsses || "");
  119. },
  120. thSubgridClasses = getSubgridStyle("thSubgrid", "ui-th-subgrid ui-th-column ui-th-" + p.direction),
  121. rowSubTableClasses = getSubgridStyle("rowSubTable", "ui-subtblcell"),
  122. rowClasses = getSubgridStyle("row", "ui-subgrid ui-row-" + p.direction),
  123. tdWithIconClasses = getSubgridStyle("tdWithIcon", "subgrid-cell"),
  124. tdDataClasses = getSubgridStyle("tdData", "subgrid-data"),
  125. subGridCell = function ($tr, cell, pos1) {
  126. var align = cm.align[pos1],
  127. $td = $("<td" +
  128. (align ? " style='text-align:" + align + ";'" : "") +
  129. "></td>").html(cell);
  130. $tr.append($td);
  131. },
  132. fillXmlBody = function (data, $tbody) {
  133. var sgmap = p.xmlReader.subgrid;
  134. $(sgmap.root + " " + sgmap.row, data).each(function () {
  135. var f, i, $tr = $("<tr class='" + rowSubTableClasses + "'></tr>");
  136. if (sgmap.repeatitems === true) {
  137. $(sgmap.cell, this).each(function (j) {
  138. subGridCell($tr, $(this).text() || "&#160;", j);
  139. });
  140. } else {
  141. f = cm.mapping || cm.name;
  142. if (f) {
  143. for (i = 0; i < f.length; i++) {
  144. subGridCell($tr, jgrid.getXmlData(this, f[i]) || "&#160;", i);
  145. }
  146. }
  147. }
  148. $tbody.append($tr);
  149. });
  150. },
  151. fillJsonBody = function (data, $tbody) {
  152. var $tr, i, j, f, cur, sgmap = p.jsonReader.subgrid,
  153. result = jgrid.getAccessor(data, sgmap.root);
  154. if (result != null) {
  155. for (i = 0; i < result.length; i++) {
  156. cur = result[i];
  157. $tr = $("<tr class='" + rowSubTableClasses + "'></tr>");
  158. if (sgmap.repeatitems === true) {
  159. if (sgmap.cell) {
  160. cur = cur[sgmap.cell];
  161. }
  162. for (j = 0; j < cur.length; j++) {
  163. subGridCell($tr, cur[j] || "&#160;", j);
  164. }
  165. } else {
  166. f = cm.mapping || cm.name;
  167. if (f.length) {
  168. for (j = 0; j < f.length; j++) {
  169. subGridCell($tr, jgrid.getAccessor(cur, f[j]) || "&#160;", j);
  170. }
  171. }
  172. }
  173. $tbody.append($tr);
  174. }
  175. }
  176. },
  177. subGridXmlOrJson = function (sjxml, sbid, fullBody) {
  178. var $th, i, subgridTableClasses = getSubgridStyle("legacyTable", "ui-jqgrid-legacy-subgrid" +
  179. (p.altRows === true && $(ts).jqGrid("isBootstrapGuiStyle") ? " table-striped" : "")),
  180. $table = $("<table" +
  181. (subgridTableClasses ? " style='width:1px' role='presentation' class='" + subgridTableClasses + "'" : "") +
  182. "><thead></thead><tbody></tbody></table>"),
  183. $tr = $("<tr></tr>");
  184. ts.grid.endReq.call(ts);
  185. for (i = 0; i < cm.name.length; i++) {
  186. $th = $("<th class='" + thSubgridClasses + "'></th>")
  187. .html(cm.name[i])
  188. .width(cm.width[i]);
  189. $tr.append($th);
  190. }
  191. $tr.appendTo($table[0].tHead);
  192. fullBody(sjxml, $($table[0].tBodies[0]));
  193. $("#" + jqID(p.id + "_" + sbid)).append($table);
  194. return false;
  195. },
  196. populatesubgrid = function (rd) {
  197. var sid = $(rd).attr("id"), dp = { nd_: (new Date().getTime()) }, iCol, j;
  198. dp[p.prmNames.subgridid] = sid;
  199. if (!cm) {
  200. return false;
  201. }
  202. if (cm.params) {
  203. for (j = 0; j < cm.params.length; j++) {
  204. iCol = p.iColByName[cm.params[j]];
  205. if (iCol !== undefined) {
  206. dp[p.colModel[iCol].name] = $(rd.cells[iCol]).text().replace(/\&#160\;/ig, "");
  207. }
  208. }
  209. }
  210. if (!ts.grid.hDiv.loading) {
  211. ts.grid.beginReq.call(ts);
  212. if (!p.subgridtype) {
  213. p.subgridtype = p.datatype;
  214. }
  215. if ($.isFunction(p.subgridtype)) {
  216. p.subgridtype.call(ts, dp);
  217. } else {
  218. p.subgridtype = p.subgridtype.toLowerCase();
  219. }
  220. switch (p.subgridtype) {
  221. case "xml":
  222. case "json":
  223. $.ajax($.extend({
  224. type: p.mtype,
  225. url: $.isFunction(p.subGridUrl) ? p.subGridUrl.call(ts, dp) : p.subGridUrl,
  226. dataType: p.subgridtype,
  227. context: sid,
  228. data: jgrid.serializeFeedback.call(ts, p.serializeSubGridData, "jqGridSerializeSubGridData", dp),
  229. success: function (data) {
  230. subGridXmlOrJson(
  231. data,
  232. this,
  233. p.subgridtype === "xml" ? fillXmlBody : fillJsonBody
  234. );
  235. },
  236. error: function (jqXHR, textStatus, errorThrown) {
  237. var loadError = p.loadSubgridError === undefined ?
  238. p.loadError :
  239. p.loadSubgridError;
  240. ts.grid.endReq.call(ts);
  241. if ($.isFunction(loadError)) {
  242. loadError.call(ts, jqXHR, textStatus, errorThrown);
  243. }
  244. // for compatibility only
  245. if (!p.subGridOptions.noEmptySubgridOnError) {
  246. subGridXmlOrJson(
  247. null,
  248. this,
  249. p.subgridtype === "xml" ? fillXmlBody : fillJsonBody
  250. );
  251. }
  252. }
  253. }, jgrid.ajaxOptions, p.ajaxSubgridOptions || {}));
  254. break;
  255. }
  256. }
  257. return false;
  258. },
  259. onClick = function () {
  260. var tr = $(this).parent("tr")[0], r = tr.nextSibling, rowid = tr.id, subgridDivId = p.id + "_" + rowid, atd,
  261. iconClass = function (iconName) {
  262. return jgrid.mergeCssClasses(p.subGridOptions.commonIconClass, p.subGridOptions[iconName]);
  263. },
  264. nhc = 1;
  265. $.each(p.colModel, function () {
  266. if (this.hidden === true || this.name === "rn" || this.name === "cb") {
  267. // ??? probably one should don't calculate hidden columns of subgrid?
  268. // (remove this.hidden === true part from the if) ???
  269. nhc++;
  270. }
  271. });
  272. if ($(this).hasClass("sgcollapsed")) {
  273. if (p.subGridOptions.reloadOnExpand === true || (p.subGridOptions.reloadOnExpand === false && !$(r).hasClass("ui-subgrid"))) {
  274. atd = pos >= 1 ? "<td colspan='" + pos + "'>&#160;</td>" : "";
  275. if (!subGridFeedback.call(ts, "beforeExpand", subgridDivId, rowid)) {
  276. return;
  277. }
  278. $(tr).after("<tr role='row' class='" + rowClasses + "'>" + atd + "<td class='" + tdWithIconClasses +
  279. "'><span class='" + iconClass("openicon") + "'></span></td><td colspan='" + parseInt(p.colNames.length - nhc, 10) +
  280. "' class='" + tdDataClasses + "'><div id='" + subgridDivId + "' class='tablediv'></div></td></tr>");
  281. $(ts).triggerHandler("jqGridSubGridRowExpanded", [subgridDivId, rowid]);
  282. if ($.isFunction(p.subGridRowExpanded)) {
  283. p.subGridRowExpanded.call(ts, subgridDivId, rowid);
  284. } else {
  285. populatesubgrid(tr);
  286. }
  287. } else {
  288. $(r).show();
  289. }
  290. $(this).html(
  291. "<div class='" + base.getGuiStyles.call(ts, "subgrid.buttonDiv", "sgbutton-div") +
  292. "'><a role='button' class='" + base.getGuiStyles.call(ts, "subgrid.button", "sgbutton") +
  293. "'><span class='" + iconClass("minusicon") + "'></span></a></div>"
  294. ).removeClass("sgcollapsed").addClass("sgexpanded");
  295. if (p.subGridOptions.selectOnExpand) {
  296. $(ts).jqGrid("setSelection", rowid);
  297. }
  298. } else if ($(this).hasClass("sgexpanded")) {
  299. if (!subGridFeedback.call(ts, "beforeCollapse", subgridDivId, rowid)) {
  300. return;
  301. }
  302. if (p.subGridOptions.reloadOnExpand === true) {
  303. $(r).remove(".ui-subgrid");
  304. } else if ($(r).hasClass("ui-subgrid")) { // incase of dynamic deleting
  305. $(r).hide();
  306. }
  307. $(this).html(
  308. "<div class='" + base.getGuiStyles.call(ts, "subgrid.buttonDiv", "sgbutton-div") +
  309. "'><a role='button' class='" + base.getGuiStyles.call(ts, "subgrid.button", "sgbutton") +
  310. "'><span class='" + iconClass("plusicon") + "'></span></a></div>"
  311. ).removeClass("sgexpanded").addClass("sgcollapsed");
  312. if (p.subGridOptions.selectOnCollapse) {
  313. $(ts).jqGrid("setSelection", rowid);
  314. }
  315. }
  316. return false;
  317. },
  318. len,
  319. tr1,
  320. $td1,
  321. iRow = 1;
  322. if (!ts.grid) {
  323. return;
  324. }
  325. len = ts.rows.length;
  326. if (sind !== undefined && sind > 0) {
  327. iRow = sind;
  328. len = sind + 1;
  329. }
  330. while (iRow < len) {
  331. tr1 = ts.rows[iRow];
  332. if ($(tr1).hasClass("jqgrow")) {
  333. $td1 = $(tr1.cells[pos]);
  334. if ($td1.hasClass("ui-sgcollapsed")) {
  335. if (p.scroll) {
  336. $td1.off("click");
  337. }
  338. $td1.on("click", onClick);
  339. }
  340. }
  341. iRow++;
  342. }
  343. if (p.subGridOptions.expandOnLoad === true) {
  344. var iColSubgrid = p.iColByName.subgrid;
  345. $(ts.rows).filter(".jqgrow").each(function (index, row) {
  346. $(row.cells[iColSubgrid])
  347. .filter(".sgcollapsed")
  348. .children(".sgbutton-div")
  349. .children(".sgbutton")
  350. .click();
  351. });
  352. }
  353. ts.subGridXml = function (xml, sid) {
  354. return subGridXmlOrJson(xml, sid, fillXmlBody);
  355. };
  356. ts.subGridJson = function (json, sid) {
  357. return subGridXmlOrJson(json, sid, fillJsonBody);
  358. };
  359. });
  360. },
  361. expandSubGridRow: function (rowid) {
  362. return collapseOrExpand.call(this, rowid, "sgcollapsed");
  363. },
  364. collapseSubGridRow: function (rowid) {
  365. return collapseOrExpand.call(this, rowid, "sgexpanded");
  366. },
  367. toggleSubGridRow: function (rowid) {
  368. return collapseOrExpand.call(this, rowid, "ui-sgcollapsed");
  369. }
  370. });
  371. // end module grid.subgrid
  372. }));