jsgrid.js 72 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454
  1. /*
  2. * jsGrid v1.4.1 (http://js-grid.com)
  3. * (c) 2016 Artem Tabalin
  4. * Licensed under MIT (https://github.com/tabalinas/jsgrid/blob/master/LICENSE)
  5. */
  6. (function(window, $, undefined) {
  7. var JSGRID = "JSGrid",
  8. JSGRID_DATA_KEY = JSGRID,
  9. JSGRID_ROW_DATA_KEY = "JSGridItem",
  10. JSGRID_EDIT_ROW_DATA_KEY = "JSGridEditRow",
  11. SORT_ORDER_ASC = "asc",
  12. SORT_ORDER_DESC = "desc",
  13. FIRST_PAGE_PLACEHOLDER = "{first}",
  14. PAGES_PLACEHOLDER = "{pages}",
  15. PREV_PAGE_PLACEHOLDER = "{prev}",
  16. NEXT_PAGE_PLACEHOLDER = "{next}",
  17. LAST_PAGE_PLACEHOLDER = "{last}",
  18. PAGE_INDEX_PLACEHOLDER = "{pageIndex}",
  19. PAGE_COUNT_PLACEHOLDER = "{pageCount}",
  20. ITEM_COUNT_PLACEHOLDER = "{itemCount}",
  21. EMPTY_HREF = "javascript:void(0);";
  22. var getOrApply = function(value, context) {
  23. if($.isFunction(value)) {
  24. return value.apply(context, $.makeArray(arguments).slice(2));
  25. }
  26. return value;
  27. };
  28. var defaultController = {
  29. loadData: $.noop,
  30. insertItem: $.noop,
  31. updateItem: $.noop,
  32. deleteItem: $.noop
  33. };
  34. function Grid(element, config) {
  35. var $element = $(element);
  36. $element.data(JSGRID_DATA_KEY, this);
  37. this._container = $element;
  38. this.data = [];
  39. this.fields = [];
  40. this._editingRow = null;
  41. this._sortField = null;
  42. this._sortOrder = SORT_ORDER_ASC;
  43. this._firstDisplayingPage = 1;
  44. this._init(config);
  45. this.render();
  46. }
  47. Grid.prototype = {
  48. width: "auto",
  49. height: "auto",
  50. updateOnResize: true,
  51. rowClass: $.noop,
  52. rowRenderer: null,
  53. rowClick: function(args) {
  54. if(this.editing) {
  55. this.editItem($(args.event.target).closest("tr"));
  56. }
  57. },
  58. rowDoubleClick: $.noop,
  59. noDataContent: "Not found",
  60. noDataRowClass: "jsgrid-nodata-row",
  61. heading: true,
  62. headerRowRenderer: null,
  63. headerRowClass: "jsgrid-header-row",
  64. filtering: false,
  65. filterRowRenderer: null,
  66. filterRowClass: "jsgrid-filter-row",
  67. inserting: false,
  68. insertRowRenderer: null,
  69. insertRowClass: "jsgrid-insert-row",
  70. editing: false,
  71. editRowRenderer: null,
  72. editRowClass: "jsgrid-edit-row",
  73. confirmDeleting: true,
  74. deleteConfirm: "Are you sure?",
  75. selecting: true,
  76. selectedRowClass: "jsgrid-selected-row",
  77. oddRowClass: "jsgrid-row",
  78. evenRowClass: "jsgrid-alt-row",
  79. sorting: false,
  80. sortableClass: "jsgrid-header-sortable",
  81. sortAscClass: "jsgrid-header-sort jsgrid-header-sort-asc",
  82. sortDescClass: "jsgrid-header-sort jsgrid-header-sort-desc",
  83. paging: false,
  84. pagerContainer: null,
  85. pageIndex: 1,
  86. pageSize: 20,
  87. pageButtonCount: 15,
  88. pagerFormat: "Pages: {first} {prev} {pages} {next} {last}    {pageIndex} of {pageCount}",
  89. pagePrevText: "Prev",
  90. pageNextText: "Next",
  91. pageFirstText: "First",
  92. pageLastText: "Last",
  93. pageNavigatorNextText: "...",
  94. pageNavigatorPrevText: "...",
  95. pagerContainerClass: "jsgrid-pager-container",
  96. pagerClass: "jsgrid-pager",
  97. pagerNavButtonClass: "jsgrid-pager-nav-button",
  98. pagerNavButtonInactiveClass: "jsgrid-pager-nav-inactive-button",
  99. pageClass: "jsgrid-pager-page",
  100. currentPageClass: "jsgrid-pager-current-page",
  101. customLoading: false,
  102. pageLoading: false,
  103. autoload: false,
  104. controller: defaultController,
  105. loadIndication: true,
  106. loadIndicationDelay: 500,
  107. loadMessage: "Please, wait...",
  108. loadShading: true,
  109. invalidMessage: "Invalid data entered!",
  110. invalidNotify: function(args) {
  111. var messages = $.map(args.errors, function(error) {
  112. return error.message || null;
  113. });
  114. window.alert([this.invalidMessage].concat(messages).join("\n"));
  115. },
  116. onRefreshing: $.noop,
  117. onRefreshed: $.noop,
  118. onItemDeleting: $.noop,
  119. onItemDeleted: $.noop,
  120. onItemInserting: $.noop,
  121. onItemInserted: $.noop,
  122. onItemEditing: $.noop,
  123. onItemUpdating: $.noop,
  124. onItemUpdated: $.noop,
  125. onItemInvalid: $.noop,
  126. onDataLoading: $.noop,
  127. onDataLoaded: $.noop,
  128. onOptionChanging: $.noop,
  129. onOptionChanged: $.noop,
  130. onError: $.noop,
  131. invalidClass: "jsgrid-invalid",
  132. containerClass: "jsgrid",
  133. tableClass: "jsgrid-table",
  134. gridHeaderClass: "jsgrid-grid-header",
  135. gridBodyClass: "jsgrid-grid-body",
  136. _init: function(config) {
  137. $.extend(this, config);
  138. this._initLoadStrategy();
  139. this._initController();
  140. this._initFields();
  141. this._attachWindowLoadResize();
  142. this._attachWindowResizeCallback();
  143. },
  144. loadStrategy: function() {
  145. return this.pageLoading
  146. ? new jsGrid.loadStrategies.PageLoadingStrategy(this)
  147. : new jsGrid.loadStrategies.DirectLoadingStrategy(this);
  148. },
  149. _initLoadStrategy: function() {
  150. this._loadStrategy = getOrApply(this.loadStrategy, this);
  151. },
  152. _initController: function() {
  153. this._controller = $.extend({}, defaultController, getOrApply(this.controller, this));
  154. },
  155. loadIndicator: function(config) {
  156. return new jsGrid.LoadIndicator(config);
  157. },
  158. validation: function(config) {
  159. return jsGrid.Validation && new jsGrid.Validation(config);
  160. },
  161. _initFields: function() {
  162. var self = this;
  163. self.fields = $.map(self.fields, function(field) {
  164. if($.isPlainObject(field)) {
  165. var fieldConstructor = (field.type && jsGrid.fields[field.type]) || jsGrid.Field;
  166. field = new fieldConstructor(field);
  167. }
  168. field._grid = self;
  169. return field;
  170. });
  171. },
  172. _attachWindowLoadResize: function() {
  173. $(window).on("load", $.proxy(this._refreshSize, this));
  174. },
  175. _attachWindowResizeCallback: function() {
  176. if(this.updateOnResize) {
  177. $(window).on("resize", $.proxy(this._refreshSize, this));
  178. }
  179. },
  180. _detachWindowResizeCallback: function() {
  181. $(window).off("resize", this._refreshSize);
  182. },
  183. option: function(key, value) {
  184. var optionChangingEventArgs,
  185. optionChangedEventArgs;
  186. if(arguments.length === 1)
  187. return this[key];
  188. optionChangingEventArgs = {
  189. option: key,
  190. oldValue: this[key],
  191. newValue: value
  192. };
  193. this._callEventHandler(this.onOptionChanging, optionChangingEventArgs);
  194. this._handleOptionChange(optionChangingEventArgs.option, optionChangingEventArgs.newValue);
  195. optionChangedEventArgs = {
  196. option: optionChangingEventArgs.option,
  197. value: optionChangingEventArgs.newValue
  198. };
  199. this._callEventHandler(this.onOptionChanged, optionChangedEventArgs);
  200. },
  201. fieldOption: function(field, key, value) {
  202. field = this._normalizeField(field);
  203. if(arguments.length === 2)
  204. return field[key];
  205. field[key] = value;
  206. this._renderGrid();
  207. },
  208. _handleOptionChange: function(name, value) {
  209. this[name] = value;
  210. switch(name) {
  211. case "width":
  212. case "height":
  213. this._refreshSize();
  214. break;
  215. case "rowClass":
  216. case "rowRenderer":
  217. case "rowClick":
  218. case "rowDoubleClick":
  219. case "noDataText":
  220. case "noDataRowClass":
  221. case "noDataContent":
  222. case "selecting":
  223. case "selectedRowClass":
  224. case "oddRowClass":
  225. case "evenRowClass":
  226. this._refreshContent();
  227. break;
  228. case "pageButtonCount":
  229. case "pagerFormat":
  230. case "pagePrevText":
  231. case "pageNextText":
  232. case "pageFirstText":
  233. case "pageLastText":
  234. case "pageNavigatorNextText":
  235. case "pageNavigatorPrevText":
  236. case "pagerClass":
  237. case "pagerNavButtonClass":
  238. case "pageClass":
  239. case "currentPageClass":
  240. case "pagerRenderer":
  241. this._refreshPager();
  242. break;
  243. case "fields":
  244. this._initFields();
  245. this.render();
  246. break;
  247. case "data":
  248. case "editing":
  249. case "heading":
  250. case "filtering":
  251. case "inserting":
  252. case "paging":
  253. this.refresh();
  254. break;
  255. case "loadStrategy":
  256. case "pageLoading":
  257. this._initLoadStrategy();
  258. this.search();
  259. break;
  260. case "pageIndex":
  261. this.openPage(value);
  262. break;
  263. case "pageSize":
  264. this.refresh();
  265. this.search();
  266. break;
  267. case "editRowRenderer":
  268. case "editRowClass":
  269. this.cancelEdit();
  270. break;
  271. case "updateOnResize":
  272. this._detachWindowResizeCallback();
  273. this._attachWindowResizeCallback();
  274. break;
  275. case "invalidNotify":
  276. case "invalidMessage":
  277. break;
  278. default:
  279. this.render();
  280. break;
  281. }
  282. },
  283. destroy: function() {
  284. this._detachWindowResizeCallback();
  285. this._clear();
  286. this._container.removeData(JSGRID_DATA_KEY);
  287. },
  288. render: function() {
  289. this._renderGrid();
  290. return this.autoload ? this.loadData() : $.Deferred().resolve().promise();
  291. },
  292. _renderGrid: function() {
  293. this._clear();
  294. this._container.addClass(this.containerClass)
  295. .css("position", "relative")
  296. .append(this._createHeader())
  297. .append(this._createBody());
  298. this._pagerContainer = this._createPagerContainer();
  299. this._loadIndicator = this._createLoadIndicator();
  300. this._validation = this._createValidation();
  301. this.refresh();
  302. },
  303. _createLoadIndicator: function() {
  304. return getOrApply(this.loadIndicator, this, {
  305. message: this.loadMessage,
  306. shading: this.loadShading,
  307. container: this._container
  308. });
  309. },
  310. _createValidation: function() {
  311. return getOrApply(this.validation, this);
  312. },
  313. _clear: function() {
  314. this.cancelEdit();
  315. clearTimeout(this._loadingTimer);
  316. this._pagerContainer && this._pagerContainer.empty();
  317. this._container.empty()
  318. .css({ position: "", width: "", height: "" });
  319. },
  320. _createHeader: function() {
  321. var $headerRow = this._headerRow = this._createHeaderRow(),
  322. $filterRow = this._filterRow = this._createFilterRow(),
  323. $insertRow = this._insertRow = this._createInsertRow();
  324. var $headerGrid = this._headerGrid = $("<table>").addClass(this.tableClass)
  325. .append($headerRow)
  326. .append($filterRow)
  327. .append($insertRow);
  328. var $header = this._header = $("<div>").addClass(this.gridHeaderClass)
  329. .addClass(this._scrollBarWidth() ? "jsgrid-header-scrollbar" : "")
  330. .append($headerGrid);
  331. return $header;
  332. },
  333. _createBody: function() {
  334. var $content = this._content = $("<tbody>");
  335. var $bodyGrid = this._bodyGrid = $("<table>").addClass(this.tableClass)
  336. .append($content);
  337. var $body = this._body = $("<div>").addClass(this.gridBodyClass)
  338. .append($bodyGrid)
  339. .on("scroll", $.proxy(function(e) {
  340. this._header.scrollLeft(e.target.scrollLeft);
  341. }, this));
  342. return $body;
  343. },
  344. _createPagerContainer: function() {
  345. var pagerContainer = this.pagerContainer || $("<div>").appendTo(this._container);
  346. return $(pagerContainer).addClass(this.pagerContainerClass);
  347. },
  348. _eachField: function(callBack) {
  349. var self = this;
  350. $.each(this.fields, function(index, field) {
  351. if(field.visible) {
  352. callBack.call(self, field, index);
  353. }
  354. });
  355. },
  356. _createHeaderRow: function() {
  357. if($.isFunction(this.headerRowRenderer))
  358. return $(this.headerRowRenderer());
  359. var $result = $("<tr>").addClass(this.headerRowClass);
  360. this._eachField(function(field, index) {
  361. var $th = this._prepareCell("<th>", field, "headercss")
  362. .append(field.headerTemplate ? field.headerTemplate() : "")
  363. .appendTo($result);
  364. if(this.sorting && field.sorting) {
  365. $th.addClass(this.sortableClass)
  366. .on("click", $.proxy(function() {
  367. this.sort(index);
  368. }, this));
  369. }
  370. });
  371. return $result;
  372. },
  373. _prepareCell: function(cell, field, cssprop) {
  374. return $(cell).css("width", field.width)
  375. .addClass((cssprop && field[cssprop]) || field.css)
  376. .addClass(field.align ? ("jsgrid-align-" + field.align) : "");
  377. },
  378. _createFilterRow: function() {
  379. if($.isFunction(this.filterRowRenderer))
  380. return $(this.filterRowRenderer());
  381. var $result = $("<tr>").addClass(this.filterRowClass);
  382. this._eachField(function(field) {
  383. this._prepareCell("<td>", field, "filtercss")
  384. .append(field.filterTemplate ? field.filterTemplate() : "")
  385. .appendTo($result);
  386. });
  387. return $result;
  388. },
  389. _createInsertRow: function() {
  390. if($.isFunction(this.insertRowRenderer))
  391. return $(this.insertRowRenderer());
  392. var $result = $("<tr>").addClass(this.insertRowClass);
  393. this._eachField(function(field) {
  394. this._prepareCell("<td>", field, "insertcss")
  395. .append(field.insertTemplate ? field.insertTemplate() : "")
  396. .appendTo($result);
  397. });
  398. return $result;
  399. },
  400. _callEventHandler: function(handler, eventParams) {
  401. handler.call(this, $.extend(eventParams, {
  402. grid: this
  403. }));
  404. return eventParams;
  405. },
  406. reset: function() {
  407. this._resetSorting();
  408. this._resetPager();
  409. this.refresh();
  410. },
  411. _resetPager: function() {
  412. this._firstDisplayingPage = 1;
  413. this._setPage(1);
  414. },
  415. _resetSorting: function() {
  416. this._sortField = null;
  417. this._sortOrder = SORT_ORDER_ASC;
  418. this._clearSortingCss();
  419. },
  420. refresh: function() {
  421. this._callEventHandler(this.onRefreshing);
  422. this.cancelEdit();
  423. this._refreshHeading();
  424. this._refreshFiltering();
  425. this._refreshInserting();
  426. this._refreshContent();
  427. this._refreshPager();
  428. this._refreshSize();
  429. this._callEventHandler(this.onRefreshed);
  430. },
  431. _refreshHeading: function() {
  432. this._headerRow.toggle(this.heading);
  433. },
  434. _refreshFiltering: function() {
  435. this._filterRow.toggle(this.filtering);
  436. },
  437. _refreshInserting: function() {
  438. this._insertRow.toggle(this.inserting);
  439. },
  440. _refreshContent: function() {
  441. var $content = this._content;
  442. $content.empty();
  443. if(!this.data.length) {
  444. $content.append(this._createNoDataRow());
  445. return this;
  446. }
  447. var indexFrom = this._loadStrategy.firstDisplayIndex();
  448. var indexTo = this._loadStrategy.lastDisplayIndex();
  449. for(var itemIndex = indexFrom; itemIndex < indexTo; itemIndex++) {
  450. var item = this.data[itemIndex];
  451. $content.append(this._createRow(item, itemIndex));
  452. }
  453. },
  454. _createNoDataRow: function() {
  455. var noDataContent = getOrApply(this.noDataContent, this);
  456. var amountOfFields = 0;
  457. this._eachField(function() {
  458. amountOfFields++;
  459. });
  460. return $("<tr>").addClass(this.noDataRowClass)
  461. .append($("<td>").attr("colspan", amountOfFields).append(noDataContent));
  462. },
  463. _createNoDataContent: function() {
  464. return $.isFunction(this.noDataRenderer)
  465. ? this.noDataRenderer()
  466. : this.noDataText;
  467. },
  468. _createRow: function(item, itemIndex) {
  469. var $result;
  470. if($.isFunction(this.rowRenderer)) {
  471. $result = $(this.rowRenderer(item, itemIndex));
  472. } else {
  473. $result = $("<tr>");
  474. this._renderCells($result, item);
  475. }
  476. $result.addClass(this._getRowClasses(item, itemIndex))
  477. .data(JSGRID_ROW_DATA_KEY, item)
  478. .on("click", $.proxy(function(e) {
  479. this.rowClick({
  480. item: item,
  481. itemIndex: itemIndex,
  482. event: e
  483. });
  484. }, this))
  485. .on("dblclick", $.proxy(function(e) {
  486. this.rowDoubleClick({
  487. item: item,
  488. itemIndex: itemIndex,
  489. event: e
  490. });
  491. }, this));
  492. if(this.selecting) {
  493. this._attachRowHover($result);
  494. }
  495. return $result;
  496. },
  497. _getRowClasses: function(item, itemIndex) {
  498. var classes = [];
  499. classes.push(((itemIndex + 1) % 2) ? this.oddRowClass : this.evenRowClass);
  500. classes.push(getOrApply(this.rowClass, this, item, itemIndex));
  501. return classes.join(" ");
  502. },
  503. _attachRowHover: function($row) {
  504. var selectedRowClass = this.selectedRowClass;
  505. $row.hover(function() {
  506. $(this).addClass(selectedRowClass);
  507. },
  508. function() {
  509. $(this).removeClass(selectedRowClass);
  510. }
  511. );
  512. },
  513. _renderCells: function($row, item) {
  514. this._eachField(function(field) {
  515. $row.append(this._createCell(item, field));
  516. });
  517. return this;
  518. },
  519. _createCell: function(item, field) {
  520. var $result;
  521. var fieldValue = this._getItemFieldValue(item, field);
  522. if($.isFunction(field.cellRenderer)) {
  523. $result = $(field.cellRenderer(fieldValue, item));
  524. } else {
  525. $result = $("<td>").append(field.itemTemplate ? field.itemTemplate(fieldValue, item) : fieldValue);
  526. }
  527. return this._prepareCell($result, field);
  528. },
  529. _getItemFieldValue: function(item, field) {
  530. var props = field.name.split('.');
  531. var result = item[props.shift()];
  532. while(result && props.length) {
  533. result = result[props.shift()];
  534. }
  535. return result;
  536. },
  537. _setItemFieldValue: function(item, field, value) {
  538. var props = field.name.split('.');
  539. var current = item;
  540. var prop = props[0];
  541. while(current && props.length) {
  542. item = current;
  543. prop = props.shift();
  544. current = item[prop];
  545. }
  546. if(!current) {
  547. while(props.length) {
  548. item = item[prop] = {};
  549. prop = props.shift();
  550. }
  551. }
  552. item[prop] = value;
  553. },
  554. sort: function(field, order) {
  555. if($.isPlainObject(field)) {
  556. order = field.order;
  557. field = field.field;
  558. }
  559. this._clearSortingCss();
  560. this._setSortingParams(field, order);
  561. this._setSortingCss();
  562. return this._loadStrategy.sort();
  563. },
  564. _clearSortingCss: function() {
  565. this._headerRow.find("th")
  566. .removeClass(this.sortAscClass)
  567. .removeClass(this.sortDescClass);
  568. },
  569. _setSortingParams: function(field, order) {
  570. field = this._normalizeField(field);
  571. order = order || ((this._sortField === field) ? this._reversedSortOrder(this._sortOrder) : SORT_ORDER_ASC);
  572. this._sortField = field;
  573. this._sortOrder = order;
  574. },
  575. _normalizeField: function(field) {
  576. if($.isNumeric(field)) {
  577. return this.fields[field];
  578. }
  579. if(typeof field === "string") {
  580. return $.grep(this.fields, function(f) {
  581. return f.name === field;
  582. })[0];
  583. }
  584. return field;
  585. },
  586. _reversedSortOrder: function(order) {
  587. return (order === SORT_ORDER_ASC ? SORT_ORDER_DESC : SORT_ORDER_ASC);
  588. },
  589. _setSortingCss: function() {
  590. var fieldIndex = $.inArray(this._sortField, $.grep(this.fields, function(f) { return f.visible; }));
  591. this._headerRow.find("th").eq(fieldIndex)
  592. .addClass(this._sortOrder === SORT_ORDER_ASC ? this.sortAscClass : this.sortDescClass);
  593. },
  594. _sortData: function() {
  595. var sortFactor = this._sortFactor(),
  596. sortField = this._sortField;
  597. if(sortField) {
  598. this.data.sort(function(item1, item2) {
  599. return sortFactor * sortField.sortingFunc(item1[sortField.name], item2[sortField.name]);
  600. });
  601. }
  602. },
  603. _sortFactor: function() {
  604. return this._sortOrder === SORT_ORDER_ASC ? 1 : -1;
  605. },
  606. _itemsCount: function() {
  607. return this._loadStrategy.itemsCount();
  608. },
  609. _pagesCount: function() {
  610. var itemsCount = this._itemsCount(),
  611. pageSize = this.pageSize;
  612. return Math.floor(itemsCount / pageSize) + (itemsCount % pageSize ? 1 : 0);
  613. },
  614. _refreshPager: function() {
  615. var $pagerContainer = this._pagerContainer;
  616. $pagerContainer.empty();
  617. if(this.paging) {
  618. $pagerContainer.append(this._createPager());
  619. }
  620. var showPager = this.paging && this._pagesCount() > 1;
  621. $pagerContainer.toggle(showPager);
  622. },
  623. _createPager: function() {
  624. var $result;
  625. if($.isFunction(this.pagerRenderer)) {
  626. $result = $(this.pagerRenderer({
  627. pageIndex: this.pageIndex,
  628. pageCount: this._pagesCount()
  629. }));
  630. } else {
  631. $result = $("<div>").append(this._createPagerByFormat());
  632. }
  633. $result.addClass(this.pagerClass);
  634. return $result;
  635. },
  636. _createPagerByFormat: function() {
  637. var pageIndex = this.pageIndex,
  638. pageCount = this._pagesCount(),
  639. itemCount = this._itemsCount(),
  640. pagerParts = this.pagerFormat.split(" ");
  641. return $.map(pagerParts, $.proxy(function(pagerPart) {
  642. var result = pagerPart;
  643. if(pagerPart === PAGES_PLACEHOLDER) {
  644. result = this._createPages();
  645. } else if(pagerPart === FIRST_PAGE_PLACEHOLDER) {
  646. result = this._createPagerNavButton(this.pageFirstText, 1, pageIndex > 1);
  647. } else if(pagerPart === PREV_PAGE_PLACEHOLDER) {
  648. result = this._createPagerNavButton(this.pagePrevText, pageIndex - 1, pageIndex > 1);
  649. } else if(pagerPart === NEXT_PAGE_PLACEHOLDER) {
  650. result = this._createPagerNavButton(this.pageNextText, pageIndex + 1, pageIndex < pageCount);
  651. } else if(pagerPart === LAST_PAGE_PLACEHOLDER) {
  652. result = this._createPagerNavButton(this.pageLastText, pageCount, pageIndex < pageCount);
  653. } else if(pagerPart === PAGE_INDEX_PLACEHOLDER) {
  654. result = pageIndex;
  655. } else if(pagerPart === PAGE_COUNT_PLACEHOLDER) {
  656. result = pageCount;
  657. } else if(pagerPart === ITEM_COUNT_PLACEHOLDER) {
  658. result = itemCount;
  659. }
  660. return $.isArray(result) ? result.concat([" "]) : [result, " "];
  661. }, this));
  662. },
  663. _createPages: function() {
  664. var pageCount = this._pagesCount(),
  665. pageButtonCount = this.pageButtonCount,
  666. firstDisplayingPage = this._firstDisplayingPage,
  667. pages = [];
  668. if(firstDisplayingPage > 1) {
  669. pages.push(this._createPagerPageNavButton(this.pageNavigatorPrevText, this.showPrevPages));
  670. }
  671. for(var i = 0, pageNumber = firstDisplayingPage; i < pageButtonCount && pageNumber <= pageCount; i++, pageNumber++) {
  672. pages.push(pageNumber === this.pageIndex
  673. ? this._createPagerCurrentPage()
  674. : this._createPagerPage(pageNumber));
  675. }
  676. if((firstDisplayingPage + pageButtonCount - 1) < pageCount) {
  677. pages.push(this._createPagerPageNavButton(this.pageNavigatorNextText, this.showNextPages));
  678. }
  679. return pages;
  680. },
  681. _createPagerNavButton: function(text, pageIndex, isActive) {
  682. return this._createPagerButton(text, this.pagerNavButtonClass + (isActive ? "" : " " + this.pagerNavButtonInactiveClass),
  683. isActive ? function() { this.openPage(pageIndex); } : $.noop);
  684. },
  685. _createPagerPageNavButton: function(text, handler) {
  686. return this._createPagerButton(text, this.pagerNavButtonClass, handler);
  687. },
  688. _createPagerPage: function(pageIndex) {
  689. return this._createPagerButton(pageIndex, this.pageClass, function() {
  690. this.openPage(pageIndex);
  691. });
  692. },
  693. _createPagerButton: function(text, css, handler) {
  694. var $link = $("<a>").attr("href", EMPTY_HREF)
  695. .html(text)
  696. .on("click", $.proxy(handler, this));
  697. return $("<span>").addClass(css).append($link);
  698. },
  699. _createPagerCurrentPage: function() {
  700. return $("<span>")
  701. .addClass(this.pageClass)
  702. .addClass(this.currentPageClass)
  703. .text(this.pageIndex);
  704. },
  705. _refreshSize: function() {
  706. this._refreshHeight();
  707. this._refreshWidth();
  708. },
  709. _refreshWidth: function() {
  710. var $headerGrid = this._headerGrid,
  711. $bodyGrid = this._bodyGrid,
  712. width = this.width;
  713. if(width === "auto") {
  714. $headerGrid.width("auto");
  715. width = $headerGrid.outerWidth();
  716. }
  717. $headerGrid.width("");
  718. $bodyGrid.width("");
  719. this._container.width(width);
  720. width = $headerGrid.outerWidth();
  721. $bodyGrid.width(width);
  722. },
  723. _scrollBarWidth: (function() {
  724. var result;
  725. return function() {
  726. if(result === undefined) {
  727. var $ghostContainer = $("<div style='width:50px;height:50px;overflow:hidden;position:absolute;top:-10000px;left:-10000px;'></div>");
  728. var $ghostContent = $("<div style='height:100px;'></div>");
  729. $ghostContainer.append($ghostContent).appendTo("body");
  730. var width = $ghostContent.innerWidth();
  731. $ghostContainer.css("overflow-y", "auto");
  732. var widthExcludingScrollBar = $ghostContent.innerWidth();
  733. $ghostContainer.remove();
  734. result = width - widthExcludingScrollBar;
  735. }
  736. return result;
  737. };
  738. })(),
  739. _refreshHeight: function() {
  740. var container = this._container,
  741. pagerContainer = this._pagerContainer,
  742. height = this.height,
  743. nonBodyHeight;
  744. container.height(height);
  745. if(height !== "auto") {
  746. height = container.height();
  747. nonBodyHeight = this._header.outerHeight(true);
  748. if(pagerContainer.parents(container).length) {
  749. nonBodyHeight += pagerContainer.outerHeight(true);
  750. }
  751. this._body.outerHeight(height - nonBodyHeight);
  752. }
  753. },
  754. showPrevPages: function() {
  755. var firstDisplayingPage = this._firstDisplayingPage,
  756. pageButtonCount = this.pageButtonCount;
  757. this._firstDisplayingPage = (firstDisplayingPage > pageButtonCount) ? firstDisplayingPage - pageButtonCount : 1;
  758. this._refreshPager();
  759. },
  760. showNextPages: function() {
  761. var firstDisplayingPage = this._firstDisplayingPage,
  762. pageButtonCount = this.pageButtonCount,
  763. pageCount = this._pagesCount();
  764. this._firstDisplayingPage = (firstDisplayingPage + 2 * pageButtonCount > pageCount)
  765. ? pageCount - pageButtonCount + 1
  766. : firstDisplayingPage + pageButtonCount;
  767. this._refreshPager();
  768. },
  769. openPage: function(pageIndex) {
  770. if(pageIndex < 1 || pageIndex > this._pagesCount())
  771. return;
  772. this._setPage(pageIndex);
  773. this._loadStrategy.openPage(pageIndex);
  774. },
  775. _setPage: function(pageIndex) {
  776. var firstDisplayingPage = this._firstDisplayingPage,
  777. pageButtonCount = this.pageButtonCount;
  778. this.pageIndex = pageIndex;
  779. if(pageIndex < firstDisplayingPage) {
  780. this._firstDisplayingPage = pageIndex;
  781. }
  782. if(pageIndex > firstDisplayingPage + pageButtonCount - 1) {
  783. this._firstDisplayingPage = pageIndex - pageButtonCount + 1;
  784. }
  785. },
  786. _controllerCall: function(method, param, isCanceled, doneCallback) {
  787. if(isCanceled)
  788. return $.Deferred().reject().promise();
  789. this._showLoading();
  790. var controller = this._controller;
  791. if(!controller || !controller[method]) {
  792. throw Error("controller has no method '" + method + "'");
  793. }
  794. return $.when(controller[method](param))
  795. .done($.proxy(doneCallback, this))
  796. .fail($.proxy(this._errorHandler, this))
  797. .always($.proxy(this._hideLoading, this));
  798. },
  799. _errorHandler: function() {
  800. this._callEventHandler(this.onError, {
  801. args: $.makeArray(arguments)
  802. });
  803. },
  804. _showLoading: function() {
  805. if(!this.loadIndication)
  806. return;
  807. clearTimeout(this._loadingTimer);
  808. this._loadingTimer = setTimeout($.proxy(function() {
  809. this._loadIndicator.show();
  810. }, this), this.loadIndicationDelay);
  811. },
  812. _hideLoading: function() {
  813. if(!this.loadIndication)
  814. return;
  815. clearTimeout(this._loadingTimer);
  816. this._loadIndicator.hide();
  817. },
  818. search: function(filter) {
  819. this._resetSorting();
  820. this._resetPager();
  821. return this.loadData(filter);
  822. },
  823. loadData: function(filter) {
  824. filter = filter || (this.filtering ? this.getFilter() : {});
  825. $.extend(filter, this._loadStrategy.loadParams(), this._sortingParams());
  826. var args = this._callEventHandler(this.onDataLoading, {
  827. filter: filter
  828. });
  829. return this._controllerCall("loadData", filter, args.cancel, function(loadedData) {
  830. if(!loadedData)
  831. return;
  832. this._loadStrategy.finishLoad(loadedData);
  833. this._callEventHandler(this.onDataLoaded, {
  834. data: loadedData
  835. });
  836. });
  837. },
  838. getFilter: function() {
  839. var result = {};
  840. this._eachField(function(field) {
  841. if(field.filtering) {
  842. this._setItemFieldValue(result, field, field.filterValue());
  843. }
  844. });
  845. return result;
  846. },
  847. _sortingParams: function() {
  848. if(this.sorting && this._sortField) {
  849. return {
  850. sortField: this._sortField.name,
  851. sortOrder: this._sortOrder
  852. };
  853. }
  854. return {};
  855. },
  856. getSorting: function() {
  857. var sortingParams = this._sortingParams();
  858. return {
  859. field: sortingParams.sortField,
  860. order: sortingParams.sortOrder
  861. };
  862. },
  863. clearFilter: function() {
  864. var $filterRow = this._createFilterRow();
  865. this._filterRow.replaceWith($filterRow);
  866. this._filterRow = $filterRow;
  867. return this.search();
  868. },
  869. insertItem: function(item) {
  870. var insertingItem = item || this._getValidatedInsertItem();
  871. if(!insertingItem)
  872. return $.Deferred().reject().promise();
  873. var args = this._callEventHandler(this.onItemInserting, {
  874. item: insertingItem
  875. });
  876. return this._controllerCall("insertItem", insertingItem, args.cancel, function(insertedItem) {
  877. insertedItem = insertedItem || insertingItem;
  878. this._loadStrategy.finishInsert(insertedItem);
  879. this._callEventHandler(this.onItemInserted, {
  880. item: insertedItem
  881. });
  882. });
  883. },
  884. _getValidatedInsertItem: function() {
  885. var item = this._getInsertItem();
  886. return this._validateItem(item, this._insertRow) ? item : null;
  887. },
  888. _getInsertItem: function() {
  889. var result = {};
  890. this._eachField(function(field) {
  891. if(field.inserting) {
  892. this._setItemFieldValue(result, field, field.insertValue());
  893. }
  894. });
  895. return result;
  896. },
  897. _validateItem: function(item, $row) {
  898. var validationErrors = [];
  899. var args = {
  900. item: item,
  901. itemIndex: this._rowIndex($row),
  902. row: $row
  903. };
  904. this._eachField(function(field, index) {
  905. if(!field.validate)
  906. return;
  907. var errors = this._validation.validate($.extend({
  908. value: this._getItemFieldValue(item, field),
  909. rules: field.validate
  910. }, args));
  911. this._setCellValidity($row.children().eq(index), errors);
  912. if(!errors.length)
  913. return;
  914. validationErrors.push.apply(validationErrors,
  915. $.map(errors, function(message) {
  916. return { field: field, message: message };
  917. }));
  918. });
  919. if(!validationErrors.length)
  920. return true;
  921. var invalidArgs = $.extend({
  922. errors: validationErrors
  923. }, args);
  924. this._callEventHandler(this.onItemInvalid, invalidArgs);
  925. this.invalidNotify(invalidArgs);
  926. return false;
  927. },
  928. _setCellValidity: function($cell, errors) {
  929. $cell
  930. .toggleClass(this.invalidClass, !!errors.length)
  931. .attr("title", errors.join("\n"));
  932. },
  933. clearInsert: function() {
  934. var insertRow = this._createInsertRow();
  935. this._insertRow.replaceWith(insertRow);
  936. this._insertRow = insertRow;
  937. this.refresh();
  938. },
  939. editItem: function(item) {
  940. var $row = this.rowByItem(item);
  941. if($row.length) {
  942. this._editRow($row);
  943. }
  944. },
  945. rowByItem: function(item) {
  946. if(item.jquery || item.nodeType)
  947. return $(item);
  948. return this._content.find("tr").filter(function() {
  949. return $.data(this, JSGRID_ROW_DATA_KEY) === item;
  950. });
  951. },
  952. _editRow: function($row) {
  953. if(!this.editing)
  954. return;
  955. var item = $row.data(JSGRID_ROW_DATA_KEY);
  956. var args = this._callEventHandler(this.onItemEditing, {
  957. row: $row,
  958. item: item,
  959. itemIndex: this._itemIndex(item)
  960. });
  961. if(args.cancel)
  962. return;
  963. if(this._editingRow) {
  964. this.cancelEdit();
  965. }
  966. var $editRow = this._createEditRow(item);
  967. this._editingRow = $row;
  968. $row.hide();
  969. $editRow.insertBefore($row);
  970. $row.data(JSGRID_EDIT_ROW_DATA_KEY, $editRow);
  971. },
  972. _createEditRow: function(item) {
  973. if($.isFunction(this.editRowRenderer)) {
  974. return $(this.editRowRenderer(item, this._itemIndex(item)));
  975. }
  976. var $result = $("<tr>").addClass(this.editRowClass);
  977. this._eachField(function(field) {
  978. var fieldValue = this._getItemFieldValue(item, field);
  979. this._prepareCell("<td>", field, "editcss")
  980. .append(field.editTemplate ? field.editTemplate(fieldValue, item) : "")
  981. .appendTo($result);
  982. });
  983. return $result;
  984. },
  985. updateItem: function(item, editedItem) {
  986. if(arguments.length === 1) {
  987. editedItem = item;
  988. }
  989. var $row = item ? this.rowByItem(item) : this._editingRow;
  990. editedItem = editedItem || this._getValidatedEditedItem();
  991. if(!editedItem)
  992. return;
  993. return this._updateRow($row, editedItem);
  994. },
  995. _getValidatedEditedItem: function() {
  996. var item = this._getEditedItem();
  997. return this._validateItem(item, this._getEditRow()) ? item : null;
  998. },
  999. _updateRow: function($updatingRow, editedItem) {
  1000. var updatingItem = $updatingRow.data(JSGRID_ROW_DATA_KEY),
  1001. updatingItemIndex = this._itemIndex(updatingItem),
  1002. previousItem = $.extend(true, {}, updatingItem);
  1003. $.extend(true, updatingItem, editedItem);
  1004. var args = this._callEventHandler(this.onItemUpdating, {
  1005. row: $updatingRow,
  1006. item: updatingItem,
  1007. itemIndex: updatingItemIndex,
  1008. previousItem: previousItem
  1009. });
  1010. return this._controllerCall("updateItem", updatingItem, args.cancel, function(updatedItem) {
  1011. updatedItem = updatedItem || updatingItem;
  1012. var $updatedRow = this._finishUpdate($updatingRow, updatedItem, updatingItemIndex);
  1013. this._callEventHandler(this.onItemUpdated, {
  1014. row: $updatedRow,
  1015. item: updatedItem,
  1016. itemIndex: updatingItemIndex,
  1017. previousItem: previousItem
  1018. });
  1019. });
  1020. },
  1021. _rowIndex: function(row) {
  1022. return this._content.children().index($(row));
  1023. },
  1024. _itemIndex: function(item) {
  1025. return $.inArray(item, this.data);
  1026. },
  1027. _finishUpdate: function($updatingRow, updatedItem, updatedItemIndex) {
  1028. this.cancelEdit();
  1029. this.data[updatedItemIndex] = updatedItem;
  1030. var $updatedRow = this._createRow(updatedItem, updatedItemIndex);
  1031. $updatingRow.replaceWith($updatedRow);
  1032. return $updatedRow;
  1033. },
  1034. _getEditedItem: function() {
  1035. var result = {};
  1036. this._eachField(function(field) {
  1037. if(field.editing) {
  1038. this._setItemFieldValue(result, field, field.editValue());
  1039. }
  1040. });
  1041. return result;
  1042. },
  1043. cancelEdit: function() {
  1044. if(!this._editingRow)
  1045. return;
  1046. this._getEditRow().remove();
  1047. this._editingRow.show();
  1048. this._editingRow = null;
  1049. },
  1050. _getEditRow: function() {
  1051. return this._editingRow.data(JSGRID_EDIT_ROW_DATA_KEY);
  1052. },
  1053. deleteItem: function(item) {
  1054. var $row = this.rowByItem(item);
  1055. if(!$row.length)
  1056. return;
  1057. if(this.confirmDeleting && !window.confirm(getOrApply(this.deleteConfirm, this, $row.data(JSGRID_ROW_DATA_KEY))))
  1058. return;
  1059. return this._deleteRow($row);
  1060. },
  1061. _deleteRow: function($row) {
  1062. var deletingItem = $row.data(JSGRID_ROW_DATA_KEY),
  1063. deletingItemIndex = this._itemIndex(deletingItem);
  1064. var args = this._callEventHandler(this.onItemDeleting, {
  1065. row: $row,
  1066. item: deletingItem,
  1067. itemIndex: deletingItemIndex
  1068. });
  1069. return this._controllerCall("deleteItem", deletingItem, args.cancel, function() {
  1070. this._loadStrategy.finishDelete(deletingItem, deletingItemIndex);
  1071. this._callEventHandler(this.onItemDeleted, {
  1072. row: $row,
  1073. item: deletingItem,
  1074. itemIndex: deletingItemIndex
  1075. });
  1076. });
  1077. }
  1078. };
  1079. $.fn.jsGrid = function(config) {
  1080. var args = $.makeArray(arguments),
  1081. methodArgs = args.slice(1),
  1082. result = this;
  1083. this.each(function() {
  1084. var $element = $(this),
  1085. instance = $element.data(JSGRID_DATA_KEY),
  1086. methodResult;
  1087. if(instance) {
  1088. if(typeof config === "string") {
  1089. methodResult = instance[config].apply(instance, methodArgs);
  1090. if(methodResult !== undefined && methodResult !== instance) {
  1091. result = methodResult;
  1092. return false;
  1093. }
  1094. } else {
  1095. instance._detachWindowResizeCallback();
  1096. instance._init(config);
  1097. instance.render();
  1098. }
  1099. } else {
  1100. new Grid($element, config);
  1101. }
  1102. });
  1103. return result;
  1104. };
  1105. var fields = {};
  1106. var setDefaults = function(config) {
  1107. var componentPrototype;
  1108. if($.isPlainObject(config)) {
  1109. componentPrototype = Grid.prototype;
  1110. } else {
  1111. componentPrototype = fields[config].prototype;
  1112. config = arguments[1] || {};
  1113. }
  1114. $.extend(componentPrototype, config);
  1115. };
  1116. var locales = {};
  1117. var locale = function(lang) {
  1118. var localeConfig = $.isPlainObject(lang) ? lang : locales[lang];
  1119. if(!localeConfig)
  1120. throw Error("unknown locale " + lang);
  1121. setLocale(jsGrid, localeConfig);
  1122. };
  1123. var setLocale = function(obj, localeConfig) {
  1124. $.each(localeConfig, function(field, value) {
  1125. if($.isPlainObject(value)) {
  1126. setLocale(obj[field] || obj[field[0].toUpperCase() + field.slice(1)], value);
  1127. return;
  1128. }
  1129. if(obj.hasOwnProperty(field)) {
  1130. obj[field] = value;
  1131. } else {
  1132. obj.prototype[field] = value;
  1133. }
  1134. });
  1135. };
  1136. window.jsGrid = {
  1137. Grid: Grid,
  1138. fields: fields,
  1139. setDefaults: setDefaults,
  1140. locales: locales,
  1141. locale: locale
  1142. };
  1143. }(window, jQuery));
  1144. (function(jsGrid, $, undefined) {
  1145. function LoadIndicator(config) {
  1146. this._init(config);
  1147. }
  1148. LoadIndicator.prototype = {
  1149. container: "body",
  1150. message: "Loading...",
  1151. shading: true,
  1152. zIndex: 1000,
  1153. shaderClass: "jsgrid-load-shader",
  1154. loadPanelClass: "jsgrid-load-panel",
  1155. _init: function(config) {
  1156. $.extend(true, this, config);
  1157. this._initContainer();
  1158. this._initShader();
  1159. this._initLoadPanel();
  1160. },
  1161. _initContainer: function() {
  1162. this._container = $(this.container);
  1163. },
  1164. _initShader: function() {
  1165. if(!this.shading)
  1166. return;
  1167. this._shader = $("<div>").addClass(this.shaderClass)
  1168. .hide()
  1169. .css({
  1170. position: "absolute",
  1171. top: 0,
  1172. right: 0,
  1173. bottom: 0,
  1174. left: 0,
  1175. zIndex: this.zIndex
  1176. })
  1177. .appendTo(this._container);
  1178. },
  1179. _initLoadPanel: function() {
  1180. this._loadPanel = $("<div>").addClass(this.loadPanelClass)
  1181. .text(this.message)
  1182. .hide()
  1183. .css({
  1184. position: "absolute",
  1185. top: "50%",
  1186. left: "50%",
  1187. zIndex: this.zIndex
  1188. })
  1189. .appendTo(this._container);
  1190. },
  1191. show: function() {
  1192. var $loadPanel = this._loadPanel.show();
  1193. var actualWidth = $loadPanel.outerWidth();
  1194. var actualHeight = $loadPanel.outerHeight();
  1195. $loadPanel.css({
  1196. marginTop: -actualHeight / 2,
  1197. marginLeft: -actualWidth / 2
  1198. });
  1199. this._shader.show();
  1200. },
  1201. hide: function() {
  1202. this._loadPanel.hide();
  1203. this._shader.hide();
  1204. }
  1205. };
  1206. jsGrid.LoadIndicator = LoadIndicator;
  1207. }(jsGrid, jQuery));
  1208. (function(jsGrid, $, undefined) {
  1209. function DirectLoadingStrategy(grid) {
  1210. this._grid = grid;
  1211. }
  1212. DirectLoadingStrategy.prototype = {
  1213. firstDisplayIndex: function() {
  1214. var grid = this._grid;
  1215. return grid.option("paging") ? (grid.option("pageIndex") - 1) * grid.option("pageSize") : 0;
  1216. },
  1217. lastDisplayIndex: function() {
  1218. var grid = this._grid;
  1219. var itemsCount = grid.option("data").length;
  1220. return grid.option("paging")
  1221. ? Math.min(grid.option("pageIndex") * grid.option("pageSize"), itemsCount)
  1222. : itemsCount;
  1223. },
  1224. itemsCount: function() {
  1225. return this._grid.option("data").length;
  1226. },
  1227. openPage: function(index) {
  1228. this._grid.refresh();
  1229. },
  1230. loadParams: function() {
  1231. return {};
  1232. },
  1233. sort: function() {
  1234. this._grid._sortData();
  1235. this._grid.refresh();
  1236. return $.Deferred().resolve().promise();
  1237. },
  1238. finishLoad: function(loadedData) {
  1239. this._grid.option("data", loadedData);
  1240. },
  1241. finishInsert: function(insertedItem) {
  1242. var grid = this._grid;
  1243. grid.option("data").push(insertedItem);
  1244. grid.refresh();
  1245. },
  1246. finishDelete: function(deletedItem, deletedItemIndex) {
  1247. var grid = this._grid;
  1248. grid.option("data").splice(deletedItemIndex, 1);
  1249. grid.reset();
  1250. }
  1251. };
  1252. function PageLoadingStrategy(grid) {
  1253. this._grid = grid;
  1254. this._itemsCount = 0;
  1255. }
  1256. PageLoadingStrategy.prototype = {
  1257. firstDisplayIndex: function() {
  1258. return 0;
  1259. },
  1260. lastDisplayIndex: function() {
  1261. return this._grid.option("data").length;
  1262. },
  1263. itemsCount: function() {
  1264. return this._itemsCount;
  1265. },
  1266. openPage: function(index) {
  1267. this._grid.loadData();
  1268. },
  1269. loadParams: function() {
  1270. var grid = this._grid;
  1271. return {
  1272. pageIndex: grid.option("pageIndex"),
  1273. pageSize: grid.option("pageSize")
  1274. };
  1275. },
  1276. sort: function() {
  1277. return this._grid.loadData();
  1278. },
  1279. finishLoad: function(loadedData) {
  1280. this._itemsCount = loadedData.itemsCount;
  1281. this._grid.option("data", loadedData.data);
  1282. },
  1283. finishInsert: function(insertedItem) {
  1284. this._grid.search();
  1285. },
  1286. finishDelete: function(deletedItem, deletedItemIndex) {
  1287. this._grid.search();
  1288. }
  1289. };
  1290. jsGrid.loadStrategies = {
  1291. DirectLoadingStrategy: DirectLoadingStrategy,
  1292. PageLoadingStrategy: PageLoadingStrategy
  1293. };
  1294. }(jsGrid, jQuery));
  1295. (function(jsGrid, $, undefined) {
  1296. var isDefined = function(val) {
  1297. return typeof(val) !== "undefined" && val !== null;
  1298. };
  1299. var sortStrategies = {
  1300. string: function(str1, str2) {
  1301. if(!isDefined(str1) && !isDefined(str2))
  1302. return 0;
  1303. if(!isDefined(str1))
  1304. return -1;
  1305. if(!isDefined(str2))
  1306. return 1;
  1307. return ("" + str1).localeCompare("" + str2);
  1308. },
  1309. number: function(n1, n2) {
  1310. return n1 - n2;
  1311. },
  1312. date: function(dt1, dt2) {
  1313. return dt1 - dt2;
  1314. },
  1315. numberAsString: function(n1, n2) {
  1316. return parseFloat(n1) - parseFloat(n2);
  1317. }
  1318. };
  1319. jsGrid.sortStrategies = sortStrategies;
  1320. }(jsGrid, jQuery));
  1321. (function(jsGrid, $, undefined) {
  1322. function Validation(config) {
  1323. this._init(config);
  1324. }
  1325. Validation.prototype = {
  1326. _init: function(config) {
  1327. $.extend(true, this, config);
  1328. },
  1329. validate: function(args) {
  1330. var errors = [];
  1331. $.each(this._normalizeRules(args.rules), function(_, rule) {
  1332. if(rule.validator(args.value, args.item, rule.param))
  1333. return;
  1334. var errorMessage = $.isFunction(rule.message) ? rule.message(args.value, args.item) : rule.message;
  1335. errors.push(errorMessage);
  1336. });
  1337. return errors;
  1338. },
  1339. _normalizeRules: function(rules) {
  1340. if(!$.isArray(rules))
  1341. rules = [rules];
  1342. return $.map(rules, $.proxy(function(rule) {
  1343. return this._normalizeRule(rule);
  1344. }, this));
  1345. },
  1346. _normalizeRule: function(rule) {
  1347. if(typeof rule === "string")
  1348. rule = { validator: rule };
  1349. if($.isFunction(rule))
  1350. rule = { validator: rule };
  1351. if($.isPlainObject(rule))
  1352. rule = $.extend({}, rule);
  1353. else
  1354. throw Error("wrong validation config specified");
  1355. if($.isFunction(rule.validator))
  1356. return rule;
  1357. return this._applyNamedValidator(rule, rule.validator);
  1358. },
  1359. _applyNamedValidator: function(rule, validatorName) {
  1360. delete rule.validator;
  1361. var validator = validators[validatorName];
  1362. if(!validator)
  1363. throw Error("unknown validator \"" + validatorName + "\"");
  1364. if($.isFunction(validator)) {
  1365. validator = { validator: validator };
  1366. }
  1367. return $.extend({}, validator, rule);
  1368. }
  1369. };
  1370. jsGrid.Validation = Validation;
  1371. var validators = {
  1372. required: {
  1373. message: "Field is required",
  1374. validator: function(value) {
  1375. return value !== undefined && value !== null && value !== "";
  1376. }
  1377. },
  1378. rangeLength: {
  1379. message: "Field value length is out of the defined range",
  1380. validator: function(value, _, param) {
  1381. return value.length >= param[0] && value.length <= param[1];
  1382. }
  1383. },
  1384. minLength: {
  1385. message: "Field value is too long",
  1386. validator: function(value, _, param) {
  1387. return value.length >= param;
  1388. }
  1389. },
  1390. maxLength: {
  1391. message: "Field value is too short",
  1392. validator: function(value, _, param) {
  1393. return value.length <= param;
  1394. }
  1395. },
  1396. pattern: {
  1397. message: "Field value is not matching the defined pattern",
  1398. validator: function(value, _, param) {
  1399. if(typeof param === "string") {
  1400. param = new RegExp("^(?:" + param + ")$");
  1401. }
  1402. return param.test(value);
  1403. }
  1404. },
  1405. range: {
  1406. message: "Field value is out of the defined range",
  1407. validator: function(value, _, param) {
  1408. return value >= param[0] && value <= param[1];
  1409. }
  1410. },
  1411. min: {
  1412. message: "Field value is too large",
  1413. validator: function(value, _, param) {
  1414. return value >= param;
  1415. }
  1416. },
  1417. max: {
  1418. message: "Field value is too small",
  1419. validator: function(value, _, param) {
  1420. return value <= param;
  1421. }
  1422. }
  1423. };
  1424. jsGrid.validators = validators;
  1425. }(jsGrid, jQuery));
  1426. (function(jsGrid, $, undefined) {
  1427. function Field(config) {
  1428. $.extend(true, this, config);
  1429. this.sortingFunc = this._getSortingFunc();
  1430. }
  1431. Field.prototype = {
  1432. name: "",
  1433. title: null,
  1434. css: "",
  1435. align: "",
  1436. width: 100,
  1437. visible: true,
  1438. filtering: true,
  1439. inserting: true,
  1440. editing: true,
  1441. sorting: true,
  1442. sorter: "string", // name of SortStrategy or function to compare elements
  1443. headerTemplate: function() {
  1444. return (this.title === undefined || this.title === null) ? this.name : this.title;
  1445. },
  1446. itemTemplate: function(value, item) {
  1447. return value;
  1448. },
  1449. filterTemplate: function() {
  1450. return "";
  1451. },
  1452. insertTemplate: function() {
  1453. return "";
  1454. },
  1455. editTemplate: function(value, item) {
  1456. this._value = value;
  1457. return this.itemTemplate(value, item);
  1458. },
  1459. filterValue: function() {
  1460. return "";
  1461. },
  1462. insertValue: function() {
  1463. return "";
  1464. },
  1465. editValue: function() {
  1466. return this._value;
  1467. },
  1468. _getSortingFunc: function() {
  1469. var sorter = this.sorter;
  1470. if($.isFunction(sorter)) {
  1471. return sorter;
  1472. }
  1473. if(typeof sorter === "string") {
  1474. return jsGrid.sortStrategies[sorter];
  1475. }
  1476. throw Error("wrong sorter for the field \"" + this.name + "\"!");
  1477. }
  1478. };
  1479. jsGrid.Field = Field;
  1480. }(jsGrid, jQuery));
  1481. (function(jsGrid, $, undefined) {
  1482. var Field = jsGrid.Field;
  1483. function TextField(config) {
  1484. Field.call(this, config);
  1485. }
  1486. TextField.prototype = new Field({
  1487. autosearch: true,
  1488. readOnly: false,
  1489. filterTemplate: function() {
  1490. if(!this.filtering)
  1491. return "";
  1492. var grid = this._grid,
  1493. $result = this.filterControl = this._createTextBox();
  1494. if(this.autosearch) {
  1495. $result.on("keypress", function(e) {
  1496. if(e.which === 13) {
  1497. grid.search();
  1498. e.preventDefault();
  1499. }
  1500. });
  1501. }
  1502. return $result;
  1503. },
  1504. insertTemplate: function() {
  1505. if(!this.inserting)
  1506. return "";
  1507. return this.insertControl = this._createTextBox();
  1508. },
  1509. editTemplate: function(value) {
  1510. if(!this.editing)
  1511. return this.itemTemplate(value);
  1512. var $result = this.editControl = this._createTextBox();
  1513. $result.val(value);
  1514. return $result;
  1515. },
  1516. filterValue: function() {
  1517. return this.filterControl.val();
  1518. },
  1519. insertValue: function() {
  1520. return this.insertControl.val();
  1521. },
  1522. editValue: function() {
  1523. return this.editControl.val();
  1524. },
  1525. _createTextBox: function() {
  1526. return $("<input>").attr("type", "text")
  1527. .prop("readonly", !!this.readOnly);
  1528. }
  1529. });
  1530. jsGrid.fields.text = jsGrid.TextField = TextField;
  1531. }(jsGrid, jQuery));
  1532. (function(jsGrid, $, undefined) {
  1533. var TextField = jsGrid.TextField;
  1534. function NumberField(config) {
  1535. TextField.call(this, config);
  1536. }
  1537. NumberField.prototype = new TextField({
  1538. sorter: "number",
  1539. align: "right",
  1540. readOnly: false,
  1541. filterValue: function() {
  1542. return parseInt(this.filterControl.val() || 0, 10);
  1543. },
  1544. insertValue: function() {
  1545. return parseInt(this.insertControl.val() || 0, 10);
  1546. },
  1547. editValue: function() {
  1548. return parseInt(this.editControl.val() || 0, 10);
  1549. },
  1550. _createTextBox: function() {
  1551. return $("<input>").attr("type", "number")
  1552. .prop("readonly", !!this.readOnly);
  1553. }
  1554. });
  1555. jsGrid.fields.number = jsGrid.NumberField = NumberField;
  1556. }(jsGrid, jQuery));
  1557. (function(jsGrid, $, undefined) {
  1558. var TextField = jsGrid.TextField;
  1559. function TextAreaField(config) {
  1560. TextField.call(this, config);
  1561. }
  1562. TextAreaField.prototype = new TextField({
  1563. insertTemplate: function() {
  1564. if(!this.inserting)
  1565. return "";
  1566. return this.insertControl = this._createTextArea();
  1567. },
  1568. editTemplate: function(value) {
  1569. if(!this.editing)
  1570. return this.itemTemplate(value);
  1571. var $result = this.editControl = this._createTextArea();
  1572. $result.val(value);
  1573. return $result;
  1574. },
  1575. _createTextArea: function() {
  1576. return $("<textarea>").prop("readonly", !!this.readOnly);
  1577. }
  1578. });
  1579. jsGrid.fields.textarea = jsGrid.TextAreaField = TextAreaField;
  1580. }(jsGrid, jQuery));
  1581. (function(jsGrid, $, undefined) {
  1582. var NumberField = jsGrid.NumberField;
  1583. function SelectField(config) {
  1584. this.items = [];
  1585. this.selectedIndex = -1;
  1586. this.valueField = "";
  1587. this.textField = "";
  1588. if(config.valueField && config.items.length) {
  1589. this.valueType = typeof config.items[0][config.valueField];
  1590. }
  1591. this.sorter = this.valueType;
  1592. NumberField.call(this, config);
  1593. }
  1594. SelectField.prototype = new NumberField({
  1595. align: "center",
  1596. valueType: "number",
  1597. itemTemplate: function(value) {
  1598. var items = this.items,
  1599. valueField = this.valueField,
  1600. textField = this.textField,
  1601. resultItem;
  1602. if(valueField) {
  1603. resultItem = $.grep(items, function(item, index) {
  1604. return item[valueField] === value;
  1605. })[0] || {};
  1606. }
  1607. else {
  1608. resultItem = items[value];
  1609. }
  1610. var result = (textField ? resultItem[textField] : resultItem);
  1611. return (result === undefined || result === null) ? "" : result;
  1612. },
  1613. filterTemplate: function() {
  1614. if(!this.filtering)
  1615. return "";
  1616. var grid = this._grid,
  1617. $result = this.filterControl = this._createSelect();
  1618. if(this.autosearch) {
  1619. $result.on("change", function(e) {
  1620. grid.search();
  1621. });
  1622. }
  1623. return $result;
  1624. },
  1625. insertTemplate: function() {
  1626. if(!this.inserting)
  1627. return "";
  1628. return this.insertControl = this._createSelect();
  1629. },
  1630. editTemplate: function(value) {
  1631. if(!this.editing)
  1632. return this.itemTemplate(value);
  1633. var $result = this.editControl = this._createSelect();
  1634. (value !== undefined) && $result.val(value);
  1635. return $result;
  1636. },
  1637. filterValue: function() {
  1638. var val = this.filterControl.val();
  1639. return this.valueType === "number" ? parseInt(val || 0, 10) : val;
  1640. },
  1641. insertValue: function() {
  1642. var val = this.insertControl.val();
  1643. return this.valueType === "number" ? parseInt(val || 0, 10) : val;
  1644. },
  1645. editValue: function() {
  1646. var val = this.editControl.val();
  1647. return this.valueType === "number" ? parseInt(val || 0, 10) : val;
  1648. },
  1649. _createSelect: function() {
  1650. var $result = $("<select>"),
  1651. valueField = this.valueField,
  1652. textField = this.textField,
  1653. selectedIndex = this.selectedIndex;
  1654. $.each(this.items, function(index, item) {
  1655. var value = valueField ? item[valueField] : index,
  1656. text = textField ? item[textField] : item;
  1657. var $option = $("<option>")
  1658. .attr("value", value)
  1659. .text(text)
  1660. .appendTo($result);
  1661. $option.prop("selected", (selectedIndex === index));
  1662. });
  1663. $result.prop("disabled", !!this.readOnly);
  1664. return $result;
  1665. }
  1666. });
  1667. jsGrid.fields.select = jsGrid.SelectField = SelectField;
  1668. }(jsGrid, jQuery));
  1669. (function(jsGrid, $, undefined) {
  1670. var Field = jsGrid.Field;
  1671. function CheckboxField(config) {
  1672. Field.call(this, config);
  1673. }
  1674. CheckboxField.prototype = new Field({
  1675. sorter: "number",
  1676. align: "center",
  1677. autosearch: true,
  1678. itemTemplate: function(value) {
  1679. return this._createCheckbox().prop({
  1680. checked: value,
  1681. disabled: true
  1682. });
  1683. },
  1684. filterTemplate: function() {
  1685. if(!this.filtering)
  1686. return "";
  1687. var grid = this._grid,
  1688. $result = this.filterControl = this._createCheckbox();
  1689. $result.prop({
  1690. readOnly: true,
  1691. indeterminate: true
  1692. });
  1693. $result.on("click", function() {
  1694. var $cb = $(this);
  1695. if($cb.prop("readOnly")) {
  1696. $cb.prop({
  1697. checked: false,
  1698. readOnly: false
  1699. });
  1700. }
  1701. else if(!$cb.prop("checked")) {
  1702. $cb.prop({
  1703. readOnly: true,
  1704. indeterminate: true
  1705. });
  1706. }
  1707. });
  1708. if(this.autosearch) {
  1709. $result.on("click", function() {
  1710. grid.search();
  1711. });
  1712. }
  1713. return $result;
  1714. },
  1715. insertTemplate: function() {
  1716. if(!this.inserting)
  1717. return "";
  1718. return this.insertControl = this._createCheckbox();
  1719. },
  1720. editTemplate: function(value) {
  1721. if(!this.editing)
  1722. return this.itemTemplate(value);
  1723. var $result = this.editControl = this._createCheckbox();
  1724. $result.prop("checked", value);
  1725. return $result;
  1726. },
  1727. filterValue: function() {
  1728. return this.filterControl.get(0).indeterminate
  1729. ? undefined
  1730. : this.filterControl.is(":checked");
  1731. },
  1732. insertValue: function() {
  1733. return this.insertControl.is(":checked");
  1734. },
  1735. editValue: function() {
  1736. return this.editControl.is(":checked");
  1737. },
  1738. _createCheckbox: function() {
  1739. return $("<input>").attr("type", "checkbox");
  1740. }
  1741. });
  1742. jsGrid.fields.checkbox = jsGrid.CheckboxField = CheckboxField;
  1743. }(jsGrid, jQuery));
  1744. (function(jsGrid, $, undefined) {
  1745. var Field = jsGrid.Field;
  1746. function ControlField(config) {
  1747. Field.call(this, config);
  1748. this._configInitialized = false;
  1749. }
  1750. ControlField.prototype = new Field({
  1751. css: "jsgrid-control-field",
  1752. align: "center",
  1753. width: 50,
  1754. filtering: false,
  1755. inserting: false,
  1756. editing: false,
  1757. sorting: false,
  1758. buttonClass: "jsgrid-button",
  1759. modeButtonClass: "jsgrid-mode-button",
  1760. modeOnButtonClass: "jsgrid-mode-on-button",
  1761. searchModeButtonClass: "jsgrid-search-mode-button",
  1762. insertModeButtonClass: "jsgrid-insert-mode-button",
  1763. editButtonClass: "jsgrid-edit-button",
  1764. deleteButtonClass: "jsgrid-delete-button",
  1765. searchButtonClass: "jsgrid-search-button",
  1766. clearFilterButtonClass: "jsgrid-clear-filter-button",
  1767. insertButtonClass: "jsgrid-insert-button",
  1768. updateButtonClass: "jsgrid-update-button",
  1769. cancelEditButtonClass: "jsgrid-cancel-edit-button",
  1770. searchModeButtonTooltip: "Switch to searching",
  1771. insertModeButtonTooltip: "Switch to inserting",
  1772. editButtonTooltip: "Edit",
  1773. deleteButtonTooltip: "Delete",
  1774. searchButtonTooltip: "Search",
  1775. clearFilterButtonTooltip: "Clear filter",
  1776. insertButtonTooltip: "Insert",
  1777. updateButtonTooltip: "Update",
  1778. cancelEditButtonTooltip: "Cancel edit",
  1779. editButton: true,
  1780. deleteButton: true,
  1781. clearFilterButton: true,
  1782. modeSwitchButton: true,
  1783. _initConfig: function() {
  1784. this._hasFiltering = this._grid.filtering;
  1785. this._hasInserting = this._grid.inserting;
  1786. if(this._hasInserting && this.modeSwitchButton) {
  1787. this._grid.inserting = false;
  1788. }
  1789. this._configInitialized = true;
  1790. },
  1791. headerTemplate: function() {
  1792. if(!this._configInitialized) {
  1793. this._initConfig();
  1794. }
  1795. var hasFiltering = this._hasFiltering;
  1796. var hasInserting = this._hasInserting;
  1797. if(!this.modeSwitchButton || (!hasFiltering && !hasInserting))
  1798. return "";
  1799. if(hasFiltering && !hasInserting)
  1800. return this._createFilterSwitchButton();
  1801. if(hasInserting && !hasFiltering)
  1802. return this._createInsertSwitchButton();
  1803. return this._createModeSwitchButton();
  1804. },
  1805. itemTemplate: function(value, item) {
  1806. var $result = $([]);
  1807. if(this.editButton) {
  1808. $result = $result.add(this._createEditButton(item));
  1809. }
  1810. if(this.deleteButton) {
  1811. $result = $result.add(this._createDeleteButton(item));
  1812. }
  1813. return $result;
  1814. },
  1815. filterTemplate: function() {
  1816. var $result = this._createSearchButton();
  1817. return this.clearFilterButton ? $result.add(this._createClearFilterButton()) : $result;
  1818. },
  1819. insertTemplate: function() {
  1820. return this._createInsertButton();
  1821. },
  1822. editTemplate: function() {
  1823. return this._createUpdateButton().add(this._createCancelEditButton());
  1824. },
  1825. _createFilterSwitchButton: function() {
  1826. return this._createOnOffSwitchButton("filtering", this.searchModeButtonClass, true);
  1827. },
  1828. _createInsertSwitchButton: function() {
  1829. return this._createOnOffSwitchButton("inserting", this.insertModeButtonClass, false);
  1830. },
  1831. _createOnOffSwitchButton: function(option, cssClass, isOnInitially) {
  1832. var isOn = isOnInitially;
  1833. var updateButtonState = $.proxy(function() {
  1834. $button.toggleClass(this.modeOnButtonClass, isOn);
  1835. }, this);
  1836. var $button = this._createGridButton(this.modeButtonClass + " " + cssClass, "", function(grid) {
  1837. isOn = !isOn;
  1838. grid.option(option, isOn);
  1839. updateButtonState();
  1840. });
  1841. updateButtonState();
  1842. return $button;
  1843. },
  1844. _createModeSwitchButton: function() {
  1845. var isInserting = false;
  1846. var updateButtonState = $.proxy(function() {
  1847. $button.attr("title", isInserting ? this.searchModeButtonTooltip : this.insertModeButtonTooltip)
  1848. .toggleClass(this.insertModeButtonClass, !isInserting)
  1849. .toggleClass(this.searchModeButtonClass, isInserting);
  1850. }, this);
  1851. var $button = this._createGridButton(this.modeButtonClass, "", function(grid) {
  1852. isInserting = !isInserting;
  1853. grid.option("inserting", isInserting);
  1854. grid.option("filtering", !isInserting);
  1855. updateButtonState();
  1856. });
  1857. updateButtonState();
  1858. return $button;
  1859. },
  1860. _createEditButton: function(item) {
  1861. return this._createGridButton(this.editButtonClass, this.editButtonTooltip, function(grid, e) {
  1862. grid.editItem(item);
  1863. e.stopPropagation();
  1864. });
  1865. },
  1866. _createDeleteButton: function(item) {
  1867. return this._createGridButton(this.deleteButtonClass, this.deleteButtonTooltip, function(grid, e) {
  1868. grid.deleteItem(item);
  1869. e.stopPropagation();
  1870. });
  1871. },
  1872. _createSearchButton: function() {
  1873. return this._createGridButton(this.searchButtonClass, this.searchButtonTooltip, function(grid) {
  1874. grid.search();
  1875. });
  1876. },
  1877. _createClearFilterButton: function() {
  1878. return this._createGridButton(this.clearFilterButtonClass, this.clearFilterButtonTooltip, function(grid) {
  1879. grid.clearFilter();
  1880. });
  1881. },
  1882. _createInsertButton: function() {
  1883. return this._createGridButton(this.insertButtonClass, this.insertButtonTooltip, function(grid) {
  1884. grid.insertItem().done(function() {
  1885. grid.clearInsert();
  1886. });
  1887. });
  1888. },
  1889. _createUpdateButton: function() {
  1890. return this._createGridButton(this.updateButtonClass, this.updateButtonTooltip, function(grid, e) {
  1891. grid.updateItem();
  1892. e.stopPropagation();
  1893. });
  1894. },
  1895. _createCancelEditButton: function() {
  1896. return this._createGridButton(this.cancelEditButtonClass, this.cancelEditButtonTooltip, function(grid, e) {
  1897. grid.cancelEdit();
  1898. e.stopPropagation();
  1899. });
  1900. },
  1901. _createGridButton: function(cls, tooltip, clickHandler) {
  1902. var grid = this._grid;
  1903. return $("<input>").addClass(this.buttonClass)
  1904. .addClass(cls)
  1905. .attr({
  1906. type: "button",
  1907. title: tooltip
  1908. })
  1909. .on("click", function(e) {
  1910. clickHandler(grid, e);
  1911. });
  1912. },
  1913. editValue: function() {
  1914. return "";
  1915. }
  1916. });
  1917. jsGrid.fields.control = jsGrid.ControlField = ControlField;
  1918. }(jsGrid, jQuery));