您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

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