You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

control.js 27 KiB

11 vuotta sitten
11 vuotta sitten
12 vuotta sitten
12 vuotta sitten
12 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
12 vuotta sitten
11 vuotta sitten
12 vuotta sitten
12 vuotta sitten
11 vuotta sitten
12 vuotta sitten
12 vuotta sitten
11 vuotta sitten
11 vuotta sitten
12 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
12 vuotta sitten
11 vuotta sitten
11 vuotta sitten
12 vuotta sitten
11 vuotta sitten
11 vuotta sitten
12 vuotta sitten
12 vuotta sitten
11 vuotta sitten
11 vuotta sitten
12 vuotta sitten
11 vuotta sitten
12 vuotta sitten
11 vuotta sitten
12 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
11 vuotta sitten
12 vuotta sitten
12 vuotta sitten
11 vuotta sitten
11 vuotta sitten
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017
  1. // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. // MIT License. See license.txt
  3. wn.ui.form.make_control = function(opts) {
  4. var control_class_name = "Control" + opts.df.fieldtype.replace(/ /g, "");
  5. if(wn.ui.form[control_class_name]) {
  6. return new wn.ui.form[control_class_name](opts);
  7. } else {
  8. console.log("Invalid Control Name: " + opts.df.fieldtype);
  9. }
  10. }
  11. // old style
  12. function make_field(docfield, doctype, parent, frm, in_grid, hide_label) { // Factory
  13. return wn.ui.form.make_control({
  14. df: docfield,
  15. doctype: doctype,
  16. parent: parent,
  17. hide_label: hide_label,
  18. frm: frm
  19. });
  20. }
  21. wn.ui.form.Control = Class.extend({
  22. init: function(opts) {
  23. $.extend(this, opts);
  24. this.make();
  25. // if developer_mode=1, show fieldname as tooltip
  26. if(wn.boot.profile && wn.boot.profile.name==="Administrator" &&
  27. wn.boot.developer_mode===1 && this.$wrapper) {
  28. this.$wrapper.attr("title", wn._(this.df.fieldname));
  29. }
  30. },
  31. make: function() {
  32. this.make_wrapper();
  33. this.wrapper = this.$wrapper.get(0);
  34. this.wrapper.fieldobj = this; // reference for event handlers
  35. },
  36. make_wrapper: function() {
  37. this.$wrapper = $("<div>").appendTo(this.parent);
  38. },
  39. // returns "Read", "Write" or "None"
  40. // as strings based on permissions
  41. get_status: function(explain) {
  42. if(!this.doctype)
  43. return "Write";
  44. return wn.perm.get_field_display_status(this.df,
  45. locals[this.doctype][this.docname], this.perm || this.frm.perm, explain);
  46. },
  47. refresh: function() {
  48. this.disp_status = this.get_status();
  49. this.$wrapper
  50. && this.$wrapper.toggle(this.disp_status!="None")
  51. && this.$wrapper.trigger("refresh");
  52. },
  53. get_doc: function() {
  54. return this.doctype && this.docname
  55. && locals[this.doctype] && locals[this.doctype][this.docname] || {};
  56. },
  57. parse_validate_and_set_in_model: function(value) {
  58. var me = this;
  59. this.inside_change_event = true;
  60. if(this.parse) value = this.parse(value);
  61. var set = function(value) {
  62. me.set_model_value(value);
  63. me.inside_change_event = false;
  64. me.set_mandatory && me.set_mandatory(value);
  65. }
  66. this.validate ? this.validate(value, set) : set(value);
  67. },
  68. get_parsed_value: function() {
  69. var me = this;
  70. return this.get_value ?
  71. (this.parse ? this.parse(this.get_value()) : this.get_value()) :
  72. undefined;
  73. },
  74. set_model_value: function(value) {
  75. if(wn.model.set_value(this.doctype, this.docname, this.df.fieldname,
  76. value, this.df.fieldtype)) {
  77. this.last_value = value;
  78. }
  79. },
  80. });
  81. wn.ui.form.ControlHTML = wn.ui.form.Control.extend({
  82. make: function() {
  83. this._super();
  84. var me = this;
  85. this.disp_area = this.wrapper;
  86. this.$wrapper.on("refresh", function() {
  87. if(me.df.options)
  88. me.$wrapper.html(me.df.options);
  89. })
  90. }
  91. });
  92. wn.ui.form.ControlImage = wn.ui.form.Control.extend({
  93. make: function() {
  94. this._super();
  95. var me = this;
  96. this.$wrapper
  97. .css({"margin-bottom": "10px", "margin-right": "15px", "float": "right",
  98. "text-align": "right", "max-width": "100%"})
  99. .on("refresh", function() {
  100. me.$wrapper.empty();
  101. if(me.df.options && me.frm.doc[me.df.options]) {
  102. $("<img src='"+me.frm.doc[me.df.options]+"' style='max-width: 70%;'>")
  103. .appendTo(me.$wrapper);
  104. } else {
  105. $("<div class='missing-image'><i class='icon-camera'></i></div>")
  106. .appendTo(me.$wrapper)
  107. }
  108. return false;
  109. });
  110. }
  111. });
  112. wn.ui.form.ControlInput = wn.ui.form.Control.extend({
  113. horizontal: true,
  114. make: function() {
  115. // parent element
  116. this._super();
  117. this.set_input_areas();
  118. // set description
  119. this.set_max_width();
  120. this.setup_update_on_refresh();
  121. },
  122. make_wrapper: function() {
  123. if(this.only_input) {
  124. this.$wrapper = $('<div class="form-group">').appendTo(this.parent);
  125. } else {
  126. this.$wrapper = $('<div class="form-horizontal">\
  127. <div class="form-group row" style="margin: 0px">\
  128. <label class="control-label small col-xs-'+(this.horizontal?"4":"12")
  129. +'" style="padding-right: 0px;"></label>\
  130. <div class="col-xs-'+(this.horizontal?"8":"12")+'">\
  131. <div class="control-input"></div>\
  132. <div class="control-value like-disabled-input" style="display: none;"></div>\
  133. <p class="help-box small text-muted"></p>\
  134. </div>\
  135. </div>\
  136. </div>').appendTo(this.parent);
  137. if(!this.horizontal) {
  138. this.$wrapper.removeClass("form-horizontal");
  139. }
  140. }
  141. },
  142. set_input_areas: function() {
  143. if(this.only_input) {
  144. this.input_area = this.wrapper;
  145. } else {
  146. this.label_area = this.label_span = this.$wrapper.find("label").get(0);
  147. this.input_area = this.$wrapper.find(".control-input").get(0);
  148. // keep a separate display area to rendered formatted values
  149. // like links, currencies, HTMLs etc.
  150. this.disp_area = this.$wrapper.find(".control-value").get(0);
  151. }
  152. },
  153. set_max_width: function() {
  154. if(this.horizontal) {
  155. this.$wrapper.css({"max-width": "600px"});
  156. }
  157. },
  158. // update input value, label, description
  159. // display (show/hide/read-only),
  160. // mandatory style on refresh
  161. setup_update_on_refresh: function() {
  162. var me = this;
  163. this.$wrapper.on("refresh", function() {
  164. if(me.disp_status != "None") {
  165. // refresh value
  166. if(me.doctype && me.docname) {
  167. me.value = wn.model.get_value(me.doctype, me.docname, me.df.fieldname);
  168. }
  169. if(me.disp_status=="Write") {
  170. me.disp_area && $(me.disp_area).toggle(false);
  171. $(me.input_area).toggle(true);
  172. $(me.input_area).find("input").prop("disabled", false);
  173. !me.has_input && me.make_input();
  174. if(me.doctype && me.docname)
  175. me.set_input(me.value);
  176. } else {
  177. $(me.input_area).toggle(false);
  178. $(me.input_area).find("input").prop("disabled", true);
  179. me.disp_area && $(me.disp_area)
  180. .toggle(true)
  181. .html(
  182. wn.format(me.value, me.df, {no_icon:true}, locals[me.doctype][me.name])
  183. );
  184. }
  185. me.set_description();
  186. me.set_label();
  187. me.set_mandatory(me.value);
  188. }
  189. return false;
  190. });
  191. },
  192. bind_change_event: function() {
  193. var me = this;
  194. this.$input && this.$input.on("change", this.change || function(e) {
  195. if(me.doctype && me.docname && me.get_value) {
  196. me.parse_validate_and_set_in_model(me.get_value());
  197. } else {
  198. // inline
  199. var value = me.get_value();
  200. if(me.parse) {
  201. value = me.parse(value);
  202. }
  203. if(me.validate) {
  204. me.validate(value, function(value1) {
  205. if(value !== value1)
  206. me.set_input(value1)
  207. });
  208. }
  209. }
  210. });
  211. },
  212. set_label: function(label) {
  213. if(label) this.df.label = label;
  214. if(this.only_input || this.df.label==this._label)
  215. return;
  216. // var icon = wn.ui.form.fieldtype_icons[this.df.fieldtype];
  217. // if(this.df.fieldtype==="Link") {
  218. // icon = wn.boot.doctype_icons[this.df.options];
  219. // } else if(this.df.link_doctype) {
  220. // icon = wn.boot.doctype_icons[this.df.link_doctype];
  221. // }
  222. var icon = "";
  223. this.label_span.innerHTML = (icon ? '<i class="'+icon+'"></i> ' : "") +
  224. wn._(this.df.label) || "&nbsp;";
  225. this._label = this.df.label;
  226. },
  227. set_description: function() {
  228. if(this.only_input || this.df.description===this._description)
  229. return;
  230. if(this.df.description) {
  231. this.$wrapper.find(".help-box").html(wn._(this.df.description));
  232. } else {
  233. this.set_empty_description();
  234. }
  235. this._description = this.df.description;
  236. },
  237. set_empty_description: function() {
  238. this.$wrapper.find(".help-box").html("");
  239. },
  240. set_mandatory: function(value) {
  241. this.$wrapper.toggleClass("has-error", (this.df.reqd
  242. && (value==null || value==="")) ? true : false);
  243. },
  244. });
  245. wn.ui.form.ControlData = wn.ui.form.ControlInput.extend({
  246. html_element: "input",
  247. input_type: "text",
  248. make_input: function() {
  249. this.$input = $("<"+ this.html_element +">")
  250. .attr("type", this.input_type)
  251. .addClass("input-with-feedback form-control")
  252. .prependTo(this.input_area)
  253. this.set_input_attributes();
  254. this.input = this.$input.get(0);
  255. this.has_input = true;
  256. this.bind_change_event();
  257. },
  258. set_input_attributes: function() {
  259. this.$input
  260. .attr("data-fieldtype", this.df.fieldtype)
  261. .attr("data-fieldname", this.df.fieldname)
  262. .attr("placeholder", this.df.placeholder || "")
  263. if(this.doctype)
  264. this.$input.attr("data-doctype", this.doctype);
  265. if(this.df.input_css)
  266. this.$input.css(this.df.input_css);
  267. },
  268. set_input: function(val) {
  269. this.$input.val(this.format_for_input(val));
  270. this.last_value = val;
  271. this.set_mandatory && this.set_mandatory(val);
  272. },
  273. get_value: function() {
  274. return this.$input ? this.$input.val() : undefined;
  275. },
  276. format_for_input: function(val) {
  277. return val==null ? "" : val;
  278. },
  279. validate: function(v, callback) {
  280. if(this.df.options == 'Phone') {
  281. if(v+''=='')return '';
  282. v1 = ''
  283. // phone may start with + and must only have numbers later, '-' and ' ' are stripped
  284. v = v.replace(/ /g, '').replace(/-/g, '').replace(/\(/g, '').replace(/\)/g, '');
  285. // allow initial +,0,00
  286. if(v && v.substr(0,1)=='+') {
  287. v1 = '+'; v = v.substr(1);
  288. }
  289. if(v && v.substr(0,2)=='00') {
  290. v1 += '00'; v = v.substr(2);
  291. }
  292. if(v && v.substr(0,1)=='0') {
  293. v1 += '0'; v = v.substr(1);
  294. }
  295. v1 += cint(v) + '';
  296. callback(v1);
  297. } else if(this.df.options == 'Email') {
  298. if(v+''=='')return '';
  299. if(!validate_email(v)) {
  300. msgprint(wn._("Invalid Email") + ": " + v);
  301. callback("");
  302. } else
  303. callback(v);
  304. } else {
  305. callback(v);
  306. }
  307. }
  308. });
  309. wn.ui.form.ControlReadOnly = wn.ui.form.ControlData.extend({
  310. get_status: function(explain) {
  311. var status = this._super(explain);
  312. if(status==="Write")
  313. status = "Read";
  314. return;
  315. }
  316. });
  317. wn.ui.form.ControlPassword = wn.ui.form.ControlData.extend({
  318. input_type: "password"
  319. });
  320. wn.ui.form.ControlInt = wn.ui.form.ControlData.extend({
  321. make_input: function() {
  322. var me = this;
  323. this._super();
  324. this.$input
  325. .css({"text-align": "right"})
  326. .on("focus", function() {
  327. setTimeout(function() {
  328. if(!document.activeElement) return;
  329. me.validate(document.activeElement.value, function(val) {
  330. document.activeElement.value = val;
  331. });
  332. document.activeElement.select()
  333. }, 100);
  334. return false;
  335. })
  336. },
  337. validate: function(value, callback) {
  338. return callback(cint(value, null));
  339. }
  340. });
  341. wn.ui.form.ControlFloat = wn.ui.form.ControlInt.extend({
  342. validate: function(value, callback) {
  343. return callback(isNaN(parseFloat(value)) ? null : flt(value));
  344. },
  345. format_for_input: function(value) {
  346. var formatted_value = format_number(parseFloat(value),
  347. null, cint(wn.boot.sysdefaults.float_precision, null));
  348. return isNaN(parseFloat(value)) ? "" : formatted_value;
  349. }
  350. });
  351. wn.ui.form.ControlCurrency = wn.ui.form.ControlFloat.extend({
  352. format_for_input: function(value) {
  353. var formatted_value = format_number(parseFloat(value),
  354. get_number_format(this.get_currency()));
  355. return isNaN(parseFloat(value)) ? "" : formatted_value;
  356. },
  357. get_currency: function() {
  358. return wn.meta.get_field_currency(this.df, this.get_doc());
  359. }
  360. });
  361. wn.ui.form.ControlPercent = wn.ui.form.ControlFloat;
  362. wn.ui.form.ControlDate = wn.ui.form.ControlData.extend({
  363. datepicker_options: {
  364. altFormat:'yy-mm-dd',
  365. changeYear: true,
  366. yearRange: "-70Y:+10Y",
  367. },
  368. make_input: function() {
  369. this._super();
  370. this.set_datepicker();
  371. },
  372. set_datepicker: function() {
  373. this.datepicker_options.dateFormat =
  374. (wn.boot.sysdefaults.date_format || 'yyyy-mm-dd').replace("yyyy", "yy")
  375. this.$input.datepicker(this.datepicker_options);
  376. },
  377. parse: function(value) {
  378. return value ? dateutil.user_to_str(value) : value;
  379. },
  380. format_for_input: function(value) {
  381. return value ? dateutil.str_to_user(value) : "";
  382. },
  383. validate: function(value, callback) {
  384. var value = wn.datetime.validate(value);
  385. if(!value) {
  386. msgprint (wn._("Date must be in format") + ": " + (sys_defaults.date_format || "yyyy-mm-dd"));
  387. callback("");
  388. }
  389. return callback(value);
  390. }
  391. })
  392. import_timepicker = function() {
  393. wn.require("assets/webnotes/js/lib/jquery/jquery.ui.slider.min.js");
  394. wn.require("assets/webnotes/js/lib/jquery/jquery.ui.sliderAccess.js");
  395. wn.require("assets/webnotes/js/lib/jquery/jquery.ui.timepicker-addon.css");
  396. wn.require("assets/webnotes/js/lib/jquery/jquery.ui.timepicker-addon.js");
  397. }
  398. wn.ui.form.ControlTime = wn.ui.form.ControlData.extend({
  399. make_input: function() {
  400. import_timepicker();
  401. this._super();
  402. this.$input.timepicker({
  403. timeFormat: 'hh:mm:ss',
  404. });
  405. }
  406. });
  407. wn.ui.form.ControlDatetime = wn.ui.form.ControlDate.extend({
  408. set_datepicker: function() {
  409. this.datepicker_options.timeFormat = "hh:mm:ss";
  410. this.datepicker_options.dateFormat =
  411. (wn.boot.sysdefaults.date_format || 'yy-mm-dd').replace('yyyy','yy');
  412. this.$input.datetimepicker(this.datepicker_options);
  413. },
  414. make_input: function() {
  415. import_timepicker();
  416. this._super();
  417. },
  418. });
  419. wn.ui.form.ControlText = wn.ui.form.ControlData.extend({
  420. html_element: "textarea",
  421. horizontal: false
  422. });
  423. wn.ui.form.ControlLongText = wn.ui.form.ControlText;
  424. wn.ui.form.ControlSmallText = wn.ui.form.ControlText;
  425. wn.ui.form.ControlCheck = wn.ui.form.ControlData.extend({
  426. input_type: "checkbox",
  427. make_wrapper: function() {
  428. this.$wrapper = $('<div class="form-group row" style="margin: 0px;">\
  429. <div class="col-md-offset-4 col-md-8">\
  430. <div class="checkbox" style="margin: 5px 0px">\
  431. <label>\
  432. <span class="input-area"></span>\
  433. <span class="disp-area" style="display:none;"></span>\
  434. <span class="label-area small"></span>\
  435. </label>\
  436. <p class="help-box small text-muted"></p>\
  437. </div>\
  438. </div>\
  439. </div>').appendTo(this.parent)
  440. },
  441. set_input_areas: function() {
  442. this.label_area = this.label_span = this.$wrapper.find(".label-area").get(0);
  443. this.input_area = this.$wrapper.find(".input-area").get(0);
  444. this.disp_area = this.$wrapper.find(".disp-area").get(0);
  445. },
  446. make_input: function() {
  447. this._super();
  448. this.$input.removeClass("form-control");
  449. },
  450. parse: function(value) {
  451. return this.input.checked ? 1 : 0;
  452. },
  453. validate: function(value, callback) {
  454. return callback(cint(value));
  455. },
  456. set_input: function(value) {
  457. this.input.checked = value ? 1 : 0;
  458. this.last_value = value;
  459. }
  460. });
  461. wn.ui.form.ControlButton = wn.ui.form.ControlData.extend({
  462. make_input: function() {
  463. var me = this;
  464. this.$input = $('<button class="btn btn-default">')
  465. .prependTo(me.input_area)
  466. .on("click", function() {
  467. me.onclick();
  468. });
  469. this.input = this.$input.get(0);
  470. this.set_input_attributes();
  471. this.has_input = true;
  472. },
  473. onclick: function() {
  474. if(this.frm && this.frm.doc && this.frm.cscript) {
  475. if(this.frm.cscript[this.df.fieldname]) {
  476. this.frm.script_manager.trigger(this.df.fieldname, this.doctype, this.docname);
  477. } else {
  478. this.frm.runscript(this.df.options, this);
  479. }
  480. }
  481. else if(this.df.click) {
  482. this.df.click();
  483. }
  484. },
  485. set_input_areas: function() {
  486. this._super();
  487. $(this.disp_area).removeClass();
  488. },
  489. set_empty_description: function() {
  490. this.$wrapper.find(".help-box").empty().toggle(false);
  491. },
  492. set_label: function() {
  493. $(this.label_span).html("&nbsp;");
  494. this.$input && this.$input.html(this.df.label);
  495. }
  496. });
  497. wn.ui.form.ControlAttach = wn.ui.form.ControlData.extend({
  498. make_input: function() {
  499. var me = this;
  500. this.$input = $('<button class="btn btn-default">')
  501. .html(wn._("Upload"))
  502. .prependTo(me.input_area)
  503. .on("click", function() {
  504. me.onclick();
  505. });
  506. this.$value = $('<div class="alert alert-info">\
  507. <a class="attached-file text-ellipsis" style="width: 80%" target="_blank"></a>\
  508. <a class="close">&times;</a></div>')
  509. .prependTo(me.input_area)
  510. .toggle(false);
  511. this.input = this.$input.get(0);
  512. this.set_input_attributes();
  513. this.has_input = true;
  514. this.$value.find(".close").on("click", function() {
  515. if(me.frm) {
  516. me.frm.attachments.remove_attachment_by_filename(me.value, function() {
  517. me.parse_validate_and_set_in_model(null);
  518. me.refresh();
  519. });
  520. } else {
  521. me.dataurl = null;
  522. me.fileobj = null;
  523. me.set_input(null);
  524. me.refresh();
  525. }
  526. })
  527. },
  528. onclick: function() {
  529. if(!this.dialog) {
  530. this.dialog = new wn.ui.Dialog({
  531. title: wn._(this.df.label || wn._("Upload")),
  532. });
  533. }
  534. $(this.dialog.body).empty();
  535. this.set_upload_options();
  536. wn.upload.make(this.upload_options);
  537. this.dialog.show();
  538. },
  539. set_upload_options: function() {
  540. var me = this;
  541. this.upload_options = {
  542. parent: this.dialog.body,
  543. args: {},
  544. max_width: this.df.max_width,
  545. max_height: this.df.max_height,
  546. callback: function(fileid, filename, r) {
  547. me.dialog.hide();
  548. me.on_upload_complete(fileid, filename, r);
  549. },
  550. onerror: function() {
  551. me.dialog.hide();
  552. },
  553. }
  554. if(this.frm) {
  555. this.upload_options.args = {
  556. from_form: 1,
  557. doctype: this.frm.doctype,
  558. docname: this.frm.docname,
  559. }
  560. } else {
  561. this.upload_options.on_attach = function(fileobj, dataurl) {
  562. me.dialog.hide();
  563. me.fileobj = fileobj;
  564. me.dataurl = dataurl;
  565. if(me.on_attach) {
  566. me.on_attach()
  567. }
  568. if(me.df.on_attach) {
  569. me.df.on_attach(fileobj, dataurl);
  570. }
  571. me.on_upload_complete();
  572. }
  573. }
  574. },
  575. set_input: function(value, dataurl) {
  576. this.value = value;
  577. if(this.value) {
  578. this.$input.toggle(false);
  579. this.$value.toggle(true).find(".attached-file")
  580. .html(this.value)
  581. .attr("href", dataurl || this.value);
  582. } else {
  583. this.$input.toggle(true);
  584. this.$value.toggle(false);
  585. }
  586. },
  587. get_value: function() {
  588. if(this.frm) {
  589. return this.value;
  590. } else {
  591. return this.fileobj ? (this.fileobj.filename + "," + this.dataurl) : null;
  592. }
  593. },
  594. on_upload_complete: function(fileid, filename, r) {
  595. if(this.frm) {
  596. this.parse_validate_and_set_in_model(filename);
  597. this.refresh();
  598. this.frm.attachments.update_attachment(fileid, filename, this.df.fieldname, r);
  599. } else {
  600. this.set_input(this.fileobj.filename, this.dataurl);
  601. this.refresh();
  602. }
  603. },
  604. });
  605. wn.ui.form.ControlAttachImage = wn.ui.form.ControlAttach.extend({
  606. make_input: function() {
  607. this._super();
  608. this.img = $("<img class='img-responsive'>").appendTo($('<div style="margin: 7px 0px;">\
  609. <div class="missing-image"><i class="icon-camera"></i></div></div>')
  610. .prependTo(this.input_area)).toggle(false);
  611. var me = this;
  612. this.$wrapper.on("refresh", function() {
  613. if(me.value) {
  614. $(me.input_area).find(".missing-image").toggle(false);
  615. me.img.attr("src", me.dataurl ? me.dataurl : me.value).toggle(true);
  616. } else {
  617. $(me.input_area).find(".missing-image").toggle(true);
  618. me.img.toggle(false);
  619. }
  620. });
  621. },
  622. });
  623. wn.ui.form.ControlSelect = wn.ui.form.ControlData.extend({
  624. html_element: "select",
  625. make_input: function() {
  626. var me = this;
  627. this._super();
  628. if(this.df.options=="attach_files:") {
  629. this.setup_attachment();
  630. }
  631. this.set_options();
  632. },
  633. set_input: function(value) {
  634. // refresh options first - (new ones??)
  635. this.set_options(value || "");
  636. this._super(value);
  637. // not a possible option, repair
  638. if(this.doctype && this.docname) {
  639. // model value is not an option,
  640. // set the default option (displayed)
  641. var input_value = this.$input.val();
  642. var model_value = wn.model.get_value(this.doctype, this.docname, this.df.fieldname);
  643. if(model_value == null && input_value != (model_value || "")) {
  644. this.set_model_value(input_value);
  645. } else {
  646. this.last_value = value;
  647. }
  648. }
  649. },
  650. setup_attachment: function() {
  651. var me = this;
  652. $(this.input).css({"width": "85%", "display": "inline-block"});
  653. this.$attach = $("<button class='btn btn-default' title='"+ wn._("Add attachment") + "'\
  654. style='padding-left: 6px; padding-right: 6px; margin-right: 6px;'>\
  655. <i class='icon-plus'></i></button>")
  656. .click(function() {
  657. me.frm.attachments.new_attachment(me.df.fieldname);
  658. })
  659. .prependTo(this.input_area);
  660. $(document).on("upload_complete", function(event, filename, file_url) {
  661. if(cur_frm === me.frm) {
  662. me.set_options();
  663. }
  664. })
  665. this.$wrapper.on("refresh", function() {
  666. me.$attach.toggle(!me.frm.doc.__islocal);
  667. });
  668. },
  669. set_options: function(value) {
  670. var options = this.df.options || [];
  671. if(this.df.options=="attach_files:") {
  672. options = this.get_file_attachment_list();
  673. } else if(typeof this.df.options==="string") {
  674. options = this.df.options.split("\n");
  675. }
  676. if(this.in_filter && options[0] != "") {
  677. options = add_lists([''], options);
  678. }
  679. var selected = this.$input.find(":selected").val();
  680. this.$input.empty().add_options(options || []);
  681. if(value===undefined && selected) this.$input.val(selected);
  682. },
  683. get_file_attachment_list: function() {
  684. if(!this.frm) return;
  685. var fl = wn.model.docinfo[this.frm.doctype][this.frm.docname];
  686. if(fl && fl.attachments) {
  687. fl = fl.attachments;
  688. this.set_description("");
  689. var options = [""];
  690. for(var fname in fl) {
  691. if(fname.indexOf("/")===-1)
  692. fname = "files/" + fname;
  693. options.push(fname);
  694. }
  695. return options;
  696. } else {
  697. this.set_description(wn._("Please attach a file first."))
  698. return [""];
  699. }
  700. }
  701. });
  702. // special features for link
  703. // buttons
  704. // autocomplete
  705. // link validation
  706. // custom queries
  707. // add_fetches
  708. wn.ui.form.ControlLink = wn.ui.form.ControlData.extend({
  709. make_input: function() {
  710. var me = this;
  711. $('<div class="link-field" style="display: table; width: 100%;">\
  712. <input type="text" class="input-with-feedback form-control" \
  713. style="display: table-cell">\
  714. <span class="link-field-btn" style="display: table-cell">\
  715. <a class="btn-search" title="Search Link">\
  716. <i class="icon-search"></i>\
  717. </a><a class="btn-open" title="Open Link">\
  718. <i class="icon-play"></i>\
  719. </a><a class="btn-new" title="Make New">\
  720. <i class="icon-plus"></i>\
  721. </a>\
  722. </span>\
  723. </div>').prependTo(this.input_area);
  724. this.$input_area = $(this.input_area);
  725. this.$input = this.$input_area.find('input');
  726. this.set_input_attributes();
  727. this.$input.on("focus", function() {
  728. setTimeout(function() {
  729. if(!me.$input.val()) {
  730. me.$input.val("%").trigger("keydown");
  731. }
  732. }, 1000)
  733. })
  734. this.input = this.$input.get(0);
  735. this.has_input = true;
  736. //this.bind_change_event();
  737. var me = this;
  738. this.setup_buttons();
  739. //this.setup_typeahead();
  740. this.setup_autocomplete();
  741. },
  742. setup_buttons: function() {
  743. var me = this;
  744. // magnifier - search
  745. this.$input_area.find(".btn-search").on("click", function() {
  746. new wn.ui.form.LinkSelector({
  747. doctype: me.df.options,
  748. target: me,
  749. txt: me.get_value()
  750. });
  751. });
  752. // open
  753. if(wn.model.can_read(me.df.options)) {
  754. this.$input_area.find(".btn-open").on("click", function() {
  755. var value = me.get_value();
  756. if(value && me.df.options) wn.set_route("Form", me.df.options, value);
  757. });
  758. } else {
  759. this.$input_area.find(".btn-open").remove();
  760. }
  761. // new
  762. if(wn.model.can_create(me.df.options)) {
  763. this.$input_area.find(".btn-new").on("click", function() {
  764. me.frm.new_doc(me.df.options, me);
  765. });
  766. } else {
  767. this.$input_area.find(".btn-new").remove();
  768. }
  769. if(this.only_input) this.$input_area.find(".btn-open, .btn-new").remove();
  770. },
  771. setup_autocomplete: function() {
  772. var me = this;
  773. this.$input.on("blur", function() {
  774. if(me.selected) {
  775. me.selected = false;
  776. return;
  777. }
  778. if(me.doctype && me.docname) {
  779. var value = me.get_value();
  780. if(value!==me.last_value) {
  781. me.parse_validate_and_set_in_model(value);
  782. }
  783. }});
  784. this.$input.autocomplete({
  785. source: function(request, response) {
  786. var args = {
  787. 'txt': request.term,
  788. 'doctype': me.df.options,
  789. };
  790. me.set_custom_query(args);
  791. return wn.call({
  792. type: "GET",
  793. method:'webnotes.widgets.search.search_link',
  794. no_spinner: true,
  795. args: args,
  796. callback: function(r) {
  797. response(r.results);
  798. },
  799. });
  800. },
  801. open: function(event, ui) {
  802. me.autocomplete_open = true;
  803. },
  804. close: function(event, ui) {
  805. me.autocomplete_open = false;
  806. },
  807. select: function(event, ui) {
  808. me.autocomplete_open = false;
  809. if(me.frm && me.frm.doc) {
  810. me.selected = true;
  811. me.parse_validate_and_set_in_model(ui.item.value);
  812. } else {
  813. me.$input.val(ui.item.value);
  814. me.$input.trigger("change");
  815. }
  816. }
  817. }).data('uiAutocomplete')._renderItem = function(ul, d) {
  818. var html = "";
  819. if(keys(d).length > 1) {
  820. d.info = $.map(d, function(val, key) { return ["value", "label"].indexOf(key)!==-1 ? null : val }).join(", ") || "";
  821. html = repl("<a>%(value)s<br><span class='text-muted'>%(info)s</span></a>", d);
  822. } else {
  823. html = "<a>" + d.value + "</a>";
  824. }
  825. return $('<li></li>')
  826. .data('item.autocomplete', d)
  827. .append(html)
  828. .appendTo(ul);
  829. };
  830. // remove accessibility span (for now)
  831. this.$wrapper.find(".ui-helper-hidden-accessible").remove();
  832. },
  833. set_custom_query: function(args) {
  834. var set_nulls = function(obj) {
  835. $.each(obj, function(key, value) {
  836. if(value!==undefined) {
  837. obj[key] = value || null;
  838. }
  839. });
  840. return obj;
  841. }
  842. if(this.get_query || this.df.get_query) {
  843. var get_query = this.get_query || this.df.get_query;
  844. if($.isPlainObject(get_query)) {
  845. $.extend(args, set_nulls(get_query));
  846. } else if(typeof(get_query)==="string") {
  847. args.query = get_query;
  848. } else {
  849. var q = (get_query)(this.frm && this.frm.doc, this.doctype, this.docname);
  850. if (typeof(q)==="string") {
  851. args.query = q;
  852. } else if($.isPlainObject(q)) {
  853. if(q.filters) {
  854. set_nulls(q.filters);
  855. }
  856. $.extend(args, q);
  857. }
  858. }
  859. }
  860. },
  861. validate: function(value, callback) {
  862. // validate the value just entered
  863. var me = this;
  864. if(this.df.options=="[Select]") {
  865. callback(value);
  866. return;
  867. }
  868. this.frm.script_manager.validate_link_and_fetch(this.df, this.docname, value, callback);
  869. },
  870. });
  871. wn.ui.form.ControlCode = wn.ui.form.ControlText.extend({
  872. make_input: function() {
  873. this._super();
  874. $(this.input_area).find("textarea").css({"height":"400px", "font-family": "Monaco, \"Courier New\", monospace"});
  875. }
  876. });
  877. wn.ui.form.ControlTextEditor = wn.ui.form.ControlCode.extend({
  878. editor_name: "bsEditor",
  879. horizontal: false,
  880. make_input: function() {
  881. $(this.input_area).css({"min-height":"360px"});
  882. var me = this;
  883. this.editor = new (wn.provide(this.editor_name))({
  884. parent: this.input_area,
  885. change: function(value) {
  886. me.parse_validate_and_set_in_model(value);
  887. },
  888. field: this
  889. });
  890. this.has_input = true;
  891. this.editor.editor.keypress("ctrl+s meta+s", function() {
  892. me.frm.save_or_update();
  893. });
  894. },
  895. get_value: function() {
  896. return this.editor.get_value();
  897. },
  898. set_input: function(value) {
  899. this.editor.set_input(value);
  900. this.last_value = value;
  901. }
  902. });
  903. wn.ui.form.ControlTable = wn.ui.form.Control.extend({
  904. make: function() {
  905. this._super();
  906. // add title if prev field is not column / section heading or html
  907. var prev_fieldtype = wn.model.get("DocField",
  908. {parent: this.frm.doctype, idx: this.df.idx-1})[0].fieldtype;
  909. if(["Column Break", "Section Break", "HTML"].indexOf(prev_fieldtype)===-1) {
  910. $("<label>" + this.df.label + "<label>").appendTo(this.wrapper);
  911. }
  912. this.grid = new wn.ui.form.Grid({
  913. frm: this.frm,
  914. df: this.df,
  915. perm: this.perm || this.frm.perm,
  916. parent: this.wrapper
  917. })
  918. if(this.frm)
  919. this.frm.grids[this.frm.grids.length] = this;
  920. // description
  921. if(this.df.description) {
  922. $('<p class="text-muted small">' + wn._(this.df.description) + '</p>')
  923. .appendTo(this.wrapper);
  924. }
  925. var me = this;
  926. this.$wrapper.on("refresh", function() {
  927. me.grid.refresh();
  928. return false;
  929. });
  930. }
  931. })
  932. wn.ui.form.fieldtype_icons = {
  933. "Date": "icon-calendar",
  934. "Time": "icon-time",
  935. "Datetime": "icon-time",
  936. "Code": "icon-code",
  937. "Select": "icon-flag"
  938. };