grid.jqueryui.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964
  1. /**
  2. * jqGrid addons using jQuery UI
  3. * Author: Mark Williams
  4. * Changed by 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. * depends on jQuery UI
  9. **/
  10. /*jshint evil:true, eqeqeq:false, eqnull:true, devel:true */
  11. /*global jQuery, define, exports, module, require */
  12. /*jslint browser: true, devel: true, eqeq: true, nomen: true, plusplus: true, unparam: true, vars: true, white: true */
  13. (function (global, factory) {
  14. "use strict";
  15. if (typeof define === "function" && define.amd) {
  16. // AMD. Register as an anonymous module.
  17. define([
  18. "jquery",
  19. "./grid.base",
  20. //"../plugins/ui.multiselect",
  21. "free-jqgrid-plugins/ui.multiselect",
  22. "jquery-ui/dialog",
  23. "jquery-ui/draggable",
  24. "jquery-ui/droppable",
  25. "jquery-ui/resizable",
  26. "jquery-ui/sortable"
  27. ], function ($) {
  28. return factory($, global, global.document);
  29. });
  30. } else if (typeof module === "object" && module.exports) {
  31. // Node/CommonJS
  32. module.exports = function (root, $) {
  33. if (!root) {
  34. root = window;
  35. }
  36. if ($ === undefined) {
  37. // require("jquery") returns a factory that requires window to
  38. // build a jQuery instance, we normalize how we use modules
  39. // that require this pattern but the window provided is a noop
  40. // if it's defined (how jquery works)
  41. $ = typeof window !== "undefined" ?
  42. require("jquery") :
  43. require("jquery")(root);
  44. }
  45. require("./grid.base");
  46. //require("../plugins/ui.multiselect");
  47. require("free-jqgrid-plugins/ui.multiselect");
  48. require("jquery-ui/dialog");
  49. require("jquery-ui/draggable");
  50. require("jquery-ui/droppable");
  51. require("jquery-ui/resizable");
  52. require("jquery-ui/sortable");
  53. factory($, root, root.document);
  54. return $;
  55. };
  56. } else {
  57. // Browser globals
  58. factory(jQuery, global, global.document);
  59. }
  60. }(typeof window !== "undefined" ? window : this, function ($, window, document) {
  61. "use strict";
  62. var jgrid = $.jgrid, jqID = jgrid.jqID;
  63. // begin module grid.jqueryui
  64. var $UiMultiselect = $.ui != null ? $.ui.multiselect : null,
  65. reorderSelectedColumns = function (iColItem) {
  66. /* Background information:
  67. *
  68. * Multiselect contains the list of selected items this.selectedList,
  69. * which is jQuery wrapper of <ul> element. The items of this.selectedList
  70. * are <li> elements, which represent visible (hidden:false) and movable
  71. * (hidedlg:false) columns of the grid.
  72. *
  73. * Additionally there are exist hidden <select multiple="multiple">.
  74. * Every <option> of the <select> corresponds the column of the grid.
  75. * The visible columns (hidden:false) are selected. The value of the <option>
  76. * contains the column index (iCol) in colModel. <li> elements of
  77. * this.selectedLis have data with the "optionLink" pointed to the corresponding
  78. * option of the hidden select.
  79. *
  80. * this.grid is the DOM of the grid and this.newColOrder is the array with
  81. * ALL column names in the order, which should be applied after reordering
  82. * the grid. Additionally this.gh contains the COPY of p.groupHeader.groupHeaders.
  83. * It's important that startColumnName property of elements of
  84. * p.groupHeader.groupHeaders could be changed during reordering of the columns.
  85. * On the other side the user can't click Cancel button of columnChooser for
  86. * breaking reordering. Because of that this.gh contains the COPY of
  87. * p.groupHeader.groupHeaders and the original p.groupHeader.groupHeaders will be
  88. * not changed in the reorderSelectedColumns function.
  89. *
  90. * An important implementation problem: reorderSelectedColumns function will be
  91. * called after ONE reordering of the columns. On the other side, the user can
  92. * reorder the columns MULTIPLE TIMES before saving the new order. Thus, one
  93. * have to take in consideration not only the original order of columns in
  94. * colModel, but THE CURRENT order of the columns saved only internally in
  95. * the dialog in this.newColOrder.
  96. */
  97. if (this.grid != null && this.grid.p != null) {
  98. var that = this, p = this.grid.p, iCol, j, iGrp, ghItem,
  99. gh = this.gh, selectedList = this.selectedList, inGroup = this.inGroup,
  100. items = selectedList.find("li"), optionLink,
  101. indexOfAddedItem = items.length - 1,
  102. enumSelected = function (callback, startIndex, reverse) {
  103. var i, opt, items = selectedList.find("li");
  104. if (startIndex === undefined) {
  105. startIndex = reverse ? items.length - 1 : 0;
  106. }
  107. for (i = startIndex; !reverse ? i < items.length : i >= 0; !reverse ? i++ : i--) {
  108. opt = $(items[i]).data("optionLink");
  109. if (opt && callback.call(items[i], parseInt(opt.val(), 10), i)) {
  110. return i;
  111. }
  112. }
  113. },
  114. updateNewColOrder = function () {
  115. // !!! the function set additionally indexOfAddedItem and update items
  116. // remove iColItem from this.newColOrder
  117. j = $.inArray(p.colModel[iColItem].name, that.newColOrder);
  118. if (j >= 0) {
  119. that.newColOrder.splice(j, 1);
  120. }
  121. // refill items
  122. items = selectedList.find("li");
  123. // iCol will be the index in newColOrder. The order of columns in items
  124. // should be mostly THE SAME like in selectedList, but newColOrder
  125. // contains additional elements (hidden and hidedlg). The array items
  126. // on the other side contains just inserted iColItem element.
  127. // we need find indexOfAddedItem - the index of <li> with iColItem
  128. // in the list of this.selectedList.find("li") elements
  129. iCol = 0;
  130. enumSelected(
  131. function (iColOld, index) {
  132. if (iColOld === iColItem) {
  133. indexOfAddedItem = index;
  134. // if iColItem is in the same header group as p.colModel[iCol] or
  135. // both belongs no header group then the column could be NOT in
  136. // selectedList. I find better to insert the item AFTER the hidden
  137. // or non-movable columns (like "rn", "subgrid" column or other)
  138. while (iCol >= 0 && iCol < p.colModel.length && iCol !== iColItem &&
  139. (p.colModel[iCol].hidden || p.colModel[iCol].hidedlg) &&
  140. (inGroup == null ||
  141. //inGroup[iCol] !== undefined && inGroup[iColItem] !== undefined &&
  142. inGroup[iCol] === inGroup[iColItem])) {
  143. iCol++;
  144. }
  145. that.newColOrder.splice(iCol, 0, p.colModel[iColItem].name);
  146. return true; // stop enumeration
  147. }
  148. // selectedList contains SUBSET of columns from colModel, but newColOrder
  149. // contains ALL columns. It's important that all columns from selectedList
  150. // exist in newColOrder IN THE SAME order. Thus iCol will be the current
  151. // index in newColOrder array with p.colModel[iColOld].name column name.
  152. iCol = $.inArray(p.colModel[iColOld].name, that.newColOrder, iCol);
  153. if (iCol < 0) {
  154. // to be sure that the code works in case of some errors too
  155. iCol = $.inArray(p.colModel[iColOld].name, that.newColOrder);
  156. }
  157. iCol++;
  158. }
  159. );
  160. },
  161. enumPreviousAndInsertAfter = function (iCol) {
  162. if (inGroup[iCol] === inGroup[iColItem]) {
  163. $(this).after(items[indexOfAddedItem]);
  164. updateNewColOrder();
  165. return true; // stop enumeration
  166. }
  167. },
  168. enumNextAndInsertBefore = function (iCol) {
  169. if (inGroup[iCol] === inGroup[iColItem]) {
  170. $(this).before(items[indexOfAddedItem]);
  171. updateNewColOrder();
  172. return true; // stop enumeration
  173. }
  174. },
  175. updateStartColumn = function (iCol) {
  176. if (inGroup[iCol] === inGroup[iColItem] && inGroup[iCol] !== undefined) {
  177. gh[inGroup[iCol]].startColumnName = p.colModel[iCol].name;
  178. return true; // stop enumeration
  179. }
  180. };
  181. // Fix potition of added/moved item iColItem in that.newColOrder array.
  182. // We syncronize only the initial state of newColOrder. The position of
  183. // iColItem item can be changed later in both selectedList and newColOrder
  184. updateNewColOrder();
  185. if (gh && gh[inGroup[iColItem]] !== undefined) {
  186. // the item belong to some group
  187. ghItem = gh[inGroup[iColItem]];
  188. for (j = 0; j < ghItem.numberOfColumns; j++) {
  189. iCol = p.iColByName[ghItem.startColumnName] + j;
  190. if (!p.colModel[iCol].hidden && !p.colModel[iCol].hidedlg) {
  191. // the columns are displayed in the selectedList
  192. // We need to enumerate items in reverse order and to find the index of the item
  193. // in the array items comparing the items by $(items[j]).data("optionLink").val()
  194. // If the item is found then append prevously found item AFTER this one.
  195. // We can use j variable bacause the outer loop will be exit (see break below)
  196. enumSelected(
  197. enumPreviousAndInsertAfter,
  198. indexOfAddedItem - 1,
  199. true // enum in reverse order
  200. );
  201. // If step 2 didn't find the group then we need examin NEXT items and find
  202. // the items from the same group. We should test the items in sequential order
  203. // after the item found. If the item is found then apend prevously found
  204. // item BEFORE this one.
  205. enumSelected(
  206. enumNextAndInsertBefore,
  207. indexOfAddedItem + 1
  208. );
  209. // fix the name of the first column in the group
  210. enumSelected(updateStartColumn);
  211. break; // !!!
  212. }
  213. }
  214. } else if (gh) {
  215. // The item from no group is added/moved.
  216. // We have to verify that the item is not dropped inside of some header group
  217. // find the index of added/moved element in this.selectedList.find("li")
  218. items = selectedList.find("li");
  219. j = enumSelected(function (iCol) {
  220. if (iCol === iColItem) {
  221. return true;
  222. }
  223. });
  224. if (j + 1 >= items.length || j < 0) {
  225. return;
  226. }
  227. optionLink = $(items[j + 1]).data("optionLink");
  228. if (optionLink) {
  229. iGrp = inGroup[parseInt(optionLink.val(), 10)];
  230. if (iGrp !== undefined) {
  231. optionLink = $(items[j - 1]).data("optionLink");
  232. if (optionLink && inGroup[parseInt(optionLink.val(), 10)] === iGrp) {
  233. // The next and the previous items are in the same group, but
  234. // the added/moved item in NOT in the group.
  235. // We have to move the item items[j] AFTER the last item of the group.
  236. var iColNotInTheGroup = enumSelected(
  237. function (iCol) {
  238. if (inGroup[iCol] !== iGrp) {
  239. return true; // stop enumeration
  240. }
  241. },
  242. j + 1 // start enumeration with the index
  243. );
  244. $(items[iColNotInTheGroup === undefined || iColNotInTheGroup >= items.length ? items.length - 1 : iColNotInTheGroup - 1])
  245. .after(items[indexOfAddedItem]);
  246. updateNewColOrder();
  247. }
  248. }
  249. }
  250. }
  251. }
  252. };
  253. if (jgrid.msie && jgrid.msiever() === 8) {
  254. $.expr[":"].hidden = function (elem) {
  255. return elem.offsetWidth === 0 || elem.offsetHeight === 0 ||
  256. elem.style.display === "none";
  257. };
  258. }
  259. // requiere load multiselect before grid
  260. jgrid._multiselect = false;
  261. if ($UiMultiselect) {
  262. if ($UiMultiselect.prototype._setSelected) {
  263. var setSelected = $UiMultiselect.prototype._setSelected;
  264. $UiMultiselect.prototype._setSelected = function (item, selected) {
  265. // the method will be called if the user clicks "+" button on the item of "available" list
  266. // or if the user clicks "-" button on the item of "selected" list.
  267. // The parameter "selected" is equal true on click on the item of "selected" list
  268. // and false on click on the item of "available" list.
  269. var ret = setSelected.call(this, item, selected), elt = this.element,
  270. iColItem = parseInt(item.data("optionLink").val(), 10);
  271. if (selected && this.selectedList) {
  272. // reorder items of selectedList
  273. // all items of selectedList from one group have to be together
  274. // all items of availableList from one group have to be together
  275. reorderSelectedColumns.call(this, iColItem);
  276. // apply the new sort order to the original selectbox
  277. this.selectedList.find("li").each(function () {
  278. if ($(this).data("optionLink")) {
  279. $(this).data("optionLink").remove().appendTo(elt);
  280. }
  281. });
  282. }
  283. return ret;
  284. };
  285. }
  286. if ($UiMultiselect.prototype.destroy) {
  287. $UiMultiselect.prototype.destroy = function () {
  288. var self = this;
  289. self.element.show();
  290. self.container.remove();
  291. if ($.Widget === undefined) {
  292. $.widget.prototype.destroy.apply(self, arguments);
  293. } else {
  294. $.Widget.prototype.destroy.apply(self, arguments);
  295. }
  296. };
  297. }
  298. jgrid._multiselect = true;
  299. }
  300. jgrid.extend({
  301. sortableColumns: function (tblrow) {
  302. return this.each(function () {
  303. var ts = this, p = ts.p, tid = jqID(p.id);
  304. if (!p || !p.sortable || !$.isFunction($.fn.sortable)) { return; }
  305. function start() { p.disableClick = true; }
  306. var sortableOpts = {
  307. tolerance: "pointer",
  308. axis: "x",
  309. scrollSensitivity: "1",
  310. items: ">th:not(:has(#jqgh_" + tid + "_cb" + ",#jqgh_" + tid + "_rn" + ",#jqgh_" + tid + "_subgrid),:hidden)",
  311. placeholder: {
  312. element: function (item) {
  313. var el = $(document.createElement(item[0].nodeName))
  314. .addClass(item[0].className + " ui-sortable-placeholder ui-state-highlight")
  315. .removeClass("ui-sortable-helper")[0];
  316. return el;
  317. },
  318. update: function (self, o) {
  319. o.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css("paddingTop") || 0, 10) - parseInt(self.currentItem.css("paddingBottom") || 0, 10));
  320. o.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css("paddingLeft") || 0, 10) - parseInt(self.currentItem.css("paddingRight") || 0, 10));
  321. }
  322. },
  323. start: function () {
  324. ts.grid.hDiv.scrollLeft = ts.grid.bDiv.scrollLeft;
  325. },
  326. update: function (event, ui) {
  327. var th = $(">th", $(ui.item).parent()), tid1 = p.id + "_", permutation = [];
  328. th.each(function () {
  329. var id = $(">div", this).get(0).id.replace(/^jqgh_/, "").replace(tid1, ""),
  330. iCol = p.iColByName[id];
  331. if (iCol !== undefined) {
  332. permutation.push(iCol);
  333. }
  334. });
  335. $(ts).jqGrid("remapColumns", permutation, true, true);
  336. if ($.isFunction(p.sortable.update)) {
  337. p.sortable.update(permutation);
  338. }
  339. setTimeout(function () { p.disableClick = false; }, 50);
  340. }
  341. };
  342. if (p.sortable.options) {
  343. $.extend(sortableOpts, p.sortable.options);
  344. } else if ($.isFunction(p.sortable)) {
  345. p.sortable = { "update": p.sortable };
  346. }
  347. if (sortableOpts.start) {
  348. var s = sortableOpts.start;
  349. sortableOpts.start = function (e, ui) {
  350. start();
  351. s.call(this, e, ui);
  352. };
  353. } else {
  354. sortableOpts.start = start;
  355. }
  356. if (p.sortable.exclude) {
  357. sortableOpts.items += ":not(" + p.sortable.exclude + ")";
  358. }
  359. var $e = tblrow.sortable(sortableOpts), dataObj = $e.data("sortable") || $e.data("uiSortable") || $e.data("ui-sortable");
  360. if (dataObj != null) {
  361. dataObj.floating = true;
  362. }
  363. });
  364. },
  365. columnChooser: function (opts) {
  366. var $self = this, self = $self[0], p = self.p, selector, select, dopts, mopts,
  367. $dialogContent, multiselectData, listHeight,
  368. colModel = p.colModel, nCol = colModel.length, colNames = p.colNames,
  369. getMultiselectWidgetData = function ($elem) {
  370. return ($UiMultiselect && $UiMultiselect.prototype && $elem.data($UiMultiselect.prototype.widgetFullName || $UiMultiselect.prototype.widgetName)) ||
  371. $elem.data("ui-multiselect") || $elem.data("multiselect");
  372. };
  373. if ($("#colchooser_" + jqID(p.id)).length) { return; }
  374. selector = $('<div id="colchooser_' + p.id + '" style="position:relative;overflow:hidden"><div><select multiple="multiple"></select></div></div>');
  375. select = $("select", selector);
  376. function call(fn, obj) {
  377. if (!fn) { return; }
  378. if (typeof fn === "string") {
  379. if ($.fn[fn]) {
  380. $.fn[fn].apply(obj, $.makeArray(arguments).slice(2));
  381. }
  382. } else if ($.isFunction(fn)) {
  383. fn.apply(obj, $.makeArray(arguments).slice(2));
  384. }
  385. }
  386. opts = $.extend({
  387. width: 400,
  388. height: 240,
  389. classname: null,
  390. done: function (perm) {
  391. if (perm) {
  392. $self.jqGrid("remapColumns", perm, true);
  393. }
  394. },
  395. /* msel is either the name of a ui widget class that
  396. extends a multiselect, or a function that supports
  397. creating a multiselect object (with no argument,
  398. or when passed an object), and destroying it (when
  399. passed the string "destroy"). */
  400. msel: "multiselect",
  401. /* "msel_opts" : {}, */
  402. /* dlog is either the name of a ui widget class that
  403. behaves in a dialog-like way, or a function, that
  404. supports creating a dialog (when passed dlog_opts)
  405. or destroying a dialog (when passed the string
  406. "destroy")
  407. */
  408. dlog: "dialog",
  409. dialog_opts: {
  410. minWidth: 470,
  411. dialogClass: "ui-jqdialog"
  412. },
  413. /* dlog_opts is either an option object to be passed
  414. to "dlog", or (more likely) a function that creates
  415. the options object.
  416. The default produces a suitable options object for
  417. ui.dialog */
  418. dlog_opts: function (options) {
  419. var buttons = {};
  420. buttons[options.bSubmit] = function () {
  421. options.apply_perm();
  422. options.cleanup(false);
  423. };
  424. buttons[options.bCancel] = function () {
  425. options.cleanup(true);
  426. };
  427. return $.extend(true, {
  428. buttons: buttons,
  429. close: function () {
  430. options.cleanup(true);
  431. },
  432. modal: options.modal || false,
  433. resizable: options.resizable || true,
  434. width: options.width + 70,
  435. resize: function () {
  436. var widgetData = getMultiselectWidgetData(select),
  437. $thisDialogContent = widgetData.container.closest(".ui-dialog-content");
  438. if ($thisDialogContent.length > 0 && typeof $thisDialogContent[0].style === "object") {
  439. $thisDialogContent[0].style.width = "";
  440. } else {
  441. $thisDialogContent.css("width", ""); // or just remove width style
  442. }
  443. widgetData.selectedList.height(Math.max(widgetData.selectedContainer.height() - widgetData.selectedActions.outerHeight() - 1, 1));
  444. widgetData.availableList.height(Math.max(widgetData.availableContainer.height() - widgetData.availableActions.outerHeight() - 1, 1));
  445. }
  446. }, options.dialog_opts || {});
  447. },
  448. /* Function to get the permutation array, and pass it to the
  449. "done" function */
  450. apply_perm: function () {
  451. var perm = new Array(p.colModel.length), i, gHead,
  452. showHideColOptions = {
  453. notSkipFrozen: opts.notSkipFrozen === undefined ? false : opts.notSkipFrozen,
  454. skipSetGridWidth: true,
  455. skipSetGroupHeaders: true
  456. };
  457. // we can use remapColumnsByName instead of remapColumns in general,
  458. // but we try to hold the compatibility with old version. Thus we
  459. // fill perm based on multiselectData.newColOrder
  460. for (i = 0; i < p.colModel.length; i++) {
  461. perm[i] = p.iColByName[multiselectData.newColOrder[i]];
  462. }
  463. $("option", select).each(function () {
  464. if ($(this).is(":selected")) {
  465. $self.jqGrid("showCol", colModel[this.value].name, showHideColOptions);
  466. } else {
  467. $self.jqGrid("hideCol", colModel[this.value].name, showHideColOptions);
  468. }
  469. });
  470. if (opts.done) {
  471. opts.done.call($self, perm);
  472. }
  473. if (p.groupHeader && (typeof p.groupHeader === "object" || $.isFunction(p.groupHeader))) {
  474. $self.jqGrid("destroyGroupHeader", false);
  475. p.groupHeader.groupHeaders = multiselectData.gh; // use modified groupHeader
  476. if (p.pivotOptions != null && p.pivotOptions.colHeaders != null && p.pivotOptions.colHeaders.length > 1) {
  477. gHead = p.pivotOptions.colHeaders;
  478. for (i = 0; i < gHead.length; i++) {
  479. // Multiple calls of setGroupHeaders for one grid are wrong,
  480. // but there are produces good results in case of usage
  481. // useColSpanStyle: false option. The rowspan values
  482. // needed be increased in case of usage useColSpanStyle: true
  483. if (gHead[i] && gHead[i].groupHeaders.length) {
  484. $self.jqGrid("setGroupHeaders", gHead[i]);
  485. }
  486. }
  487. } else {
  488. $self.jqGrid("setGroupHeaders", p.groupHeader);
  489. }
  490. }
  491. var newWidth = !p.autowidth && (p.widthOrg === undefined || p.widthOrg === "auto" || p.widthOrg === "100%") ? p.tblwidth : p.width;
  492. if (newWidth !== p.width) {
  493. $self.jqGrid("setGridWidth", newWidth, p.shrinkToFit);
  494. }
  495. },
  496. /* Function to cleanup the dialog, and select. Also calls the
  497. done function with no permutation (to indicate that the
  498. columnChooser was aborted */
  499. cleanup: function (calldone) {
  500. call(opts.dlog, selector, "destroy");
  501. call(opts.msel, select, "destroy");
  502. selector.remove();
  503. if (calldone && opts.done) {
  504. opts.done.call($self);
  505. }
  506. },
  507. msel_opts: {}
  508. },
  509. $self.jqGrid("getGridRes", "col"),
  510. jgrid.col, opts || {});
  511. if ($.ui) {
  512. if ($UiMultiselect && $UiMultiselect.defaults) {
  513. if (!jgrid._multiselect) {
  514. // should be in language file
  515. (jgrid.defaults != null && $.isFunction(jgrid.defaults.fatalError) ? jgrid.defaults.fatalError : alert)("Multiselect plugin loaded after jqGrid. Please load the plugin before the jqGrid!");
  516. return;
  517. }
  518. // ??? the next line uses $.ui.multiselect.defaults which will be typically undefined
  519. opts.msel_opts = $.extend($UiMultiselect.defaults, opts.msel_opts);
  520. }
  521. }
  522. if (opts.caption) {
  523. selector.attr("title", opts.caption);
  524. }
  525. if (opts.classname) {
  526. selector.addClass(opts.classname);
  527. select.addClass(opts.classname);
  528. }
  529. if (opts.width) {
  530. $(">div", selector).css({ width: opts.width, margin: "0 auto" });
  531. select.css("width", opts.width);
  532. }
  533. if (opts.height) {
  534. $(">div", selector).css("height", opts.height);
  535. select.css("height", opts.height - 10);
  536. }
  537. select.empty();
  538. var gh = p.groupHeader != null ? p.groupHeader.groupHeaders : 0,
  539. colHeader = {}, k, j, iCol, ghItem;
  540. // fill colHeader for columns which have column header
  541. if (gh) {
  542. for (k = 0; k < gh.length; k++) {
  543. ghItem = gh[k];
  544. for (j = 0; j < ghItem.numberOfColumns; j++) {
  545. iCol = p.iColByName[ghItem.startColumnName] + j;
  546. colHeader[iCol] = $.isFunction(opts.buildItemText) ?
  547. opts.buildItemText.call($self[0], {
  548. iCol: iCol,
  549. cm: colModel[iCol],
  550. cmName: colModel[iCol].name,
  551. colName: colNames[iCol],
  552. groupTitleText: ghItem.titleText
  553. }) :
  554. $.jgrid.stripHtml(ghItem.titleText) + ": " +
  555. $.jgrid.stripHtml(colNames[iCol] === "" ? colModel[iCol].name : colNames[iCol]);
  556. }
  557. }
  558. }
  559. // fill colHeader for all other columns
  560. for (iCol = 0; iCol < nCol; iCol++) {
  561. if (colHeader[iCol] === undefined) {
  562. colHeader[iCol] = $.isFunction(opts.buildItemText) ?
  563. opts.buildItemText.call($self[0], {
  564. iCol: iCol,
  565. cm: colModel[iCol],
  566. cmName: colModel[iCol].name,
  567. colName: colNames[iCol],
  568. groupTitleText: null
  569. }) :
  570. $.jgrid.stripHtml(colNames[iCol]);
  571. }
  572. }
  573. $.each(colModel, function (i) {
  574. if (!this.hidedlg) {
  575. select.append("<option value='" + i + "'" +
  576. (p.headertitles || this.headerTitle ? (" title='" + jgrid.stripHtml(typeof this.headerTitle === "string" ? this.headerTitle : colHeader[i]) + "'") : "") +
  577. (this.hidden ? "" : " selected='selected'") + ">" + colHeader[i] + "</option>");
  578. }
  579. });
  580. dopts = $.isFunction(opts.dlog_opts) ? opts.dlog_opts.call($self, opts) : opts.dlog_opts;
  581. call(opts.dlog, selector, dopts);
  582. mopts = $.isFunction(opts.msel_opts) ? opts.msel_opts.call($self, opts) : opts.msel_opts;
  583. call(opts.msel, select, mopts);
  584. // fix height of elements of the multiselect widget
  585. $dialogContent = $("#colchooser_" + jqID(p.id));
  586. $dialogContent.css({ margin: "auto" });
  587. $dialogContent.find(">div").css({ width: "100%", height: "100%", margin: "auto" });
  588. multiselectData = getMultiselectWidgetData(select);
  589. if (multiselectData) {
  590. // grid property will be used to access the grid inside of _setSelected
  591. multiselectData.grid = self;
  592. if (gh) {
  593. // make deep copy of groupHeaders to be able to hold changes of startColumnName,
  594. // but to apply the changes only after the user click OK button (not Cancel)
  595. multiselectData.gh = $.extend(true, [], gh);
  596. // filling the helper array inGroup. It contains
  597. // an item for every column. The value is undefined if the column
  598. // not belongs to a header group and it is 0-based index of the
  599. // header group (the index in gh array) if the column belongs to
  600. // a header group. The array inGroup helps us to detect whether
  601. // two columns belong to the same group or not.
  602. multiselectData.inGroup = new Array(p.colModel.length); // allocate array with undefined values
  603. var iGrp, headerItem;
  604. for (iGrp = 0; iGrp < gh.length; iGrp++) {
  605. headerItem = gh[iGrp];
  606. for (iCol = 0; iCol < headerItem.numberOfColumns; iCol++) {
  607. multiselectData.inGroup[p.iColByName[headerItem.startColumnName] + iCol] = iGrp;
  608. }
  609. }
  610. }
  611. multiselectData.newColOrder = $.map(colModel, function (cm) { return cm.name; });
  612. multiselectData.container.css({ width: "100%", height: "100%", margin: "auto" });
  613. multiselectData.selectedContainer.css({ width: multiselectData.options.dividerLocation * 100 + "%", height: "100%", margin: "auto", boxSizing: "border-box" });
  614. multiselectData.availableContainer.css({ width: (100 - multiselectData.options.dividerLocation * 100) + "%", height: "100%", margin: "auto", boxSizing: "border-box" });
  615. // set height for both selectedList and availableList
  616. multiselectData.selectedList.css("height", "auto");
  617. multiselectData.availableList.css("height", "auto");
  618. listHeight = Math.max(multiselectData.selectedList.height(), multiselectData.availableList.height());
  619. listHeight = Math.min(listHeight, $(window).height());
  620. multiselectData.selectedList.css("height", listHeight);
  621. multiselectData.availableList.css("height", listHeight);
  622. if (multiselectData.options != null && multiselectData.options.sortable) {
  623. multiselectData.selectedList.on("sortupdate", function (e, ui) {
  624. // remove fixed inline style values of width and height
  625. // added during gragging
  626. reorderSelectedColumns.call(
  627. multiselectData,
  628. parseInt(ui.item.data("optionLink").val(), 10)
  629. );
  630. ui.item.css({ width: "", height: "" });
  631. if ($.isFunction(opts.sortUpdate)) {
  632. opts.sortUpdate.call(self, e, ui);
  633. }
  634. });
  635. }
  636. if ($.isFunction(opts.init)) {
  637. opts.init.call(self, multiselectData);
  638. }
  639. }
  640. },
  641. sortableRows: function (opts) {
  642. // Can accept all sortable options and events
  643. return this.each(function () {
  644. var $t = this, grid = $t.grid, p = $t.p;
  645. if (!grid) { return; }
  646. // Currently we disable a treeGrid sortable
  647. if (p.treeGrid) { return; }
  648. if ($.fn.sortable) {
  649. opts = $.extend({
  650. cursor: "move",
  651. axis: "y",
  652. items: ">tbody>.jqgrow"
  653. },
  654. opts || {});
  655. if (opts.start && $.isFunction(opts.start)) {
  656. opts._start_ = opts.start;
  657. delete opts.start;
  658. } else { opts._start_ = false; }
  659. if (opts.update && $.isFunction(opts.update)) {
  660. opts._update_ = opts.update;
  661. delete opts.update;
  662. } else { opts._update_ = false; }
  663. opts.start = function (ev, ui) {
  664. $(ui.item).css("border-width", "0");
  665. $("td", ui.item).each(function (i) {
  666. this.style.width = grid.cols[i].style.width;
  667. });
  668. if (p.subGrid) {
  669. var subgid = $(ui.item).attr("id");
  670. try {
  671. $($t).jqGrid("collapseSubGridRow", subgid);
  672. } catch (ignore) { }
  673. }
  674. if (opts._start_) {
  675. opts._start_.apply(this, [ev, ui]);
  676. }
  677. };
  678. opts.update = function (ev, ui) {
  679. $(ui.item).css("border-width", "");
  680. if (p.rownumbers === true) {
  681. $("td.jqgrid-rownum", $t.rows).each(function (i) {
  682. $(this).html(i + 1 + (parseInt(p.page, 10) - 1) * parseInt(p.rowNum, 10));
  683. });
  684. }
  685. if (opts._update_) {
  686. opts._update_.apply(this, [ev, ui]);
  687. }
  688. };
  689. $($t).sortable(opts);
  690. /*if ($.isFunction($.fn.disableSelection)) {
  691. // The method disableSelection exists starting with jQuery UI 1.6,
  692. // but it's declared as deprecated since jQuery UI 1.9
  693. // see http://jqueryui.com/upgrade-guide/1.9/#deprecated-disableselection-and-enableselection
  694. // so we use disableSelection only if it exists
  695. var jQueryUiVersion = $.ui != null && typeof $.ui.version === "string" ?
  696. $.ui.version.match(/(\d+)\.(\d+).(\d+)/) : [];
  697. // jQuery UI version is: jQueryUiVersion[1].jQueryUiVersion[2].jQueryUiVersion[3]
  698. if (jQueryUiVersion.length === 4 && jQueryUiVersion[1] === "1" &&
  699. jQueryUiVersion[2] > 5 && jQueryUiVersion[2] < 9) {
  700. // disable selection only for old jQuery UI
  701. $($t.tBodies[0]).children("tr.jqgrow").disableSelection();
  702. }
  703. }*/
  704. }
  705. });
  706. },
  707. gridDnD: function (opts) {
  708. return this.each(function () {
  709. var $t = this, j, cn;
  710. if (!$t.grid) { return; }
  711. // Currently we disable a treeGrid drag and drop
  712. if ($t.p.treeGrid) { return; }
  713. if (!$.fn.draggable || !$.fn.droppable) { return; }
  714. function updateDnD() {
  715. var datadnd = $.data($t, "dnd");
  716. $("tr.jqgrow:not(.ui-draggable)", $t).draggable($.isFunction(datadnd.drag) ? datadnd.drag.call($($t), datadnd) : datadnd.drag);
  717. }
  718. var appender = "<table id='jqgrid_dnd' class='ui-jqgrid-dnd'></table>";
  719. if ($("#jqgrid_dnd")[0] === undefined) {
  720. $("body").append(appender);
  721. }
  722. if (typeof opts === "string" && opts === "updateDnD" && $t.p.jqgdnd === true) {
  723. updateDnD();
  724. return;
  725. }
  726. opts = $.extend({
  727. drag: function (opts1) {
  728. return $.extend({
  729. start: function (ev, ui) {
  730. var i, subgid;
  731. // if we are in subgrid mode try to collapse the node
  732. if ($t.p.subGrid) {
  733. subgid = $(ui.helper).attr("id");
  734. try {
  735. $($t).jqGrid("collapseSubGridRow", subgid);
  736. } catch (ignore) { }
  737. }
  738. // hack
  739. // drag and drop does not insert tr in table, when the table has no rows
  740. // we try to insert new empty row on the target(s)
  741. for (i = 0; i < $.data($t, "dnd").connectWith.length; i++) {
  742. if ($($.data($t, "dnd").connectWith[i]).jqGrid("getGridParam", "reccount") === 0) {
  743. $($.data($t, "dnd").connectWith[i]).jqGrid("addRowData", "jqg_empty_row", {});
  744. }
  745. }
  746. ui.helper.addClass("ui-state-highlight");
  747. $("td", ui.helper).each(function (iCol) {
  748. this.style.width = $t.grid.headers[iCol].width + "px";
  749. });
  750. if (opts1.onstart && $.isFunction(opts1.onstart)) { opts1.onstart.call($($t), ev, ui); }
  751. },
  752. stop: function (ev, ui) {
  753. var i, ids;
  754. if (ui.helper.dropped && !opts1.dragcopy) {
  755. ids = $(ui.helper).attr("id");
  756. if (ids === undefined) { ids = $(this).attr("id"); }
  757. $($t).jqGrid("delRowData", ids);
  758. }
  759. // if we have a empty row inserted from start event try to delete it
  760. for (i = 0; i < $.data($t, "dnd").connectWith.length; i++) {
  761. $($.data($t, "dnd").connectWith[i]).jqGrid("delRowData", "jqg_empty_row");
  762. }
  763. if (opts1.onstop && $.isFunction(opts1.onstop)) { opts1.onstop.call($($t), ev, ui); }
  764. }
  765. }, opts1.drag_opts || {});
  766. },
  767. drop: function (opts1) {
  768. return $.extend({
  769. accept: function (d) {
  770. if (!$(d).hasClass("jqgrow")) { return d; }
  771. var tid = $(d).closest("table.ui-jqgrid-btable");
  772. if (tid.length > 0 && $.data(tid[0], "dnd") !== undefined) {
  773. var cn1 = $.data(tid[0], "dnd").connectWith;
  774. return $.inArray("#" + jqID(this.id), cn1) !== -1 ? true : false;
  775. }
  776. return false;
  777. },
  778. drop: function (ev, ui) {
  779. if (!$(ui.draggable).hasClass("jqgrow")) { return; }
  780. var rowid = $(ui.draggable).attr("id"),
  781. $srcGrid = ui.draggable.parent().parent(),
  782. getdata = $srcGrid.jqGrid("getRowData", rowid);
  783. if (!opts1.dropbyname) {
  784. var tmpdata = {}, iSrc, iDest, srcName, destName,
  785. srcColModel = $srcGrid.jqGrid("getGridParam", "colModel"),
  786. destColModel = $("#" + jqID(this.id)).jqGrid("getGridParam", "colModel");
  787. try {
  788. for (iSrc = 0, iDest = 0; iSrc < srcColModel.length && iDest < destColModel.length; iSrc++) {
  789. srcName = srcColModel[iSrc].name;
  790. if (!(srcName === "cb" || srcName === "rn" || srcName === "subgrid")) {
  791. // src column found, which need be copied
  792. for (; iDest < destColModel.length; iDest++) {
  793. destName = destColModel[iDest].name;
  794. if (!(destName === "cb" || destName === "rn" || destName === "subgrid")) {
  795. tmpdata[destName] = getdata[srcName];
  796. break;
  797. }
  798. }
  799. iDest++;
  800. }
  801. }
  802. getdata = tmpdata;
  803. } catch (ignore) { }
  804. }
  805. ui.helper.dropped = true;
  806. if (opts1.beforedrop && $.isFunction(opts1.beforedrop)) {
  807. //parameters to this callback - event, element, data to be inserted, sender, reciever
  808. // should return object which will be inserted into the reciever
  809. var datatoinsert = opts1.beforedrop.call(this, ev, ui, getdata, $("#" + jqID($t.p.id)), $(this));
  810. if (datatoinsert !== undefined && datatoinsert !== null && typeof datatoinsert === "object") { getdata = datatoinsert; }
  811. }
  812. if (ui.helper.dropped) {
  813. var grid;
  814. if (opts1.autoid) {
  815. if ($.isFunction(opts1.autoid)) {
  816. grid = opts1.autoid.call(this, getdata, {
  817. rowid: rowid,
  818. ev: ev,
  819. ui: ui
  820. });
  821. } else {
  822. grid = Math.ceil(Math.random() * 1000);
  823. grid = opts1.autoidprefix + grid;
  824. }
  825. }
  826. // NULL is interpreted as undefined while null as object
  827. $("#" + jqID(this.id)).jqGrid("addRowData", grid, getdata, opts1.droppos);
  828. getdata[$t.p.localReader.id] = grid;
  829. }
  830. if (opts1.ondrop && $.isFunction(opts1.ondrop)) { opts1.ondrop.call(this, ev, ui, getdata); }
  831. }
  832. }, opts1.drop_opts || {});
  833. },
  834. onstart: null,
  835. onstop: null,
  836. beforedrop: null,
  837. ondrop: null,
  838. drop_opts: {
  839. //activeClass: "ui-state-active",
  840. //hoverClass: "ui-state-hover"
  841. },
  842. drag_opts: {
  843. revert: "invalid",
  844. helper: "clone",
  845. cursor: "move",
  846. appendTo: "#jqgrid_dnd",
  847. zIndex: 5000
  848. },
  849. dragcopy: false,
  850. dropbyname: false,
  851. droppos: "first",
  852. autoid: true,
  853. autoidprefix: "dnd_"
  854. }, opts || {});
  855. if (!opts.connectWith) { return; }
  856. opts.connectWith = opts.connectWith.split(",");
  857. opts.connectWith = $.map(opts.connectWith, function (n) { return $.trim(n); });
  858. $.data($t, "dnd", opts);
  859. if ($t.p.reccount !== 0 && !$t.p.jqgdnd) {
  860. updateDnD();
  861. }
  862. $t.p.jqgdnd = true;
  863. for (j = 0; j < opts.connectWith.length; j++) {
  864. cn = opts.connectWith[j];
  865. $(cn).droppable($.isFunction(opts.drop) ? opts.drop.call($($t), opts) : opts.drop);
  866. }
  867. });
  868. },
  869. gridResize: function (opts) {
  870. return this.each(function () {
  871. var $t = this, grid = $t.grid, p = $t.p, bdivSelector = p.gView + ">.ui-jqgrid-bdiv", onlyHorizontal = false, sel, gridHeight = p.height;
  872. if (!grid || !$.fn.resizable) { return; }
  873. opts = $.extend({}, opts || {});
  874. if (opts.alsoResize) {
  875. opts._alsoResize_ = opts.alsoResize;
  876. delete opts.alsoResize;
  877. } else {
  878. opts._alsoResize_ = false;
  879. }
  880. if (opts.stop && $.isFunction(opts.stop)) {
  881. opts._stop_ = opts.stop;
  882. delete opts.stop;
  883. } else {
  884. opts._stop_ = false;
  885. }
  886. opts.stop = function (ev, ui) {
  887. $($t).jqGrid("setGridWidth", ui.size.width, opts.shrinkToFit);
  888. $(p.gView + ">.ui-jqgrid-titlebar").css("width", "");
  889. if (!onlyHorizontal) {
  890. $($t).jqGrid("setGridParam", { height: $(bdivSelector).height() });
  891. } else {
  892. $(sel).each(function () {
  893. $(this).css("height", "");
  894. });
  895. if (gridHeight === "auto" || gridHeight === "100%") {
  896. $(grid.bDiv).css("height", gridHeight);
  897. }
  898. }
  899. if ($t.fixScrollOffsetAndhBoxPadding) {
  900. $t.fixScrollOffsetAndhBoxPadding();
  901. }
  902. if (opts._stop_) { opts._stop_.call($t, ev, ui); }
  903. };
  904. sel = bdivSelector;
  905. if ((gridHeight === "auto" || gridHeight === "100%") && opts.handles === undefined) {
  906. opts.handles = "e,w";
  907. }
  908. if (opts.handles) {
  909. // test for "e, w"
  910. var ar = $.map(String(opts.handles).split(","), function (item) {
  911. return $.trim(item);
  912. });
  913. if (ar.length === 2 && ((ar[0] === "e" && ar[1] === "w") || (ar[1] === "e" && ar[1] === "w"))) {
  914. sel = p.gView + ">div:not(.frozen-div)";
  915. onlyHorizontal = true;
  916. if (p.pager) {
  917. sel += "," + p.pager;
  918. }
  919. }
  920. }
  921. if (opts._alsoResize_) {
  922. opts.alsoResize = sel + "," + opts._alsoResize_;
  923. } else {
  924. opts.alsoResize = sel;
  925. }
  926. delete opts._alsoResize_;
  927. $(p.gBox).resizable(opts);
  928. });
  929. }
  930. });
  931. // end module grid.jqueryui
  932. }));