Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

372 řádky
9.6 KiB

  1. // Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. //
  3. // MIT License (MIT)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a
  6. // copy of this software and associated documentation files (the "Software"),
  7. // to deal in the Software without restriction, including without limitation
  8. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. // and/or sell copies of the Software, and to permit persons to whom the
  10. // Software is furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. // CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. //
  22. // new re-factored Listing object
  23. // uses FieldGroup for rendering filters
  24. // removed rarely used functionality
  25. //
  26. // opts:
  27. // parent
  28. // method (method to call on server)
  29. // args (additional args to method)
  30. // get_args (method to return args as dict)
  31. // show_filters [false]
  32. // doctype
  33. // filter_fields (if given, this list is rendered, else built from doctype)
  34. // query or get_query (will be deprecated)
  35. // query_max
  36. // buttons_in_frame
  37. // no_result_message ("No result")
  38. // page_length (20)
  39. // hide_refresh (False)
  40. // no_toolbar
  41. // new_doctype
  42. // [function] render_row(parent, data)
  43. // [function] onrun
  44. // no_loading (no ajax indicator)
  45. wn.provide('wn.ui');
  46. wn.ui.Listing = Class.extend({
  47. init: function(opts) {
  48. this.opts = opts || {};
  49. this.page_length = 20;
  50. this.start = 0;
  51. this.data = [];
  52. if(opts) {
  53. this.make();
  54. }
  55. },
  56. prepare_opts: function() {
  57. if(this.opts.new_doctype) {
  58. if(wn.boot.profile.can_create.indexOf(this.opts.new_doctype)==-1) {
  59. this.opts.new_doctype = null;
  60. } else {
  61. this.opts.new_doctype = wn._(this.opts.new_doctype);
  62. }
  63. }
  64. if(!this.opts.no_result_message) {
  65. this.opts.no_result_message = 'Nothing to show'
  66. }
  67. },
  68. make: function(opts) {
  69. if(opts) {
  70. this.opts = opts;
  71. }
  72. this.prepare_opts();
  73. $.extend(this, this.opts);
  74. $(this.parent).html(repl('\
  75. <div class="wnlist">\
  76. <h3 class="title hide">%(title)s</h3>\
  77. \
  78. <div class="list-filters" style="display: none;">\
  79. <div class="show_filters well">\
  80. <div class="filter_area"></div>\
  81. <div>\
  82. <button class="btn btn-info search-btn">\
  83. <i class="icon-refresh icon-white"></i> Search</button>\
  84. <button class="btn add-filter-btn">\
  85. <i class="icon-plus"></i> Add Filter</button>\
  86. </div>\
  87. </div>\
  88. </div>\
  89. \
  90. <div style="margin-bottom:9px" class="list-toolbar-wrapper">\
  91. <div class="list-toolbar btn-group" style="display:inline-block; margin-right: 10px;">\
  92. </div>\
  93. <div style="display:inline-block; width: 24px; margin-left: 4px">\
  94. <img src="lib/images/ui/button-load.gif" \
  95. class="img-load"/></div>\
  96. </div><div style="clear:both"></div>\
  97. \
  98. <div class="no-result help hide">\
  99. %(no_result_message)s\
  100. </div>\
  101. \
  102. <div class="result">\
  103. <div class="result-list"></div>\
  104. </div>\
  105. \
  106. <div class="paging-button">\
  107. <button class="btn btn-more hide">More...</div>\
  108. </div>\
  109. </div>\
  110. ', this.opts));
  111. this.$w = $(this.parent).find('.wnlist');
  112. this.set_events();
  113. if(this.appframe) {
  114. this.$w.find('.list-toolbar-wrapper').toggle(false);
  115. }
  116. if(this.show_filters) {
  117. this.make_filters();
  118. }
  119. },
  120. add_button: function(label, click, icon) {
  121. if(this.appframe) {
  122. return this.appframe.add_button(label, click, icon)
  123. } else {
  124. $button = $('<button class="btn"></button>')
  125. .appendTo(this.$w.find('.list-toolbar'))
  126. .html((icon ? ("<i class='"+icon+"'></i> ") : "") + label)
  127. .click(click);
  128. return $button
  129. }
  130. },
  131. show_view: function($btn, $div, $btn_unsel, $div_unsel) {
  132. $btn_unsel.removeClass('btn-info');
  133. $btn_unsel.find('i').removeClass('icon-white');
  134. $div_unsel.toggle(false);
  135. $btn.addClass('btn-info');
  136. $btn.find('i').addClass('icon-white');
  137. $div.toggle(true);
  138. },
  139. set_events: function() {
  140. var me = this;
  141. // next page
  142. this.$w.find('.btn-more').click(function() {
  143. me.run({append: true });
  144. });
  145. // title
  146. if(this.title) {
  147. this.$w.find('h3').html(this.title).toggle(true);
  148. }
  149. // hide-refresh
  150. if(!(this.hide_refresh || this.no_refresh)) {
  151. this.add_button('', function() {
  152. me.run();
  153. }, 'icon-refresh');
  154. }
  155. // new
  156. if(this.new_doctype) {
  157. this.add_button('New ' + this.new_doctype, function() {
  158. (me.custom_new_doc || me.make_new_doc)(me.new_doctype);
  159. }, 'icon-plus');
  160. }
  161. // hide-filter
  162. if(me.show_filters) {
  163. this.add_button('Show Filters', function() {
  164. me.filter_list.show_filters();
  165. }, 'icon-search').addClass('btn-filter');
  166. }
  167. if(me.no_toolbar || me.hide_toolbar) {
  168. me.$w.find('.list-toolbar-wrapper').toggle(false);
  169. }
  170. },
  171. make_new_doc: function(new_doctype) {
  172. new_doc(new_doctype);
  173. },
  174. make_filters: function() {
  175. this.filter_list = new wn.ui.FilterList({
  176. listobj: this,
  177. $parent: this.$w.find('.list-filters').toggle(true),
  178. doctype: this.doctype,
  179. filter_fields: this.filter_fields
  180. });
  181. },
  182. clear: function() {
  183. this.data = [];
  184. this.$w.find('.result-list').empty();
  185. this.$w.find('.result').toggle(true);
  186. this.$w.find('.no-result').toggle(false);
  187. this.start = 0;
  188. },
  189. run: function() {
  190. // in old - arguments: 0 = callback, 1 = append
  191. var me = this;
  192. var a0 = arguments[0]; var a1 = arguments[1];
  193. if(a0 && typeof a0=='function')
  194. this.onrun = a0;
  195. if(a0 && a0.callback)
  196. this.onrun = a0.callback;
  197. if(!a1 && !(a0 && a0.append))
  198. this.start = 0;
  199. if(!me.opts.no_loading)
  200. me.set_working(true);
  201. wn.call({
  202. method: this.opts.method || 'webnotes.widgets.query_builder.runquery',
  203. args: this.get_call_args(a0),
  204. callback: function(r) {
  205. if(!me.opts.no_loading)me.set_working(false);
  206. me.render_results(r)
  207. },
  208. no_spinner: this.opts.no_loading
  209. });
  210. },
  211. set_working: function(flag) {
  212. this.$w.find('.img-load').toggle(flag);
  213. },
  214. get_call_args: function(opts) {
  215. // load query
  216. if(!this.method) {
  217. var query = this.get_query ? this.get_query() : this.query;
  218. query = this.add_limits(query);
  219. var args={
  220. query_max: this.query_max,
  221. as_dict: 1
  222. }
  223. args.simple_query = query;
  224. } else {
  225. var args = {
  226. limit_start: this.start,
  227. limit_page_length: this.page_length
  228. }
  229. }
  230. // append user-defined arguments
  231. if(this.args)
  232. $.extend(args, this.args)
  233. if(this.get_args) {
  234. $.extend(args, this.get_args(opts));
  235. }
  236. return args;
  237. },
  238. render_results: function(r) {
  239. if(this.start==0) this.clear();
  240. this.$w.find('.btn-more').toggle(false);
  241. if(r.message) {
  242. r.values = this.get_values_from_response(r.message);
  243. }
  244. if(r.values && r.values.length) {
  245. this.data = this.data.concat(r.values);
  246. this.render_list(r.values);
  247. this.update_paging(r.values);
  248. } else {
  249. if(this.start==0) {
  250. this.$w.find('.result').toggle(false);
  251. var msg = this.get_no_result_message
  252. ? this.get_no_result_message()
  253. : (this.no_result_message
  254. ? this.no_result_message
  255. : wn._("Nothing to show"));
  256. this.$w.find('.no-result')
  257. .html(msg)
  258. .toggle(true);
  259. }
  260. }
  261. // callbacks
  262. if(this.onrun) this.onrun();
  263. if(this.callback) this.callback(r);
  264. },
  265. get_values_from_response: function(data) {
  266. // make dictionaries from keys and values
  267. if(data.keys) {
  268. var values = [];
  269. $.each(data.values, function(row_idx, row) {
  270. var new_row = {};
  271. $.each(data.keys, function(key_idx, key) {
  272. new_row[key] = row[key_idx];
  273. })
  274. values.push(new_row);
  275. });
  276. return values;
  277. } else {
  278. return data;
  279. }
  280. },
  281. render_list: function(values) {
  282. var m = Math.min(values.length, this.page_length);
  283. this.data = values;
  284. if(this.filter_list)
  285. this.filter_values = this.filter_list.get_filters();
  286. // render the rows
  287. for(var i=0; i < m; i++) {
  288. this.render_row(this.add_row(), values[i], this, i);
  289. }
  290. },
  291. update_paging: function(values) {
  292. if(values.length >= this.page_length) {
  293. this.$w.find('.btn-more').toggle(true);
  294. this.start += this.page_length;
  295. }
  296. },
  297. add_row: function() {
  298. return $('<div class="list-row">').appendTo(this.$w.find('.result-list')).get(0);
  299. },
  300. refresh: function() {
  301. this.run();
  302. },
  303. add_limits: function(query) {
  304. query += ' LIMIT ' + this.start + ',' + (this.page_length+1);
  305. return query
  306. },
  307. set_filter: function(fieldname, label) {
  308. var filter = this.filter_list.get_filter(fieldname);
  309. //this.filter_list.show_filters(true);
  310. if(filter) {
  311. var v = filter.field.get_value();
  312. if(v.indexOf(label)!=-1) {
  313. // already set
  314. return false;
  315. } else {
  316. // second filter set for this field
  317. if(fieldname=='_user_tags') {
  318. // and for tags
  319. this.filter_list.add_filter(this.doctype, fieldname,
  320. 'like', '%' + label);
  321. } else {
  322. // or for rest using "in"
  323. filter.set_values(this.doctype, fieldname, 'in', v + ', ' + label);
  324. }
  325. }
  326. } else {
  327. // no filter for this item,
  328. // setup one
  329. if(fieldname=='_user_tags') {
  330. this.filter_list.add_filter(this.doctype, fieldname,
  331. 'like', '%' + label);
  332. } else {
  333. this.filter_list.add_filter(this.doctype, fieldname, '=', label);
  334. }
  335. }
  336. }
  337. });