選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

listview.js 11 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // Copyright 2013 Web Notes Technologies Pvt Ltd
  2. // License: MIT. See license.txt
  3. wn.views.get_listview = function(doctype, parent) {
  4. var meta = locals.DocType[doctype];
  5. if(meta.__listjs) {
  6. eval(meta.__listjs);
  7. }
  8. if(wn.doclistviews[doctype]) {
  9. var listview = new wn.doclistviews[doctype](parent);
  10. } else {
  11. var listview = new wn.views.ListView(parent, doctype);
  12. }
  13. return listview;
  14. }
  15. wn.provide("wn.listview_settings");
  16. wn.views.ListView = Class.extend({
  17. init: function(doclistview, doctype) {
  18. this.doclistview = doclistview;
  19. this.doctype = doctype;
  20. this.settings = wn.listview_settings[this.doctype] || {};
  21. this.set_fields();
  22. this.set_columns();
  23. if(this.settings.group_by)
  24. this.group_by = this.settings.group_by;
  25. },
  26. set_fields: function() {
  27. var me = this;
  28. var t = "`tab"+this.doctype+"`.";
  29. this.fields = [t + 'name', t + 'owner', t + 'docstatus',
  30. t + '_user_tags', t + 'modified', t + 'modified_by'];
  31. this.stats = ['_user_tags'];
  32. $.each(wn.model.get("DocField", {"parent":this.doctype, "in_list_view":1}), function(i,d) {
  33. if(d.fieldtype=="Image" && d.options) {
  34. me.fields.push(t + "`" + d.options + "`");
  35. } else {
  36. me.fields.push(t + "`" + d.fieldname + "`");
  37. }
  38. if(d.fieldtype=="Select") {
  39. me.stats.push(d.fieldname);
  40. }
  41. // currency field for symbol (multi-currency)
  42. if(d.fieldtype=="Currency" && d.options) {
  43. if(d.options.indexOf(":")!=-1) {
  44. me.fields.push(t + "`" + d.options.split(":")[1] + "`");
  45. } else {
  46. me.fields.push(t + "`" + d.options + "`");
  47. };
  48. }
  49. });
  50. // additional fields
  51. if(this.settings.add_fields) {
  52. $.each(this.settings.add_fields, function(i, d) {
  53. me.fields.push(d);
  54. });
  55. }
  56. },
  57. set_columns: function() {
  58. this.columns = [];
  59. var me = this;
  60. if(wn.model.can_delete(this.doctype)) {
  61. this.columns.push({width: '3%', content:'check'})
  62. }
  63. this.columns.push({width: '4%', content:'avatar'});
  64. if(wn.model.is_submittable(this.doctype)) {
  65. this.columns.push({width: '3%', content:'docstatus',
  66. css: {"text-align": "center"}});
  67. }
  68. this.columns.push({width: '20%', content:'name'});
  69. // overridden
  70. var overridden = $.map(this.settings.add_columns || [], function(d) {
  71. return d.content;
  72. });
  73. $.each(wn.model.get("DocField", {"parent":this.doctype, "in_list_view":1}),
  74. function(i,d) {
  75. if(in_list(overridden, d.fieldname)) {
  76. return;
  77. }
  78. // field width
  79. var width = "15%";
  80. if(in_list(["Int", "Percent"], d.fieldtype)) {
  81. width = "13%";
  82. } else if(in_list(["Int", "Percent"], d.fieldtype)) {
  83. width = "10%";
  84. } else if(d.fieldtype=="Check" || d.fieldname=="file_list") {
  85. width = "5%";
  86. } else if(d.fieldname=="subject") { // subjects are longer
  87. width = "20%";
  88. }
  89. me.columns.push({width:width, content: d.fieldname,
  90. type:d.fieldtype, df:d, title:wn._(d.label) });
  91. });
  92. // additional columns
  93. if(this.settings.add_columns) {
  94. $.each(this.settings.add_columns, function(i, d) {
  95. me.columns.push(d);
  96. });
  97. }
  98. // tags
  99. this.columns.push({width: '15%', content:'tags', css: {'color':'#aaa'}}),
  100. this.columns.push({width: '15%', content:'modified',
  101. css: {'text-align': 'right', 'color':'#222'}});
  102. },
  103. render: function(row, data) {
  104. var me = this;
  105. this.prepare_data(data);
  106. rowhtml = '';
  107. // make table
  108. $.each(this.columns, function(i, v) {
  109. if(v.content && v.content.substr && v.content.substr(0,6)=="avatar") {
  110. rowhtml += repl('<td style="width: 40px;"></td>');
  111. } else {
  112. rowhtml += repl('<td style="width: %(width)s"></td>', v);
  113. }
  114. });
  115. var tr = $(row).html('<table class="doclist-row"><tbody><tr>' + rowhtml + '</tr></tbody></table>').find('tr').get(0);
  116. // render cells
  117. $.each(this.columns, function(i, v) {
  118. me.render_column(data, $("<div>")
  119. .css({
  120. "overflow":"hidden",
  121. "white-space": "nowrap",
  122. "text-overflow": "ellipsis",
  123. "max-height": "30px",
  124. })
  125. .appendTo(tr.cells[i]).get(0), v);
  126. });
  127. },
  128. render_column: function(data, parent, opts) {
  129. var me = this;
  130. if(opts.type) opts.type= opts.type.toLowerCase();
  131. // style
  132. if(opts.css) {
  133. $.each(opts.css, function(k, v) { $(parent).css(k, v)});
  134. }
  135. // multiple content
  136. if(opts.content.indexOf && opts.content.indexOf('+')!=-1) {
  137. $.map(opts.content.split('+'), function(v) {
  138. me.render_column(data, parent, {content:v, title: opts.title});
  139. });
  140. return;
  141. }
  142. // content
  143. if(typeof opts.content=='function') {
  144. opts.content(parent, data, me);
  145. }
  146. else if(opts.content=='name') {
  147. $("<a>")
  148. .attr("href", "#Form/" + data.doctype + "/" + data.name)
  149. .html(data.name)
  150. .appendTo(parent);
  151. }
  152. else if(opts.content=='avatar' || opts.content=='avatar_modified') {
  153. $(parent).append(wn.avatar(data.owner, false, wn._("Modified by")+": "
  154. + wn.user_info(data.modified_by).fullname));
  155. }
  156. else if(opts.content=='check') {
  157. $(parent).append('<input class="list-delete" type="checkbox">');
  158. $(parent).find('input').data('name', data.name);
  159. }
  160. else if(opts.content=='docstatus') {
  161. $(parent).append(repl('<span class="docstatus"> \
  162. <i class="%(docstatus_icon)s" \
  163. title="%(docstatus_title)s"></i></span>', data));
  164. }
  165. else if(opts.content=='enabled') {
  166. data.icon = cint(data.enabled) ? "icon-check" : "icon-check-empty";
  167. data.title = cint(data.enabled) ? wn._("Enabled") : wn._("Disabled");
  168. $(parent).append(repl('<span class="docstatus"> \
  169. <i class="%(icon)s" title="%(title)s"></i></span>', data));
  170. }
  171. else if(opts.content=='disabled') {
  172. data.icon = cint(data.disabled) ? "icon-check-empty" : "icon-check"
  173. data.title = cint(data.disabled) ? wn._("Disabled") : wn._("Enabled");
  174. $(parent).append(repl('<span class="docstatus"> \
  175. <i class="%(icon)s"></i></span>', data));
  176. }
  177. else if(opts.content=='tags') {
  178. this.add_user_tags(parent, data);
  179. }
  180. else if(opts.content=='modified') {
  181. $("<span>")
  182. .html(data.when)
  183. .appendTo(parent)
  184. .attr("title", wn._("Last Modified On"))
  185. .css({"color":"#888"})
  186. }
  187. else if(opts.content=="file_list") {
  188. if(data[opts.content]) {
  189. $("<i class='icon-paper-clip'>").appendTo(parent);
  190. }
  191. }
  192. else if(opts.type=='bar-graph' || opts.type=="percent") {
  193. this.render_bar_graph(parent, data, opts.content, opts.label);
  194. }
  195. else if(opts.type=='link' && (opts.df ? opts.df.options : opts.doctype)) {
  196. var doctype = (opts.df ? opts.df.options : opts.doctype);
  197. $(parent).append(repl('<a href="#Form/'+ doctype +'/'
  198. +data[opts.content]+'">'+data[opts.content]+'</a>', data));
  199. }
  200. else if(opts.template) {
  201. $(parent).append(repl(opts.template, data));
  202. }
  203. else if(opts.type=="date" && data[opts.content]) {
  204. $("<span>")
  205. .html(wn.datetime.str_to_user(data[opts.content]))
  206. .css({"color":"#888"})
  207. .appendTo(parent);
  208. }
  209. else if(opts.type=="image") {
  210. data[opts.content] = data[opts.df.options];
  211. if(data[opts.content])
  212. $("<img>")
  213. .attr("src", wn.utils.get_file_link(data[opts.content]))
  214. .css({
  215. "max-width": "100px",
  216. "max-height": "30px"
  217. })
  218. .appendTo(parent);
  219. }
  220. else if(opts.type=="select" && data[opts.content]) {
  221. var label_class = "";
  222. if(has_words(["Open", "Pending"], data[opts.content])) {
  223. label_class = "label-important";
  224. } else if(has_words(["Closed", "Finished", "Converted", "Completed", "Confirmed",
  225. "Approved", "Yes", "Active"], data[opts.content])) {
  226. label_class = "label-success";
  227. } else if(has_words(["Submitted"], data[opts.content])) {
  228. label_class = "label-info";
  229. }
  230. $("<span class='label'>"
  231. + data[opts.content] + "</span>")
  232. .css({"cursor":"pointer"})
  233. .addClass(label_class)
  234. .attr("data-fieldname", opts.content)
  235. .click(function() {
  236. me.doclistview.set_filter($(this).attr("data-fieldname"),
  237. $(this).text());
  238. })
  239. .appendTo(parent);
  240. }
  241. else if(data[opts.content]) {
  242. $("<span>")
  243. .html(wn.format(data[opts.content], opts.df, null, data))
  244. .appendTo(parent)
  245. }
  246. // finally
  247. if(!$(parent).html()) {
  248. $("<span>-</span>").css({color:"#ccc"}).appendTo(parent);
  249. }
  250. // title
  251. if(!in_list(["avatar"], opts.content)) {
  252. $(parent).attr("title", (opts.title || opts.content) + ": "
  253. + (data[opts.content] || "Not Set"))
  254. .tooltip();
  255. }
  256. },
  257. show_hide_check_column: function() {
  258. if(!this.doclistview.can_delete) {
  259. this.columns = $.map(this.columns, function(v, i) { if(v.content!='check') return v });
  260. }
  261. },
  262. prepare_data: function(data) {
  263. if(data.modified)
  264. this.prepare_when(data, data.modified);
  265. // docstatus
  266. if(data.docstatus==0 || data.docstatus==null) {
  267. data.docstatus_icon = 'icon-pencil';
  268. data.docstatus_title = wn._('Editable');
  269. } else if(data.docstatus==1) {
  270. data.docstatus_icon = 'icon-lock';
  271. data.docstatus_title = wn._('Submitted');
  272. } else if(data.docstatus==2) {
  273. data.docstatus_icon = 'icon-remove';
  274. data.docstatus_title = wn._('Cancelled');
  275. }
  276. // nulls as strings
  277. for(key in data) {
  278. if(data[key]==null) {
  279. data[key]='';
  280. }
  281. }
  282. // prepare data in settings
  283. if(this.settings.prepare_data)
  284. this.settings.prepare_data(data);
  285. },
  286. prepare_when: function(data, date_str) {
  287. if (!date_str) date_str = data.modified;
  288. // when
  289. data.when = (dateutil.str_to_user(date_str)).split(' ')[0];
  290. var diff = dateutil.get_diff(dateutil.get_today(), date_str.split(' ')[0]);
  291. if(diff==0) {
  292. data.when = dateutil.comment_when(date_str);
  293. }
  294. if(diff == 1) {
  295. data.when = wn._('Yesterday')
  296. }
  297. if(diff == 2) {
  298. data.when = wn._('2 days ago')
  299. }
  300. },
  301. add_user_tags: function(parent, data) {
  302. var me = this;
  303. if(data._user_tags) {
  304. $.each(data._user_tags.split(','), function(i, t) {
  305. if(t) {
  306. $('<span class="label" style="cursor: pointer;">'
  307. + strip(t) + '</span>')
  308. .click(function() {
  309. me.doclistview.set_filter('_user_tags', $(this).text())
  310. })
  311. .appendTo(parent);
  312. }
  313. });
  314. }
  315. },
  316. render_bar_graph: function(parent, data, field, label) {
  317. var args = {
  318. percent: data[field],
  319. fully_delivered: (data[field] > 99 ? 'bar-complete' : ''),
  320. label: label
  321. }
  322. $(parent).append(repl('<span class="bar-outer" style="width: 30px; float: right" \
  323. title="%(percent)s% %(label)s">\
  324. <span class="bar-inner %(fully_delivered)s" \
  325. style="width: %(percent)s%;"></span>\
  326. </span>', args));
  327. },
  328. render_icon: function(parent, icon_class, label) {
  329. var icon_html = "<i class='%(icon_class)s' title='%(label)s'></i>";
  330. $(parent).append(repl(icon_html, {icon_class: icon_class, label: label || ''}));
  331. }
  332. });
  333. // embeddable
  334. wn.provide('wn.views.RecordListView');
  335. wn.views.RecordListView = wn.views.DocListView.extend({
  336. init: function(doctype, wrapper, ListView) {
  337. this.doctype = doctype;
  338. this.wrapper = wrapper;
  339. this.listview = new ListView(this, doctype);
  340. this.listview.parent = this;
  341. this.setup();
  342. },
  343. setup: function() {
  344. var me = this;
  345. me.page_length = 10;
  346. $(me.wrapper).empty();
  347. me.init_list();
  348. },
  349. get_args: function() {
  350. var args = this._super();
  351. $.each((this.default_filters || []), function(i, f) {
  352. args.filters.push(f);
  353. });
  354. args.docstatus = args.docstatus.concat((this.default_docstatus || []));
  355. return args;
  356. },
  357. });