mirror of
https://github.com/frappe/frappe_docker.git
synced 2026-06-22 15:55:09 +00:00
3701 lines
No EOL
104 KiB
JavaScript
3701 lines
No EOL
104 KiB
JavaScript
|
|
|
|
frappe.provide('frappe.dom');
|
|
|
|
frappe.dom = {
|
|
id_count: 0,
|
|
freeze_count: 0,
|
|
by_id: function by_id(id) {
|
|
return document.getElementById(id);
|
|
},
|
|
set_unique_id: function set_unique_id(ele) {
|
|
var $ele = $(ele);
|
|
if ($ele.attr('id')) {
|
|
return $ele.attr('id');
|
|
}
|
|
var id = 'unique-' + frappe.dom.id_count;
|
|
$ele.attr('id', id);
|
|
frappe.dom.id_count++;
|
|
return id;
|
|
},
|
|
eval: function _eval(txt) {
|
|
if (!txt) return;
|
|
var el = document.createElement('script');
|
|
el.appendChild(document.createTextNode(txt));
|
|
|
|
document.getElementsByTagName('head')[0].appendChild(el);
|
|
},
|
|
remove_script_and_style: function remove_script_and_style(txt) {
|
|
var div = document.createElement('div');
|
|
div.innerHTML = txt;
|
|
var found = false;
|
|
["script", "style", "noscript", "title", "meta", "base", "head"].forEach(function (e, i) {
|
|
var elements = div.getElementsByTagName(e);
|
|
var i = elements.length;
|
|
while (i--) {
|
|
found = true;
|
|
elements[i].parentNode.removeChild(elements[i]);
|
|
}
|
|
});
|
|
|
|
var elements = div.getElementsByTagName('link');
|
|
var i = elements.length;
|
|
while (i--) {
|
|
if (elements[i].getAttribute("rel") == "stylesheet") {
|
|
found = true;
|
|
elements[i].parentNode.removeChild(elements[i]);
|
|
}
|
|
}
|
|
if (found) {
|
|
return div.innerHTML;
|
|
} else {
|
|
return txt;
|
|
}
|
|
},
|
|
is_element_in_viewport: function is_element_in_viewport(el) {
|
|
if (typeof jQuery === "function" && el instanceof jQuery) {
|
|
el = el[0];
|
|
}
|
|
|
|
var rect = el.getBoundingClientRect();
|
|
|
|
return rect.top >= 0 && rect.left >= 0;
|
|
},
|
|
|
|
set_style: function set_style(txt, id) {
|
|
if (!txt) return;
|
|
|
|
var se = document.createElement('style');
|
|
se.type = "text/css";
|
|
|
|
if (id) {
|
|
var element = document.getElementById(id);
|
|
if (element) {
|
|
element.parentNode.removeChild(element);
|
|
}
|
|
se.id = id;
|
|
}
|
|
|
|
if (se.styleSheet) {
|
|
se.styleSheet.cssText = txt;
|
|
} else {
|
|
se.appendChild(document.createTextNode(txt));
|
|
}
|
|
document.getElementsByTagName('head')[0].appendChild(se);
|
|
},
|
|
add: function add(parent, newtag, className, cs, innerHTML, onclick) {
|
|
if (parent && parent.substr) parent = frappe.dom.by_id(parent);
|
|
var c = document.createElement(newtag);
|
|
if (parent) parent.appendChild(c);
|
|
|
|
if (className) {
|
|
if (newtag.toLowerCase() == 'img') c.src = className;else c.className = className;
|
|
}
|
|
if (cs) frappe.dom.css(c, cs);
|
|
if (innerHTML) c.innerHTML = innerHTML;
|
|
if (onclick) c.onclick = onclick;
|
|
return c;
|
|
},
|
|
css: function css(ele, s) {
|
|
if (ele && s) {
|
|
$.extend(ele.style, s);
|
|
}
|
|
return ele;
|
|
},
|
|
freeze: function freeze(msg, css_class) {
|
|
if (!$('#freeze').length) {
|
|
var freeze = $('<div id="freeze" class="modal-backdrop fade"></div>').on("click", function () {
|
|
if (cur_frm && cur_frm.cur_grid) {
|
|
cur_frm.cur_grid.toggle_view();
|
|
return false;
|
|
}
|
|
}).appendTo("#body_div");
|
|
|
|
freeze.html(repl('<div class="freeze-message-container"><div class="freeze-message"><p class="lead">%(msg)s</p></div></div>', { msg: msg || "" }));
|
|
|
|
setTimeout(function () {
|
|
freeze.addClass("in");
|
|
}, 1);
|
|
} else {
|
|
$("#freeze").addClass("in");
|
|
}
|
|
|
|
if (css_class) {
|
|
$("#freeze").addClass(css_class);
|
|
}
|
|
|
|
frappe.dom.freeze_count++;
|
|
},
|
|
unfreeze: function unfreeze() {
|
|
if (!frappe.dom.freeze_count) return;
|
|
frappe.dom.freeze_count--;
|
|
if (!frappe.dom.freeze_count) {
|
|
var freeze = $('#freeze').removeClass("in").remove();
|
|
}
|
|
},
|
|
save_selection: function save_selection() {
|
|
if (window.getSelection) {
|
|
var sel = window.getSelection();
|
|
if (sel.getRangeAt && sel.rangeCount) {
|
|
var ranges = [];
|
|
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
|
|
ranges.push(sel.getRangeAt(i));
|
|
}
|
|
return ranges;
|
|
}
|
|
} else if (document.selection && document.selection.createRange) {
|
|
return document.selection.createRange();
|
|
}
|
|
return null;
|
|
},
|
|
restore_selection: function restore_selection(savedSel) {
|
|
if (savedSel) {
|
|
if (window.getSelection) {
|
|
var sel = window.getSelection();
|
|
sel.removeAllRanges();
|
|
for (var i = 0, len = savedSel.length; i < len; ++i) {
|
|
sel.addRange(savedSel[i]);
|
|
}
|
|
} else if (document.selection && savedSel.select) {
|
|
savedSel.select();
|
|
}
|
|
}
|
|
},
|
|
is_touchscreen: function is_touchscreen() {
|
|
return 'ontouchstart' in window;
|
|
}
|
|
};
|
|
|
|
frappe.ellipsis = function (text, max) {
|
|
if (!max) max = 20;
|
|
text = cstr(text);
|
|
if (text.length > max) {
|
|
text = text.substr(0, max) + '...';
|
|
}
|
|
return text;
|
|
};
|
|
|
|
frappe.run_serially = function (tasks) {
|
|
var result = Promise.resolve();
|
|
tasks.forEach(function (task) {
|
|
if (task) {
|
|
result = result.then ? result.then(task) : Promise.resolve();
|
|
}
|
|
});
|
|
return result;
|
|
};
|
|
|
|
frappe.timeout = function (seconds) {
|
|
return new Promise(function (resolve) {
|
|
setTimeout(function () {
|
|
return resolve();
|
|
}, seconds * 1000);
|
|
});
|
|
};
|
|
|
|
frappe.scrub = function (text) {
|
|
return text.replace(/ /g, "_").toLowerCase();
|
|
};
|
|
|
|
frappe.get_modal = function (title, content) {
|
|
return $(frappe.render_template("modal", { title: title, content: content })).appendTo(document.body);
|
|
};
|
|
|
|
(function ($) {
|
|
$.fn.add_options = function (options_list) {
|
|
for (var i = 0; i < options_list.length; i++) {
|
|
var v = options_list[i];
|
|
if (is_null(v)) {
|
|
var value = null;
|
|
var label = null;
|
|
} else {
|
|
var is_value_null = is_null(v.value);
|
|
var is_label_null = is_null(v.label);
|
|
|
|
if (is_value_null && is_label_null) {
|
|
var value = v;
|
|
var label = __(v);
|
|
} else {
|
|
var value = is_value_null ? "" : v.value;
|
|
var label = is_label_null ? __(value) : __(v.label);
|
|
}
|
|
}
|
|
$('<option>').html(cstr(label)).attr('value', value).appendTo(this);
|
|
}
|
|
|
|
this.selectedIndex = 0;
|
|
return $(this);
|
|
};
|
|
$.fn.set_working = function () {
|
|
this.prop('disabled', true);
|
|
};
|
|
$.fn.done_working = function () {
|
|
this.prop('disabled', false);
|
|
};
|
|
})(jQuery);
|
|
|
|
(function ($) {
|
|
function pasteIntoInput(el, text) {
|
|
el.focus();
|
|
if (typeof el.selectionStart == "number") {
|
|
var val = el.value;
|
|
var selStart = el.selectionStart;
|
|
el.value = val.slice(0, selStart) + text + val.slice(el.selectionEnd);
|
|
el.selectionEnd = el.selectionStart = selStart + text.length;
|
|
} else if (typeof document.selection != "undefined") {
|
|
var textRange = document.selection.createRange();
|
|
textRange.text = text;
|
|
textRange.collapse(false);
|
|
textRange.select();
|
|
}
|
|
}
|
|
|
|
function allowTabChar(el) {
|
|
$(el).keydown(function (e) {
|
|
if (e.which == 9) {
|
|
pasteIntoInput(this, "\t");
|
|
return false;
|
|
}
|
|
});
|
|
|
|
$(el).keypress(function (e) {
|
|
if (e.which == 9) {
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
|
|
$.fn.allowTabs = function () {
|
|
if (this.jquery) {
|
|
this.each(function () {
|
|
if (this.nodeType == 1) {
|
|
var nodeName = this.nodeName.toLowerCase();
|
|
if (nodeName == "textarea" || nodeName == "input" && this.type == "text") {
|
|
allowTabChar(this);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return this;
|
|
};
|
|
})(jQuery);frappe.templates['modal'] = '<div class="modal fade" style="overflow: auto;" tabindex="-1"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <div class="row"> <div class="col-xs-7"> <span class="indicator hidden"></span> <h4 class="modal-title" style="font-weight: bold;">{%= title %}</h4> </div> <div class="col-xs-5"> <div class="text-right buttons"> <button type="button" class="btn btn-default btn-sm btn-modal-close" data-dismiss="modal"> <i class="octicon octicon-x visible-xs" style="padding: 1px 0px;"></i> <span class="hidden-xs">{%= __("Close") %}</span></button> <button type="button" class="btn btn-primary btn-sm hide"> {%= __("Confirm") %}</button> </div> </div> </div> </div> <div class="modal-body ui-front">{%= content %} </div> </div> </div> </div> ';
|
|
|
|
|
|
frappe.provide("frappe.form.formatters");
|
|
|
|
frappe.form.link_formatters = {};
|
|
|
|
frappe.form.formatters = {
|
|
_right: function _right(value, options) {
|
|
if (options && options.inline) {
|
|
return value;
|
|
} else {
|
|
return "<div style='text-align: right'>" + value + "</div>";
|
|
}
|
|
},
|
|
Data: function Data(value) {
|
|
return value == null ? "" : value;
|
|
},
|
|
Select: function Select(value) {
|
|
return __(frappe.form.formatters["Data"](value));
|
|
},
|
|
Float: function Float(value, docfield, options, doc) {
|
|
var precision = docfield.precision || cint(frappe.boot.sysdefaults.float_precision) || null;
|
|
if (docfield.options && docfield.options.trim()) {
|
|
docfield.precision = precision;
|
|
return frappe.form.formatters.Currency(value, docfield, options, doc);
|
|
} else {
|
|
if (!(options || {}).always_show_decimals && !is_null(value)) {
|
|
var temp = cstr(value).split(".");
|
|
if (temp[1] == undefined || cint(temp[1]) === 0) {
|
|
precision = 0;
|
|
}
|
|
}
|
|
|
|
return frappe.form.formatters._right(value == null || value === "" ? "" : format_number(value, null, precision), options);
|
|
}
|
|
},
|
|
Int: function Int(value, docfield, options) {
|
|
return frappe.form.formatters._right(value == null ? "" : cint(value), options);
|
|
},
|
|
Percent: function Percent(value, docfield, options) {
|
|
return frappe.form.formatters._right(flt(value, 2) + "%", options);
|
|
},
|
|
Currency: function Currency(value, docfield, options, doc) {
|
|
var currency = frappe.meta.get_field_currency(docfield, doc);
|
|
var precision = docfield.precision || cint(frappe.boot.sysdefaults.currency_precision) || 2;
|
|
if (precision > 2) {
|
|
var parts = cstr(value).split('.');
|
|
var decimals = parts.length > 1 ? parts[1] : '';
|
|
if (decimals.length < 3) {
|
|
precision = 2;
|
|
} else if (decimals.length < precision) {
|
|
precision = decimals.length;
|
|
}
|
|
}
|
|
value = value == null || value === "" ? "" : format_currency(value, currency, precision);
|
|
if (options && options.only_value) {
|
|
return value;
|
|
} else {
|
|
return frappe.form.formatters._right(value, options);
|
|
}
|
|
},
|
|
Check: function Check(value) {
|
|
if (value) {
|
|
return '<i class="octicon octicon-check" style="margin-right: 3px;"></i>';
|
|
} else {
|
|
return '<i class="fa fa-square disabled-check"></i>';
|
|
}
|
|
},
|
|
Link: function Link(value, docfield, options, doc) {
|
|
var doctype = docfield._options || docfield.options;
|
|
var original_value = value;
|
|
if (value && value.match(/^['"].*['"]$/)) {
|
|
value.replace(/^.(.*).$/, "$1");
|
|
}
|
|
|
|
if (options && (options.for_print || options.only_value)) {
|
|
return value;
|
|
}
|
|
|
|
if (frappe.form.link_formatters[doctype]) {
|
|
value = frappe.form.link_formatters[doctype](value, doc);
|
|
}
|
|
|
|
if (!value) {
|
|
return "";
|
|
}
|
|
if (value[0] == "'" && value[value.length - 1] == "'") {
|
|
return value.substring(1, value.length - 1);
|
|
}
|
|
if (docfield && docfield.link_onclick) {
|
|
return repl('<a onclick="%(onclick)s">%(value)s</a>', { onclick: docfield.link_onclick.replace(/"/g, '"'), value: value });
|
|
} else if (docfield && doctype) {
|
|
return repl('<a class="grey" href="#Form/%(doctype)s/%(name)s" data-doctype="%(doctype)s">%(label)s</a>', {
|
|
doctype: encodeURIComponent(doctype),
|
|
name: encodeURIComponent(original_value),
|
|
label: __(options && options.label || value)
|
|
});
|
|
} else {
|
|
return value;
|
|
}
|
|
},
|
|
Date: function Date(value) {
|
|
if (value) {
|
|
value = frappe.datetime.str_to_user(value);
|
|
|
|
if (value === "Invalid date") {
|
|
value = null;
|
|
}
|
|
}
|
|
|
|
return value || "";
|
|
},
|
|
DateRange: function DateRange(value) {
|
|
if ($.isArray(value)) {
|
|
return __("{0} to {1}").format([frappe.datetime.str_to_user(value[0]), frappe.datetime.str_to_user(value[1])]);
|
|
} else {
|
|
return value || "";
|
|
}
|
|
},
|
|
Datetime: function Datetime(value) {
|
|
if (value) {
|
|
var m = moment(frappe.datetime.convert_to_user_tz(value));
|
|
if (frappe.boot.sysdefaults.time_zone) {
|
|
m = m.tz(frappe.boot.sysdefaults.time_zone);
|
|
}
|
|
return m.format(frappe.boot.sysdefaults.date_format.toUpperCase() + ', h:mm a z');
|
|
} else {
|
|
return "";
|
|
}
|
|
},
|
|
Text: function Text(value) {
|
|
if (value) {
|
|
var tags = ["<p", "<div", "<br", "<table"];
|
|
var match = false;
|
|
|
|
for (var i = 0; i < tags.length; i++) {
|
|
if (value.match(tags[i])) {
|
|
match = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!match) {
|
|
value = replace_newlines(value);
|
|
}
|
|
}
|
|
|
|
return frappe.form.formatters.Data(value);
|
|
},
|
|
LikedBy: function LikedBy(value) {
|
|
var html = "";
|
|
$.each(JSON.parse(value || "[]"), function (i, v) {
|
|
if (v) html += frappe.avatar(v);
|
|
});
|
|
return html;
|
|
},
|
|
Tag: function Tag(value) {
|
|
var html = "";
|
|
$.each((value || "").split(","), function (i, v) {
|
|
if (v) html += '<span class="label label-info" \
|
|
style="margin-right: 7px; cursor: pointer;"\
|
|
data-field="_user_tags" data-label="' + v + '">' + v + '</span>';
|
|
});
|
|
return html;
|
|
},
|
|
Comment: function Comment(value) {
|
|
var html = "";
|
|
$.each(JSON.parse(value || "[]"), function (i, v) {
|
|
if (v) html += '<span class="label label-warning" \
|
|
style="margin-right: 7px;"\
|
|
data-field="_comments" data-label="' + v.name + '">' + v.comment + '</span>';
|
|
});
|
|
return html;
|
|
},
|
|
Assign: function Assign(value) {
|
|
var html = "";
|
|
$.each(JSON.parse(value || "[]"), function (i, v) {
|
|
if (v) html += '<span class="label label-warning" \
|
|
style="margin-right: 7px;"\
|
|
data-field="_assign">' + v + '</span>';
|
|
});
|
|
return html;
|
|
},
|
|
SmallText: function SmallText(value) {
|
|
return frappe.form.formatters.Text(value);
|
|
},
|
|
TextEditor: function TextEditor(value) {
|
|
return frappe.form.formatters.Text(value);
|
|
},
|
|
Code: function Code(value) {
|
|
return "<pre>" + (value == null ? "" : $("<div>").text(value).html()) + "</pre>";
|
|
},
|
|
WorkflowState: function WorkflowState(value) {
|
|
var workflow_state = frappe.get_doc("Workflow State", value);
|
|
if (workflow_state) {
|
|
return repl("<span class='label label-%(style)s' \
|
|
data-workflow-state='%(value)s'\
|
|
style='padding-bottom: 4px; cursor: pointer;'>\
|
|
<i class='fa fa-small fa-white fa-%(icon)s'></i> %(value)s</span>", {
|
|
value: value,
|
|
style: workflow_state.style.toLowerCase(),
|
|
icon: workflow_state.icon
|
|
});
|
|
} else {
|
|
return "<span class='label'>" + value + "</span>";
|
|
}
|
|
},
|
|
Email: function Email(value) {
|
|
return $("<div></div>").text(value).html();
|
|
}
|
|
};
|
|
|
|
frappe.form.get_formatter = function (fieldtype) {
|
|
if (!fieldtype) fieldtype = "Data";
|
|
return frappe.form.formatters[fieldtype.replace(/ /g, "")] || frappe.form.formatters.Data;
|
|
};
|
|
|
|
frappe.format = function (value, df, options, doc) {
|
|
if (!df) df = { "fieldtype": "Data" };
|
|
var fieldtype = df.fieldtype || "Data";
|
|
|
|
if (fieldtype === "Dynamic Link") {
|
|
fieldtype = "Link";
|
|
df._options = doc ? doc[df.options] : null;
|
|
}
|
|
|
|
var formatter = df.formatter || frappe.form.get_formatter(fieldtype);
|
|
|
|
var formatted = formatter(value, df, options, doc);
|
|
|
|
if (typeof formatted == "string") formatted = frappe.dom.remove_script_and_style(formatted);
|
|
|
|
return formatted;
|
|
};
|
|
|
|
frappe.get_format_helper = function (doc) {
|
|
var helper = {
|
|
get_formatted: function get_formatted(fieldname) {
|
|
var df = frappe.meta.get_docfield(doc.doctype, fieldname);
|
|
if (!df) {
|
|
console.log("fieldname not found: " + fieldname);
|
|
}
|
|
return frappe.format(doc[fieldname], df, { inline: 1 }, doc);
|
|
}
|
|
};
|
|
$.extend(helper, doc);
|
|
return helper;
|
|
};
|
|
|
|
frappe.provide("frappe.ui.form");
|
|
|
|
frappe.ui.form.Layout = Class.extend({
|
|
init: function init(opts) {
|
|
this.views = {};
|
|
this.pages = [];
|
|
this.sections = [];
|
|
this.fields_list = [];
|
|
this.fields_dict = {};
|
|
|
|
$.extend(this, opts);
|
|
},
|
|
make: function make() {
|
|
if (!this.parent && this.body) {
|
|
this.parent = this.body;
|
|
}
|
|
this.wrapper = $('<div class="form-layout">').appendTo(this.parent);
|
|
this.message = $('<div class="form-message text-muted small hidden"></div>').appendTo(this.wrapper);
|
|
if (!this.fields) {
|
|
this.fields = frappe.meta.sort_docfields(frappe.meta.docfield_map[this.doctype]);
|
|
}
|
|
this.setup_tabbing();
|
|
this.render();
|
|
},
|
|
show_empty_form_message: function show_empty_form_message() {
|
|
if (!(this.wrapper.find(".frappe-control:visible").length || this.wrapper.find(".section-head.collapsed").length)) {
|
|
this.show_message(__("This form does not have any input"));
|
|
}
|
|
},
|
|
show_message: function show_message(html) {
|
|
if (html) {
|
|
if (html.substr(0, 1) !== '<') {
|
|
html = '<div>' + html + '</div>';
|
|
}
|
|
$(html).appendTo(this.message.removeClass('hidden'));
|
|
} else {
|
|
this.message.empty().addClass('hidden');
|
|
}
|
|
},
|
|
render: function render(new_fields) {
|
|
var me = this;
|
|
var fields = new_fields || this.fields;
|
|
|
|
this.section = null;
|
|
this.column = null;
|
|
|
|
if (this.with_dashboard) {
|
|
this.setup_dashboard_section();
|
|
}
|
|
|
|
if (this.no_opening_section()) {
|
|
this.make_section();
|
|
}
|
|
$.each(fields, function (i, df) {
|
|
switch (df.fieldtype) {
|
|
case "Fold":
|
|
me.make_page(df);
|
|
break;
|
|
case "Section Break":
|
|
me.make_section(df);
|
|
break;
|
|
case "Column Break":
|
|
me.make_column(df);
|
|
break;
|
|
default:
|
|
me.make_field(df);
|
|
}
|
|
});
|
|
},
|
|
|
|
no_opening_section: function no_opening_section() {
|
|
return this.fields[0] && this.fields[0].fieldtype != "Section Break" || !this.fields.length;
|
|
},
|
|
|
|
setup_dashboard_section: function setup_dashboard_section() {
|
|
if (this.no_opening_section()) {
|
|
this.fields.unshift({ fieldtype: 'Section Break' });
|
|
}
|
|
|
|
this.fields.unshift({
|
|
fieldtype: 'Section Break',
|
|
fieldname: '_form_dashboard',
|
|
label: __('Dashboard'),
|
|
cssClass: 'form-dashboard',
|
|
collapsible: 1
|
|
});
|
|
},
|
|
|
|
make_field: function make_field(df, colspan) {
|
|
var render = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
|
|
!this.section && this.make_section();
|
|
!this.column && this.make_column();
|
|
|
|
var fieldobj = frappe.ui.form.make_control({
|
|
df: df,
|
|
doctype: this.doctype,
|
|
parent: this.column.wrapper.get(0),
|
|
frm: this.frm,
|
|
render_input: render
|
|
});
|
|
|
|
fieldobj.layout = this;
|
|
this.fields_list.push(fieldobj);
|
|
this.fields_dict[df.fieldname] = fieldobj;
|
|
if (this.frm) {
|
|
fieldobj.perm = this.frm.perm;
|
|
}
|
|
|
|
this.section.fields_list.push(fieldobj);
|
|
this.section.fields_dict[df.fieldname] = fieldobj;
|
|
},
|
|
make_page: function make_page(df) {
|
|
var me = this,
|
|
head = $('<div class="form-clickable-section text-center">\
|
|
<a class="btn-fold h6 text-muted">' + __("Show more details") + '</a>\
|
|
</div>').appendTo(this.wrapper);
|
|
|
|
this.page = $('<div class="form-page second-page hide"></div>').appendTo(this.wrapper);
|
|
|
|
this.fold_btn = head.find(".btn-fold").on("click", function () {
|
|
var page = $(this).parent().next();
|
|
if (page.hasClass("hide")) {
|
|
$(this).removeClass("btn-fold").html(__("Hide details"));
|
|
page.removeClass("hide");
|
|
frappe.utils.scroll_to($(this), true, 30);
|
|
me.folded = false;
|
|
} else {
|
|
$(this).addClass("btn-fold").html(__("Show more details"));
|
|
page.addClass("hide");
|
|
me.folded = true;
|
|
}
|
|
});
|
|
|
|
this.section = null;
|
|
this.folded = true;
|
|
},
|
|
|
|
unfold: function unfold() {
|
|
this.fold_btn.trigger('click');
|
|
},
|
|
|
|
make_section: function make_section(df) {
|
|
this.section = new frappe.ui.form.Section(this, df);
|
|
|
|
if (df) {
|
|
this.fields_dict[df.fieldname] = this.section;
|
|
this.fields_list.push(this.section);
|
|
}
|
|
|
|
this.column = null;
|
|
},
|
|
|
|
make_column: function make_column(df) {
|
|
this.column = new frappe.ui.form.Column(this.section, df);
|
|
if (df && df.fieldname) {
|
|
this.fields_list.push(this.column);
|
|
}
|
|
},
|
|
|
|
refresh: function refresh(doc) {
|
|
var me = this;
|
|
if (doc) this.doc = doc;
|
|
|
|
if (this.frm) {
|
|
this.wrapper.find(".empty-form-alert").remove();
|
|
}
|
|
|
|
me.attach_doc_and_docfields(true);
|
|
|
|
if (this.frm && this.frm.wrapper) {
|
|
$(this.frm.wrapper).trigger("refresh-fields");
|
|
}
|
|
|
|
this.refresh_dependency();
|
|
|
|
this.refresh_sections();
|
|
|
|
if (this.frm) {
|
|
this.refresh_section_collapse();
|
|
}
|
|
},
|
|
|
|
refresh_sections: function refresh_sections() {
|
|
var cnt = 0;
|
|
|
|
this.wrapper.find(".form-section:not(.hide-control)").each(function () {
|
|
var $this = $(this).removeClass("empty-section").removeClass("visible-section").removeClass("shaded-section");
|
|
if (!$this.find(".frappe-control:not(.hide-control)").length && !$this.hasClass('form-dashboard')) {
|
|
$this.addClass("empty-section");
|
|
} else {
|
|
$this.addClass("visible-section");
|
|
if (cnt % 2) {
|
|
$this.addClass("shaded-section");
|
|
}
|
|
cnt++;
|
|
}
|
|
});
|
|
},
|
|
|
|
refresh_section_collapse: function refresh_section_collapse() {
|
|
if (!this.doc) return;
|
|
|
|
for (var i = 0; i < this.sections.length; i++) {
|
|
var section = this.sections[i];
|
|
var df = section.df;
|
|
if (df && df.collapsible) {
|
|
var collapse = true;
|
|
|
|
if (df.collapsible_depends_on) {
|
|
collapse = !this.evaluate_depends_on_value(df.collapsible_depends_on);
|
|
}
|
|
|
|
if (collapse && section.has_missing_mandatory()) {
|
|
collapse = false;
|
|
}
|
|
|
|
if (df.fieldname === '_form_dashboard') {
|
|
collapse = false;
|
|
}
|
|
|
|
section.collapse(collapse);
|
|
}
|
|
}
|
|
},
|
|
|
|
attach_doc_and_docfields: function attach_doc_and_docfields(refresh) {
|
|
var me = this;
|
|
for (var i = 0, l = this.fields_list.length; i < l; i++) {
|
|
var fieldobj = this.fields_list[i];
|
|
if (me.doc) {
|
|
fieldobj.doc = me.doc;
|
|
fieldobj.doctype = me.doc.doctype;
|
|
fieldobj.docname = me.doc.name;
|
|
fieldobj.df = frappe.meta.get_docfield(me.doc.doctype, fieldobj.df.fieldname, me.frm ? me.frm.doc.name : me.doc.name) || fieldobj.df;
|
|
|
|
if (me.frm) {
|
|
fieldobj.perm = me.frm.perm;
|
|
}
|
|
}
|
|
refresh && fieldobj.refresh && fieldobj.refresh();
|
|
}
|
|
},
|
|
|
|
refresh_section_count: function refresh_section_count() {
|
|
this.wrapper.find(".section-count-label:visible").each(function (i) {
|
|
$(this).html(i + 1);
|
|
});
|
|
},
|
|
setup_tabbing: function setup_tabbing() {
|
|
var me = this;
|
|
this.wrapper.on("keydown", function (ev) {
|
|
if (ev.which == 9) {
|
|
var current = $(ev.target),
|
|
doctype = current.attr("data-doctype"),
|
|
fieldname = current.attr("data-fieldname");
|
|
if (doctype) return me.handle_tab(doctype, fieldname, ev.shiftKey);
|
|
}
|
|
});
|
|
},
|
|
handle_tab: function handle_tab(doctype, fieldname, shift) {
|
|
var me = this,
|
|
grid_row = null,
|
|
prev = null,
|
|
fields = me.fields_list,
|
|
in_grid = false,
|
|
focused = false;
|
|
|
|
if (doctype != me.doctype) {
|
|
grid_row = me.get_open_grid_row();
|
|
if (!grid_row || !grid_row.layout) {
|
|
return;
|
|
}
|
|
fields = grid_row.layout.fields_list;
|
|
}
|
|
|
|
for (var i = 0, len = fields.length; i < len; i++) {
|
|
if (fields[i].df.fieldname == fieldname) {
|
|
if (shift) {
|
|
if (prev) {
|
|
this.set_focus(prev);
|
|
} else {
|
|
$(this.primary_button).focus();
|
|
}
|
|
break;
|
|
}
|
|
if (i < len - 1) {
|
|
focused = me.focus_on_next_field(i, fields);
|
|
}
|
|
|
|
if (focused) {
|
|
break;
|
|
}
|
|
}
|
|
if (this.is_visible(fields[i])) prev = fields[i];
|
|
}
|
|
|
|
if (!focused) {
|
|
if (grid_row) {
|
|
if (grid_row.doc.idx == grid_row.grid.grid_rows.length) {
|
|
grid_row.toggle_view(false, function () {
|
|
grid_row.grid.frm.layout.handle_tab(grid_row.grid.df.parent, grid_row.grid.df.fieldname);
|
|
});
|
|
} else {
|
|
grid_row.grid.grid_rows[grid_row.doc.idx].toggle_view(true);
|
|
}
|
|
} else {
|
|
$(this.primary_button).focus();
|
|
}
|
|
}
|
|
|
|
return false;
|
|
},
|
|
focus_on_next_field: function focus_on_next_field(start_idx, fields) {
|
|
for (var i = start_idx + 1, len = fields.length; i < len; i++) {
|
|
var field = fields[i];
|
|
if (this.is_visible(field)) {
|
|
if (field.df.fieldtype === "Table") {
|
|
if (!(field.grid.grid_rows && field.grid.grid_rows.length)) {
|
|
field.grid.add_new_row();
|
|
}
|
|
|
|
field.grid.grid_rows[0].show_form();
|
|
return true;
|
|
} else if (!in_list(frappe.model.no_value_type, field.df.fieldtype)) {
|
|
this.set_focus(field);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
is_visible: function is_visible(field) {
|
|
return field.disp_status === "Write" && field.$wrapper && field.$wrapper.is(":visible");
|
|
},
|
|
set_focus: function set_focus(field) {
|
|
if (field.df.fieldtype == "Table") {
|
|
if (!field.grid.grid_rows.length) {
|
|
field.grid.add_new_row(1);
|
|
} else {
|
|
field.grid.grid_rows[0].toggle_view(true);
|
|
}
|
|
} else if (field.editor) {
|
|
field.editor.set_focus();
|
|
} else if (field.$input) {
|
|
field.$input.focus();
|
|
}
|
|
},
|
|
get_open_grid_row: function get_open_grid_row() {
|
|
return $(".grid-row-open").data("grid_row");
|
|
},
|
|
refresh_dependency: function refresh_dependency() {
|
|
var me = this;
|
|
|
|
var has_dep = false;
|
|
|
|
for (var fkey in this.fields_list) {
|
|
var f = this.fields_list[fkey];
|
|
f.dependencies_clear = true;
|
|
if (f.df.depends_on) {
|
|
has_dep = true;
|
|
}
|
|
}
|
|
|
|
if (!has_dep) return;
|
|
|
|
for (var i = me.fields_list.length - 1; i >= 0; i--) {
|
|
var f = me.fields_list[i];
|
|
f.guardian_has_value = true;
|
|
if (f.df.depends_on) {
|
|
|
|
f.guardian_has_value = this.evaluate_depends_on_value(f.df.depends_on);
|
|
|
|
if (f.guardian_has_value) {
|
|
if (f.df.hidden_due_to_dependency) {
|
|
f.df.hidden_due_to_dependency = false;
|
|
f.refresh();
|
|
}
|
|
} else {
|
|
if (!f.df.hidden_due_to_dependency) {
|
|
f.df.hidden_due_to_dependency = true;
|
|
f.refresh();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.refresh_section_count();
|
|
},
|
|
evaluate_depends_on_value: function evaluate_depends_on_value(expression) {
|
|
var out = null;
|
|
var doc = this.doc;
|
|
|
|
if (!doc && this.get_values) {
|
|
var doc = this.get_values(true);
|
|
}
|
|
|
|
if (!doc) {
|
|
return;
|
|
}
|
|
|
|
var parent = this.frm ? this.frm.doc : null;
|
|
|
|
if (expression.substr(0, 5) == 'eval:') {
|
|
out = eval(expression.substr(5));
|
|
} else if (expression.substr(0, 3) == 'fn:' && this.frm) {
|
|
out = this.frm.script_manager.trigger(expression.substr(3), this.doctype, this.docname);
|
|
} else {
|
|
var value = doc[expression];
|
|
if ($.isArray(value)) {
|
|
out = !!value.length;
|
|
} else {
|
|
out = !!value;
|
|
}
|
|
}
|
|
|
|
return out;
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.Section = Class.extend({
|
|
init: function init(layout, df) {
|
|
var me = this;
|
|
this.layout = layout;
|
|
this.df = df || {};
|
|
this.fields_list = [];
|
|
this.fields_dict = {};
|
|
|
|
this.make();
|
|
|
|
this.row = {
|
|
wrapper: this.wrapper
|
|
};
|
|
|
|
if (this.df.collapsible) {
|
|
this.collapse(true);
|
|
}
|
|
|
|
this.refresh();
|
|
},
|
|
make: function make() {
|
|
if (!this.layout.page) {
|
|
this.layout.page = $('<div class="form-page"></div>').appendTo(this.layout.wrapper);
|
|
}
|
|
|
|
this.wrapper = $('<div class="row form-section">').appendTo(this.layout.page);
|
|
this.layout.sections.push(this);
|
|
|
|
if (this.df) {
|
|
if (this.df.label) {
|
|
this.make_head();
|
|
}
|
|
if (this.df.description) {
|
|
$('<div class="col-sm-12 small text-muted form-section-description">' + __(this.df.description) + '</div>').appendTo(this.wrapper);
|
|
}
|
|
if (this.df.cssClass) {
|
|
this.wrapper.addClass(this.df.cssClass);
|
|
}
|
|
}
|
|
|
|
this.body = $('<div class="section-body">').appendTo(this.wrapper);
|
|
},
|
|
make_head: function make_head() {
|
|
var me = this;
|
|
if (!this.df.collapsible) {
|
|
$('<div class="col-sm-12"><h6 class="form-section-heading uppercase">' + __(this.df.label) + '</h6></div>').appendTo(this.wrapper);
|
|
} else {
|
|
this.head = $('<div class="section-head"><a class="h6 uppercase">' + __(this.df.label) + '</a><span class="octicon octicon-chevron-down collapse-indicator"></span></div>').appendTo(this.wrapper);
|
|
|
|
this.collapse_link = this.head.on("click", function () {
|
|
me.collapse();
|
|
});
|
|
|
|
this.indicator = this.head.find(".collapse-indicator");
|
|
}
|
|
},
|
|
refresh: function refresh() {
|
|
if (!this.df) return;
|
|
|
|
var hide = this.df.hidden || this.df.hidden_due_to_dependency;
|
|
|
|
if (!hide && this.layout && this.layout.frm && !this.layout.frm.get_perm(this.df.permlevel || 0, "read")) {
|
|
hide = true;
|
|
}
|
|
|
|
this.wrapper.toggleClass("hide-control", !!hide);
|
|
},
|
|
collapse: function collapse(hide) {
|
|
if (!(this.head && this.body)) {
|
|
return;
|
|
}
|
|
|
|
if (hide === undefined) {
|
|
hide = !this.body.hasClass("hide");
|
|
}
|
|
this.body.toggleClass("hide", hide);
|
|
this.head.toggleClass("collapsed", hide);
|
|
this.indicator.toggleClass("octicon-chevron-down", hide);
|
|
this.indicator.toggleClass("octicon-chevron-up", !hide);
|
|
},
|
|
has_missing_mandatory: function has_missing_mandatory() {
|
|
var missing_mandatory = false;
|
|
for (var j = 0, l = this.fields_list.length; j < l; j++) {
|
|
var section_df = this.fields_list[j].df;
|
|
if (section_df.reqd && this.layout.doc[section_df.fieldname] == null) {
|
|
missing_mandatory = true;
|
|
break;
|
|
}
|
|
}
|
|
return missing_mandatory;
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.Column = Class.extend({
|
|
init: function init(section, df) {
|
|
if (!df) df = {};
|
|
|
|
this.df = df;
|
|
this.section = section;
|
|
this.make();
|
|
this.resize_all_columns();
|
|
},
|
|
make: function make() {
|
|
this.wrapper = $('<div class="form-column">\
|
|
<form>\
|
|
</form>\
|
|
</div>').appendTo(this.section.body).find("form").on("submit", function () {
|
|
return false;
|
|
});
|
|
|
|
if (this.df.label) {
|
|
$('<label class="control-label">' + __(this.df.label) + '</label>').appendTo(this.wrapper);
|
|
}
|
|
},
|
|
resize_all_columns: function resize_all_columns() {
|
|
var colspan = cint(12 / this.section.wrapper.find(".form-column").length);
|
|
|
|
this.section.wrapper.find(".form-column").removeClass().addClass("form-column").addClass("col-sm-" + colspan);
|
|
},
|
|
refresh: function refresh() {
|
|
this.section.refresh();
|
|
}
|
|
});
|
|
|
|
frappe.provide('frappe.ui');
|
|
|
|
frappe.ui.FieldGroup = frappe.ui.form.Layout.extend({
|
|
init: function init(opts) {
|
|
$.extend(this, opts);
|
|
this._super();
|
|
$.each(this.fields || [], function (i, f) {
|
|
if (!f.fieldname && f.label) {
|
|
f.fieldname = f.label.replace(/ /g, "_").toLowerCase();
|
|
}
|
|
});
|
|
if (this.values) {
|
|
this.set_values(this.values);
|
|
}
|
|
},
|
|
make: function make() {
|
|
var me = this;
|
|
if (this.fields) {
|
|
this._super();
|
|
this.refresh();
|
|
|
|
$.each(this.fields_list, function (i, field) {
|
|
if (field.df["default"]) {
|
|
field.set_input(field.df["default"]);
|
|
}
|
|
});
|
|
|
|
if (!this.no_submit_on_enter) {
|
|
this.catch_enter_as_submit();
|
|
}
|
|
|
|
$(this.body).find('input').on('change', function () {
|
|
me.refresh_dependency();
|
|
});
|
|
|
|
$(this.body).find('select').on("change", function () {
|
|
me.refresh_dependency();
|
|
});
|
|
}
|
|
},
|
|
add_fields: function add_fields(fields) {
|
|
this.render(fields);
|
|
this.refresh_fields(fields);
|
|
},
|
|
refresh_fields: function refresh_fields(fields) {
|
|
var fieldnames = fields.map(function (field) {
|
|
if (field.fieldname) return field.fieldname;
|
|
});
|
|
|
|
this.fields_list.map(function (fieldobj) {
|
|
if (fieldnames.includes(fieldobj.df.fieldname)) {
|
|
fieldobj.refresh();
|
|
if (fieldobj.df["default"]) {
|
|
fieldobj.set_input(fieldobj.df["default"]);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
first_button: false,
|
|
catch_enter_as_submit: function catch_enter_as_submit() {
|
|
var me = this;
|
|
$(this.body).find('input[type="text"], input[type="password"]').keypress(function (e) {
|
|
if (e.which == 13) {
|
|
if (me.has_primary_action) {
|
|
e.preventDefault();
|
|
me.get_primary_btn().trigger("click");
|
|
}
|
|
}
|
|
});
|
|
},
|
|
get_input: function get_input(fieldname) {
|
|
var field = this.fields_dict[fieldname];
|
|
return $(field.txt ? field.txt : field.input);
|
|
},
|
|
get_field: function get_field(fieldname) {
|
|
return this.fields_dict[fieldname];
|
|
},
|
|
get_values: function get_values(ignore_errors) {
|
|
var ret = {};
|
|
var errors = [];
|
|
for (var key in this.fields_dict) {
|
|
var f = this.fields_dict[key];
|
|
if (f.get_value) {
|
|
var v = f.get_value();
|
|
if (f.df.reqd && is_null(v)) errors.push(__(f.df.label));
|
|
|
|
if (!is_null(v)) ret[f.df.fieldname] = v;
|
|
}
|
|
}
|
|
if (errors.length && !ignore_errors) {
|
|
frappe.msgprint({
|
|
title: __('Missing Values Required'),
|
|
message: __('Following fields have missing values:') + '<br><br><ul><li>' + errors.join('<li>') + '</ul>',
|
|
indicator: 'orange'
|
|
});
|
|
return null;
|
|
}
|
|
return ret;
|
|
},
|
|
get_value: function get_value(key) {
|
|
var f = this.fields_dict[key];
|
|
return f && (f.get_value ? f.get_value() : null);
|
|
},
|
|
set_value: function set_value(key, val) {
|
|
var _this = this;
|
|
|
|
return new Promise(function (resolve) {
|
|
var f = _this.fields_dict[key];
|
|
if (f) {
|
|
f.set_value(val).then(function () {
|
|
f.set_input(val);
|
|
_this.refresh_dependency();
|
|
resolve();
|
|
});
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
},
|
|
set_input: function set_input(key, val) {
|
|
return this.set_value(key, val);
|
|
},
|
|
set_values: function set_values(dict) {
|
|
for (var key in dict) {
|
|
if (this.fields_dict[key]) {
|
|
this.set_value(key, dict[key]);
|
|
}
|
|
}
|
|
},
|
|
clear: function clear() {
|
|
for (var key in this.fields_dict) {
|
|
var f = this.fields_dict[key];
|
|
if (f && f.set_input) {
|
|
f.set_input(f.df['default'] || '');
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.make_control = function (opts) {
|
|
var control_class_name = "Control" + opts.df.fieldtype.replace(/ /g, "");
|
|
if (frappe.ui.form[control_class_name]) {
|
|
return new frappe.ui.form[control_class_name](opts);
|
|
} else {
|
|
console.log("Invalid Control Name: " + opts.df.fieldtype);
|
|
}
|
|
};
|
|
|
|
frappe.ui.form.Control = Class.extend({
|
|
init: function init(opts) {
|
|
$.extend(this, opts);
|
|
this.make();
|
|
|
|
if (frappe.boot.user && frappe.boot.user.name === "Administrator" && frappe.boot.developer_mode === 1 && this.$wrapper) {
|
|
this.$wrapper.attr("title", __(this.df.fieldname));
|
|
}
|
|
|
|
if (this.render_input) {
|
|
this.refresh();
|
|
}
|
|
},
|
|
make: function make() {
|
|
this.make_wrapper();
|
|
this.$wrapper.attr("data-fieldtype", this.df.fieldtype).attr("data-fieldname", this.df.fieldname);
|
|
this.wrapper = this.$wrapper.get(0);
|
|
this.wrapper.fieldobj = this;
|
|
},
|
|
|
|
make_wrapper: function make_wrapper() {
|
|
this.$wrapper = $("<div class='frappe-control'></div>").appendTo(this.parent);
|
|
|
|
this.wrapper = this.$wrapper;
|
|
},
|
|
|
|
toggle: function toggle(show) {
|
|
this.df.hidden = show ? 0 : 1;
|
|
this.refresh();
|
|
},
|
|
|
|
get_status: function get_status(explain) {
|
|
if (!this.doctype && !this.docname) {
|
|
if (cint(this.df.hidden)) {
|
|
if (explain) console.log("By Hidden: None");
|
|
return "None";
|
|
} else if (cint(this.df.hidden_due_to_dependency)) {
|
|
if (explain) console.log("By Hidden Dependency: None");
|
|
return "None";
|
|
} else if (cint(this.df.read_only)) {
|
|
if (explain) console.log("By Read Only: Read");
|
|
return "Read";
|
|
}
|
|
|
|
return "Write";
|
|
}
|
|
|
|
var status = frappe.perm.get_field_display_status(this.df, frappe.model.get_doc(this.doctype, this.docname), this.perm || this.frm && this.frm.perm, explain);
|
|
|
|
if (this.doctype && status === "Read" && !this.only_input && is_null(frappe.model.get_value(this.doctype, this.docname, this.df.fieldname)) && !in_list(["HTML", "Image"], this.df.fieldtype)) {
|
|
|
|
if (explain) console.log("By Hide Read-only, null fields: None");
|
|
status = "None";
|
|
}
|
|
|
|
return status;
|
|
},
|
|
refresh: function refresh() {
|
|
this.disp_status = this.get_status();
|
|
this.$wrapper && this.$wrapper.toggleClass("hide-control", this.disp_status == "None") && this.refresh_input && this.refresh_input();
|
|
},
|
|
get_doc: function get_doc() {
|
|
return this.doctype && this.docname && locals[this.doctype] && locals[this.doctype][this.docname] || {};
|
|
},
|
|
get_model_value: function get_model_value() {
|
|
if (this.doc) {
|
|
return this.doc[this.df.fieldname];
|
|
}
|
|
},
|
|
set_value: function set_value(value) {
|
|
return this.validate_and_set_in_model(value);
|
|
},
|
|
parse_validate_and_set_in_model: function parse_validate_and_set_in_model(value, e) {
|
|
if (this.parse) {
|
|
value = this.parse(value);
|
|
}
|
|
return this.validate_and_set_in_model(value, e);
|
|
},
|
|
validate_and_set_in_model: function validate_and_set_in_model(value, e) {
|
|
var me = this;
|
|
if (this.inside_change_event) {
|
|
return new Promise.resolve();
|
|
}
|
|
this.inside_change_event = true;
|
|
var set = function set(value) {
|
|
me.inside_change_event = false;
|
|
return frappe.run_serially([function () {
|
|
return me.set_model_value(value);
|
|
}, function () {
|
|
me.set_mandatory && me.set_mandatory(value);
|
|
|
|
if (me.df.change || me.df.onchange) {
|
|
return (me.df.change || me.df.onchange).apply(me, [e]);
|
|
}
|
|
}]);
|
|
};
|
|
|
|
value = this.validate(value);
|
|
if (value && value.then) {
|
|
return value.then(function (value) {
|
|
return set(value);
|
|
});
|
|
} else {
|
|
return set(value);
|
|
}
|
|
},
|
|
get_value: function get_value() {
|
|
if (this.get_status() === 'Write') {
|
|
return this.get_input_value ? this.parse ? this.parse(this.get_input_value()) : this.get_input_value() : undefined;
|
|
} else if (this.get_status() === 'Read') {
|
|
return this.value || undefined;
|
|
} else {
|
|
return undefined;
|
|
}
|
|
},
|
|
set_model_value: function set_model_value(value) {
|
|
if (this.doctype && this.docname) {
|
|
this.last_value = value;
|
|
return frappe.model.set_value(this.doctype, this.docname, this.df.fieldname, value, this.df.fieldtype);
|
|
} else {
|
|
if (this.doc) {
|
|
this.doc[this.df.fieldname] = value;
|
|
}
|
|
this.set_input(value);
|
|
return Promise.resolve();
|
|
}
|
|
},
|
|
set_focus: function set_focus() {
|
|
if (this.$input) {
|
|
this.$input.get(0).focus();
|
|
return true;
|
|
}
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlHTML = frappe.ui.form.Control.extend({
|
|
make: function make() {
|
|
this._super();
|
|
this.disp_area = this.wrapper;
|
|
},
|
|
refresh_input: function refresh_input() {
|
|
var content = this.get_content();
|
|
if (content) this.$wrapper.html(content);
|
|
},
|
|
get_content: function get_content() {
|
|
return this.df.options || "";
|
|
},
|
|
html: function html(_html) {
|
|
this.$wrapper.html(_html || this.get_content());
|
|
},
|
|
set_value: function set_value(html) {
|
|
if (html.appendTo) {
|
|
html.appendTo(this.$wrapper.empty());
|
|
} else {
|
|
this.df.options = html;
|
|
this.html(html);
|
|
}
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlHeading = frappe.ui.form.ControlHTML.extend({
|
|
get_content: function get_content() {
|
|
return "<h4>" + __(this.df.label) + "</h4>";
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlImage = frappe.ui.form.Control.extend({
|
|
make: function make() {
|
|
this._super();
|
|
var me = this;
|
|
this.$wrapper.css({ "margin": "0px" });
|
|
this.$body = $("<div></div>").appendTo(this.$wrapper).css({ "margin-bottom": "10px" });
|
|
$('<div class="clearfix"></div>').appendTo(this.$wrapper);
|
|
},
|
|
refresh_input: function refresh_input() {
|
|
this.$body.empty();
|
|
|
|
var doc = this.get_doc();
|
|
if (doc && this.df.options && doc[this.df.options]) {
|
|
this.$img = $("<img src='" + doc[this.df.options] + "' class='img-responsive'>").appendTo(this.$body);
|
|
} else {
|
|
this.$buffer = $("<div class='missing-image'><i class='octicon octicon-circle-slash'></i></div>").appendTo(this.$body);
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
|
|
horizontal: true,
|
|
make: function make() {
|
|
this._super();
|
|
this.set_input_areas();
|
|
|
|
this.set_max_width();
|
|
},
|
|
make_wrapper: function make_wrapper() {
|
|
if (this.only_input) {
|
|
this.$wrapper = $('<div class="form-group frappe-control">').appendTo(this.parent);
|
|
} else {
|
|
this.$wrapper = $('<div class="frappe-control">\
|
|
<div class="form-group">\
|
|
<div class="clearfix">\
|
|
<label class="control-label" style="padding-right: 0px;"></label>\
|
|
</div>\
|
|
<div class="control-input-wrapper">\
|
|
<div class="control-input"></div>\
|
|
<div class="control-value like-disabled-input" style="display: none;"></div>\
|
|
<p class="help-box small text-muted hidden-xs"></p>\
|
|
</div>\
|
|
</div>\
|
|
</div>').appendTo(this.parent);
|
|
}
|
|
},
|
|
toggle_label: function toggle_label(show) {
|
|
this.$wrapper.find(".control-label").toggleClass("hide", !show);
|
|
},
|
|
toggle_description: function toggle_description(show) {
|
|
this.$wrapper.find(".help-box").toggleClass("hide", !show);
|
|
},
|
|
set_input_areas: function set_input_areas() {
|
|
if (this.only_input) {
|
|
this.input_area = this.wrapper;
|
|
} else {
|
|
this.label_area = this.label_span = this.$wrapper.find("label").get(0);
|
|
this.input_area = this.$wrapper.find(".control-input").get(0);
|
|
|
|
this.disp_area = this.$wrapper.find(".control-value").get(0);
|
|
}
|
|
},
|
|
set_max_width: function set_max_width() {
|
|
if (this.horizontal) {
|
|
this.$wrapper.addClass("input-max-width");
|
|
}
|
|
},
|
|
|
|
refresh_input: function refresh_input() {
|
|
var me = this;
|
|
var make_input = function make_input() {
|
|
if (!me.has_input) {
|
|
me.make_input();
|
|
if (me.df.on_make) {
|
|
me.df.on_make(me);
|
|
}
|
|
}
|
|
};
|
|
|
|
var update_input = function update_input() {
|
|
if (me.doctype && me.docname) {
|
|
me.set_input(me.value);
|
|
} else {
|
|
me.set_input(me.value || null);
|
|
}
|
|
};
|
|
|
|
if (me.disp_status != "None") {
|
|
if (me.doctype && me.docname) {
|
|
me.value = frappe.model.get_value(me.doctype, me.docname, me.df.fieldname);
|
|
}
|
|
|
|
if (me.disp_status == "Write") {
|
|
me.disp_area && $(me.disp_area).toggle(false);
|
|
$(me.input_area).toggle(true);
|
|
me.$input && me.$input.prop("disabled", false);
|
|
make_input();
|
|
update_input();
|
|
} else {
|
|
if (me.only_input) {
|
|
make_input();
|
|
update_input();
|
|
} else {
|
|
$(me.input_area).toggle(false);
|
|
if (me.disp_area) {
|
|
me.set_disp_area(me.value);
|
|
$(me.disp_area).toggle(true);
|
|
}
|
|
}
|
|
me.$input && me.$input.prop("disabled", true);
|
|
}
|
|
|
|
me.set_description();
|
|
me.set_label();
|
|
me.set_mandatory(me.value);
|
|
me.set_bold();
|
|
}
|
|
},
|
|
|
|
set_disp_area: function set_disp_area(value) {
|
|
if (in_list(["Currency", "Int", "Float"], this.df.fieldtype) && (this.value === 0 || value === 0)) {
|
|
value = 0;
|
|
} else {
|
|
value = this.value || value;
|
|
}
|
|
this.disp_area && $(this.disp_area).html(frappe.format(value, this.df, { no_icon: true, inline: true }, this.doc || this.frm && this.frm.doc));
|
|
},
|
|
|
|
bind_change_event: function bind_change_event() {
|
|
var me = this;
|
|
this.$input && this.$input.on("change", this.change || function (e) {
|
|
me.parse_validate_and_set_in_model(me.get_input_value(), e);
|
|
});
|
|
},
|
|
bind_focusout: function bind_focusout() {
|
|
if (frappe.dom.is_touchscreen()) {
|
|
var me = this;
|
|
this.$input && this.$input.on("focusout", function () {
|
|
if (frappe.dom.is_touchscreen()) {
|
|
frappe.utils.scroll_to(me.$wrapper);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
set_label: function set_label(label) {
|
|
if (label) this.df.label = label;
|
|
|
|
if (this.only_input || this.df.label == this._label) return;
|
|
|
|
var icon = "";
|
|
this.label_span.innerHTML = (icon ? '<i class="' + icon + '"></i> ' : "") + __(this.df.label) || " ";
|
|
this._label = this.df.label;
|
|
},
|
|
set_description: function set_description() {
|
|
if (this.only_input || this.df.description === this._description) return;
|
|
if (this.df.description) {
|
|
this.$wrapper.find(".help-box").html(__(this.df.description));
|
|
} else {
|
|
this.set_empty_description();
|
|
}
|
|
this._description = this.df.description;
|
|
},
|
|
set_new_description: function set_new_description(description) {
|
|
this.$wrapper.find(".help-box").html(description);
|
|
},
|
|
set_empty_description: function set_empty_description() {
|
|
this.$wrapper.find(".help-box").html("");
|
|
},
|
|
set_mandatory: function set_mandatory(value) {
|
|
this.$wrapper.toggleClass("has-error", this.df.reqd && is_null(value) ? true : false);
|
|
},
|
|
set_bold: function set_bold() {
|
|
if (this.$input) {
|
|
this.$input.toggleClass("bold", !!(this.df.bold || this.df.reqd));
|
|
}
|
|
if (this.disp_area) {
|
|
$(this.disp_area).toggleClass("bold", !!(this.df.bold || this.df.reqd));
|
|
}
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({
|
|
html_element: "input",
|
|
input_type: "text",
|
|
make_input: function make_input() {
|
|
if (this.$input) return;
|
|
|
|
this.$input = $("<" + this.html_element + ">").attr("type", this.input_type).attr("autocomplete", "off").addClass("input-with-feedback form-control").prependTo(this.input_area);
|
|
|
|
if (in_list(['Data', 'Link', 'Dynamic Link', 'Password', 'Select', 'Read Only', 'Attach', 'Attach Image'], this.df.fieldtype)) {
|
|
this.$input.attr("maxlength", this.df.length || 140);
|
|
}
|
|
|
|
this.set_input_attributes();
|
|
this.input = this.$input.get(0);
|
|
this.has_input = true;
|
|
this.bind_change_event();
|
|
this.bind_focusout();
|
|
},
|
|
set_input_attributes: function set_input_attributes() {
|
|
this.$input.attr("data-fieldtype", this.df.fieldtype).attr("data-fieldname", this.df.fieldname).attr("placeholder", this.df.placeholder || "");
|
|
if (this.doctype) {
|
|
this.$input.attr("data-doctype", this.doctype);
|
|
}
|
|
if (this.df.input_css) {
|
|
this.$input.css(this.df.input_css);
|
|
}
|
|
if (this.df.input_class) {
|
|
this.$input.addClass(this.df.input_class);
|
|
}
|
|
},
|
|
set_input: function set_input(value) {
|
|
this.last_value = this.value;
|
|
this.value = value;
|
|
this.set_formatted_input(value);
|
|
this.set_disp_area(value);
|
|
this.set_mandatory && this.set_mandatory(value);
|
|
},
|
|
set_formatted_input: function set_formatted_input(value) {
|
|
this.$input && this.$input.val(this.format_for_input(value));
|
|
},
|
|
get_input_value: function get_input_value() {
|
|
return this.$input ? this.$input.val() : undefined;
|
|
},
|
|
format_for_input: function format_for_input(val) {
|
|
return val == null ? "" : val;
|
|
},
|
|
validate: function validate(v) {
|
|
if (this.df.options == 'Phone') {
|
|
if (v + '' == '') {
|
|
return '';
|
|
}
|
|
var v1 = '';
|
|
|
|
v = v.replace(/ /g, '').replace(/-/g, '').replace(/\(/g, '').replace(/\)/g, '');
|
|
|
|
if (v && v.substr(0, 1) == '+') {
|
|
v1 = '+';v = v.substr(1);
|
|
}
|
|
if (v && v.substr(0, 2) == '00') {
|
|
v1 += '00';v = v.substr(2);
|
|
}
|
|
if (v && v.substr(0, 1) == '0') {
|
|
v1 += '0';v = v.substr(1);
|
|
}
|
|
v1 += cint(v) + '';
|
|
return v1;
|
|
} else if (this.df.options == 'Email') {
|
|
if (v + '' == '') {
|
|
return '';
|
|
}
|
|
|
|
var email_list = frappe.utils.split_emails(v);
|
|
if (!email_list) {
|
|
return '';
|
|
} else {
|
|
var invalid_email = false;
|
|
email_list.forEach(function (email) {
|
|
if (!validate_email(email)) {
|
|
frappe.msgprint(__("Invalid Email: {0}", [email]));
|
|
invalid_email = true;
|
|
}
|
|
});
|
|
|
|
if (invalid_email) {
|
|
return '';
|
|
} else {
|
|
return v;
|
|
}
|
|
}
|
|
} else {
|
|
return v;
|
|
}
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlReadOnly = frappe.ui.form.ControlData.extend({
|
|
get_status: function get_status(explain) {
|
|
var status = this._super(explain);
|
|
if (status === "Write") status = "Read";
|
|
return;
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlPassword = frappe.ui.form.ControlData.extend({
|
|
input_type: "password",
|
|
make: function make() {
|
|
this._super();
|
|
},
|
|
make_input: function make_input() {
|
|
var _this = this;
|
|
|
|
var me = this;
|
|
this._super();
|
|
this.$input.parent().append($('<span class="password-strength-indicator indicator"></span>'));
|
|
this.$wrapper.find('.control-input-wrapper').append($('<p class="password-strength-message text-muted small hidden"></p>'));
|
|
|
|
this.indicator = this.$wrapper.find('.password-strength-indicator');
|
|
this.message = this.$wrapper.find('.help-box');
|
|
|
|
this.$input.on('input', function () {
|
|
var $this = $(_this);
|
|
clearTimeout($this.data('timeout'));
|
|
$this.data('timeout', setTimeout(function () {
|
|
var txt = me.$input.val();
|
|
me.get_password_strength(txt);
|
|
}), 300);
|
|
});
|
|
},
|
|
get_password_strength: function get_password_strength(value) {
|
|
var me = this;
|
|
frappe.call({
|
|
type: 'GET',
|
|
method: 'frappe.core.doctype.user.user.test_password_strength',
|
|
args: {
|
|
new_password: value || ''
|
|
},
|
|
callback: function callback(r) {
|
|
if (r.message && r.message.entropy) {
|
|
var score = r.message.score,
|
|
feedback = r.message.feedback;
|
|
|
|
feedback.crack_time_display = r.message.crack_time_display;
|
|
|
|
var indicators = ['grey', 'red', 'orange', 'yellow', 'green'];
|
|
me.set_strength_indicator(indicators[score]);
|
|
}
|
|
}
|
|
|
|
});
|
|
},
|
|
set_strength_indicator: function set_strength_indicator(color) {
|
|
var message = __("Include symbols, numbers and capital letters in the password");
|
|
this.indicator.removeClass().addClass('password-strength-indicator indicator ' + color);
|
|
this.message.html(message).removeClass('hidden');
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlInt = frappe.ui.form.ControlData.extend({
|
|
make: function make() {
|
|
this._super();
|
|
},
|
|
make_input: function make_input() {
|
|
var me = this;
|
|
this._super();
|
|
this.$input.on("focus", function () {
|
|
setTimeout(function () {
|
|
if (!document.activeElement) return;
|
|
document.activeElement.value = me.validate(document.activeElement.value);
|
|
document.activeElement.select();
|
|
}, 100);
|
|
return false;
|
|
});
|
|
},
|
|
parse: function parse(value) {
|
|
return cint(value, null);
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlFloat = frappe.ui.form.ControlInt.extend({
|
|
parse: function parse(value) {
|
|
return isNaN(parseFloat(value)) ? null : flt(value, this.get_precision());
|
|
},
|
|
|
|
format_for_input: function format_for_input(value) {
|
|
var number_format;
|
|
if (this.df.fieldtype === "Float" && this.df.options && this.df.options.trim()) {
|
|
number_format = this.get_number_format();
|
|
}
|
|
var formatted_value = format_number(parseFloat(value), number_format, this.get_precision());
|
|
return isNaN(parseFloat(value)) ? "" : formatted_value;
|
|
},
|
|
|
|
get_number_format: function (_get_number_format) {
|
|
function get_number_format() {
|
|
return _get_number_format.apply(this, arguments);
|
|
}
|
|
|
|
get_number_format.toString = function () {
|
|
return _get_number_format.toString();
|
|
};
|
|
|
|
return get_number_format;
|
|
}(function () {
|
|
var currency = frappe.meta.get_field_currency(this.df, this.get_doc());
|
|
return get_number_format(currency);
|
|
}),
|
|
|
|
get_precision: function get_precision() {
|
|
return this.df.precision || cint(frappe.boot.sysdefaults.float_precision, null);
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlCurrency = frappe.ui.form.ControlFloat.extend({
|
|
format_for_input: function format_for_input(value) {
|
|
var formatted_value = format_number(parseFloat(value), this.get_number_format(), this.get_precision());
|
|
return isNaN(parseFloat(value)) ? "" : formatted_value;
|
|
},
|
|
|
|
get_precision: function get_precision() {
|
|
if (!this.df.precision) {
|
|
if (frappe.boot.sysdefaults.currency_precision) {
|
|
this.df.precision = frappe.boot.sysdefaults.currency_precision;
|
|
} else {
|
|
this.df.precision = get_number_format_info(this.get_number_format()).precision;
|
|
}
|
|
}
|
|
|
|
return this.df.precision;
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlPercent = frappe.ui.form.ControlFloat;
|
|
|
|
frappe.ui.form.ControlColor = frappe.ui.form.ControlData.extend({
|
|
make_input: function make_input() {
|
|
this._super();
|
|
this.colors = ["#ffc4c4", "#ff8989", "#ff4d4d", "#a83333", "#ffe8cd", "#ffd19c", "#ffb868", "#a87945", "#ffd2c2", "#ffa685", "#ff7846", "#a85b5b", "#ffd7d7", "#ffb1b1", "#ff8989", "#a84f2e", "#fffacd", "#fff168", "#fff69c", "#a89f45", "#ebf8cc", "#d9f399", "#c5ec63", "#7b933d", "#cef6d1", "#9deca2", "#6be273", "#428b46", "#d2f8ed", "#a4f3dd", "#77ecca", "#49937e", "#d2f1ff", "#a6e4ff", "#78d6ff", "#4f8ea8", "#d2d2ff", "#a3a3ff", "#7575ff", "#4d4da8", "#dac7ff", "#b592ff", "#8e58ff", "#5e3aa8", "#f8d4f8", "#f3aaf0", "#ec7dea", "#934f92"];
|
|
this.make_color_input();
|
|
},
|
|
make_color_input: function make_color_input() {
|
|
this.$wrapper.find('.control-input-wrapper').append("<div class=\"color-picker\">\n\t\t\t\t<div class=\"color-picker-pallete\"></div>\n\t\t\t</div>");
|
|
this.$color_pallete = this.$wrapper.find('.color-picker-pallete');
|
|
|
|
var color_html = this.colors.map(this.get_color_box).join("");
|
|
this.$color_pallete.append(color_html);
|
|
this.$color_pallete.hide();
|
|
this.bind_events();
|
|
},
|
|
get_color_box: function get_color_box(hex) {
|
|
return "<div class=\"color-box\" data-color=\"" + hex + "\" style=\"background-color: " + hex + "\"></div>";
|
|
},
|
|
set_formatted_input: function set_formatted_input(value) {
|
|
this._super(value);
|
|
this.$input.css({
|
|
"background-color": value
|
|
});
|
|
},
|
|
bind_events: function bind_events() {
|
|
var _this2 = this;
|
|
|
|
var mousedown_happened = false;
|
|
this.$wrapper.on("click", ".color-box", function (e) {
|
|
mousedown_happened = false;
|
|
|
|
var color_val = $(e.target).data("color");
|
|
_this2.set_value(color_val);
|
|
|
|
_this2.set_focus();
|
|
});
|
|
|
|
this.$wrapper.find(".color-box").mousedown(function () {
|
|
mousedown_happened = true;
|
|
});
|
|
|
|
this.$input.on("focus", function () {
|
|
_this2.$color_pallete.show();
|
|
});
|
|
this.$input.on("blur", function () {
|
|
if (mousedown_happened) {
|
|
mousedown_happened = false;
|
|
} else {
|
|
$(_this2.$color_pallete).hide();
|
|
}
|
|
});
|
|
},
|
|
validate: function validate(value) {
|
|
var is_valid = /^#[0-9A-F]{6}$/i.test(value);
|
|
if (is_valid) {
|
|
return value;
|
|
}
|
|
frappe.msgprint(__("{0} is not a valid hex color", [value]));
|
|
return null;
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlDate = frappe.ui.form.ControlData.extend({
|
|
make_input: function make_input() {
|
|
this._super();
|
|
this.set_date_options();
|
|
this.set_datepicker();
|
|
this.set_t_for_today();
|
|
},
|
|
set_formatted_input: function set_formatted_input(value) {
|
|
this._super(value);
|
|
if (value && (this.last_value && this.last_value !== value || !this.datepicker.selectedDates.length)) {
|
|
this.datepicker.selectDate(frappe.datetime.str_to_obj(value));
|
|
}
|
|
},
|
|
set_date_options: function set_date_options() {
|
|
var _this3 = this;
|
|
|
|
var me = this;
|
|
var lang = frappe.boot.user.language;
|
|
if (!$.fn.datepicker.language[lang]) {
|
|
lang = 'en';
|
|
}
|
|
this.today_text = __("Today");
|
|
this.datepicker_options = {
|
|
language: lang,
|
|
autoClose: true,
|
|
todayButton: frappe.datetime.now_date(true),
|
|
dateFormat: frappe.boot.sysdefaults.date_format || 'yyyy-mm-dd',
|
|
startDate: frappe.datetime.now_date(true),
|
|
onSelect: function onSelect() {
|
|
_this3.$input.trigger('change');
|
|
},
|
|
onShow: function onShow() {
|
|
_this3.datepicker.$datepicker.find('.datepicker--button:visible').text(me.today_text);
|
|
|
|
_this3.update_datepicker_position();
|
|
}
|
|
};
|
|
},
|
|
update_datepicker_position: function update_datepicker_position() {
|
|
if (!this.frm) return;
|
|
|
|
var window_height = $(window).height();
|
|
var window_scroll_top = $(window).scrollTop();
|
|
var el_offset_top = this.$input.offset().top + 280;
|
|
var position = 'top left';
|
|
if (window_height + window_scroll_top >= el_offset_top) {
|
|
position = 'bottom left';
|
|
}
|
|
this.datepicker.update('position', position);
|
|
},
|
|
set_datepicker: function set_datepicker() {
|
|
this.$input.datepicker(this.datepicker_options);
|
|
this.datepicker = this.$input.data('datepicker');
|
|
},
|
|
set_t_for_today: function set_t_for_today() {
|
|
var me = this;
|
|
this.$input.on("keydown", function (e) {
|
|
if (e.which === 84) {
|
|
if (me.df.fieldtype == 'Date') {
|
|
me.set_value(frappe.datetime.nowdate());
|
|
}if (me.df.fieldtype == 'Datetime') {
|
|
me.set_value(frappe.datetime.now_datetime());
|
|
}if (me.df.fieldtype == 'Time') {
|
|
me.set_value(frappe.datetime.now_time());
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
},
|
|
parse: function parse(value) {
|
|
if (value) {
|
|
return frappe.datetime.user_to_str(value);
|
|
}
|
|
},
|
|
format_for_input: function format_for_input(value) {
|
|
if (value) {
|
|
return frappe.datetime.str_to_user(value);
|
|
}
|
|
return "";
|
|
},
|
|
validate: function validate(value) {
|
|
if (value && !frappe.datetime.validate(value)) {
|
|
frappe.msgprint(__("Date must be in format: {0}", [frappe.sys_defaults.date_format || "yyyy-mm-dd"]));
|
|
return '';
|
|
}
|
|
return value;
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlDatetime = frappe.ui.form.ControlDate.extend({
|
|
set_date_options: function set_date_options() {
|
|
this._super();
|
|
this.today_text = __("Now");
|
|
$.extend(this.datepicker_options, {
|
|
timepicker: true,
|
|
timeFormat: "hh:ii:ss",
|
|
todayButton: frappe.datetime.now_datetime(true)
|
|
});
|
|
},
|
|
set_description: function set_description() {
|
|
var description = this.df.description;
|
|
var time_zone = frappe.sys_defaults.time_zone;
|
|
|
|
if (!frappe.datetime.is_timezone_same()) {
|
|
if (!description) {
|
|
this.df.description = time_zone;
|
|
} else if (!description.includes(time_zone)) {
|
|
this.df.description += '<br>' + time_zone;
|
|
}
|
|
}
|
|
this._super();
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlTime = frappe.ui.form.ControlData.extend({
|
|
make_input: function make_input() {
|
|
var me = this;
|
|
this._super();
|
|
this.$input.datepicker({
|
|
language: "en",
|
|
timepicker: true,
|
|
onlyTimepicker: true,
|
|
timeFormat: "hh:ii:ss",
|
|
startDate: frappe.datetime.now_time(true),
|
|
onSelect: function onSelect() {
|
|
me.$input.trigger('change');
|
|
},
|
|
onShow: function onShow() {
|
|
$('.datepicker--button:visible').text(__('Now'));
|
|
},
|
|
todayButton: frappe.datetime.now_time(true)
|
|
});
|
|
this.datepicker = this.$input.data('datepicker');
|
|
this.refresh();
|
|
},
|
|
set_input: function set_input(value) {
|
|
this._super(value);
|
|
if (value && (this.last_value && this.last_value !== this.value || !this.datepicker.selectedDates.length)) {
|
|
|
|
var date_obj = frappe.datetime.moment_to_date_obj(moment(value, 'hh:mm:ss'));
|
|
this.datepicker.selectDate(date_obj);
|
|
}
|
|
},
|
|
set_description: function set_description() {
|
|
var description = this.df.description;
|
|
var time_zone = frappe.sys_defaults.time_zone;
|
|
|
|
if (!frappe.datetime.is_timezone_same()) {
|
|
if (!description) {
|
|
this.df.description = time_zone;
|
|
} else if (!description.includes(time_zone)) {
|
|
this.df.description += '<br>' + time_zone;
|
|
}
|
|
}
|
|
this._super();
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlDateRange = frappe.ui.form.ControlData.extend({
|
|
make_input: function make_input() {
|
|
this._super();
|
|
this.set_date_options();
|
|
this.set_datepicker();
|
|
this.refresh();
|
|
},
|
|
set_date_options: function set_date_options() {
|
|
var me = this;
|
|
this.datepicker_options = {
|
|
language: "en",
|
|
range: true,
|
|
autoClose: true,
|
|
toggleSelected: false
|
|
};
|
|
this.datepicker_options.dateFormat = frappe.boot.sysdefaults.date_format || 'yyyy-mm-dd';
|
|
this.datepicker_options.onSelect = function () {
|
|
me.$input.trigger('change');
|
|
};
|
|
},
|
|
set_datepicker: function set_datepicker() {
|
|
this.$input.datepicker(this.datepicker_options);
|
|
this.datepicker = this.$input.data('datepicker');
|
|
},
|
|
set_input: function set_input(value, value2) {
|
|
this.last_value = this.value;
|
|
if (value && value2) {
|
|
this.value = [value, value2];
|
|
} else {
|
|
this.value = value;
|
|
}
|
|
if (this.value) {
|
|
var formatted = this.format_for_input(this.value[0], this.value[1]);
|
|
this.$input && this.$input.val(formatted);
|
|
} else {
|
|
this.$input && this.$input.val("");
|
|
}
|
|
this.set_disp_area(value || '');
|
|
this.set_mandatory && this.set_mandatory(value);
|
|
},
|
|
parse: function parse(value) {
|
|
if (value && (value.indexOf(',') !== -1 || value.indexOf('to') !== -1)) {
|
|
var vals = value.split(/[( to )(,)]/);
|
|
var from_date = moment(frappe.datetime.user_to_obj(vals[0])).format('YYYY-MM-DD');
|
|
var to_date = moment(frappe.datetime.user_to_obj(vals[vals.length - 1])).format('YYYY-MM-DD');
|
|
return [from_date, to_date];
|
|
}
|
|
},
|
|
format_for_input: function format_for_input(value1, value2) {
|
|
if (value1 && value2) {
|
|
value1 = frappe.datetime.str_to_user(value1);
|
|
value2 = frappe.datetime.str_to_user(value2);
|
|
return __("{0} to {1}").format([value1, value2]);
|
|
}
|
|
return "";
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlText = frappe.ui.form.ControlData.extend({
|
|
html_element: "textarea",
|
|
horizontal: false,
|
|
make_wrapper: function make_wrapper() {
|
|
this._super();
|
|
this.$wrapper.find(".like-disabled-input").addClass("for-description");
|
|
},
|
|
make_input: function make_input() {
|
|
this._super();
|
|
this.$input.css({ 'height': '300px' });
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlLongText = frappe.ui.form.ControlText;
|
|
frappe.ui.form.ControlSmallText = frappe.ui.form.ControlText.extend({
|
|
make_input: function make_input() {
|
|
this._super();
|
|
this.$input.css({ 'height': '150px' });
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlCheck = frappe.ui.form.ControlData.extend({
|
|
input_type: "checkbox",
|
|
make_wrapper: function make_wrapper() {
|
|
this.$wrapper = $('<div class="form-group frappe-control">\
|
|
<div class="checkbox">\
|
|
<label>\
|
|
<span class="input-area"></span>\
|
|
<span class="disp-area" style="display:none; margin-left: -20px;"></span>\
|
|
<span class="label-area small"></span>\
|
|
</label>\
|
|
<p class="help-box small text-muted"></p>\
|
|
</div>\
|
|
</div>').appendTo(this.parent);
|
|
},
|
|
set_input_areas: function set_input_areas() {
|
|
this.label_area = this.label_span = this.$wrapper.find(".label-area").get(0);
|
|
this.input_area = this.$wrapper.find(".input-area").get(0);
|
|
this.disp_area = this.$wrapper.find(".disp-area").get(0);
|
|
},
|
|
make_input: function make_input() {
|
|
this._super();
|
|
this.$input.removeClass("form-control");
|
|
},
|
|
get_input_value: function get_input_value() {
|
|
return this.input && this.input.checked ? 1 : 0;
|
|
},
|
|
validate: function validate(value) {
|
|
return cint(value);
|
|
},
|
|
set_input: function set_input(value) {
|
|
if (this.input) {
|
|
this.input.checked = value ? 1 : 0;
|
|
}
|
|
this.last_value = value;
|
|
this.set_mandatory(value);
|
|
this.set_disp_area(value);
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlButton = frappe.ui.form.ControlData.extend({
|
|
make_input: function make_input() {
|
|
var me = this;
|
|
this.$input = $('<button class="btn btn-default btn-xs">').prependTo(me.input_area).on("click", function () {
|
|
me.onclick();
|
|
});
|
|
this.input = this.$input.get(0);
|
|
this.set_input_attributes();
|
|
this.has_input = true;
|
|
this.toggle_label(false);
|
|
},
|
|
onclick: function onclick() {
|
|
if (this.frm && this.frm.doc) {
|
|
if (this.frm.script_manager.has_handlers(this.df.fieldname, this.doctype)) {
|
|
this.frm.script_manager.trigger(this.df.fieldname, this.doctype, this.docname);
|
|
} else {
|
|
this.frm.runscript(this.df.options, this);
|
|
}
|
|
} else if (this.df.click) {
|
|
this.df.click();
|
|
}
|
|
},
|
|
set_input_areas: function set_input_areas() {
|
|
this._super();
|
|
$(this.disp_area).removeClass().addClass("hide");
|
|
},
|
|
set_empty_description: function set_empty_description() {
|
|
this.$wrapper.find(".help-box").empty().toggle(false);
|
|
},
|
|
set_label: function set_label() {
|
|
$(this.label_span).html(" ");
|
|
this.$input && this.$input.html((this.df.icon ? '<i class="' + this.df.icon + ' fa-fw"></i> ' : "") + __(this.df.label));
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({
|
|
make_input: function make_input() {
|
|
var me = this;
|
|
this.$input = $('<button class="btn btn-default btn-sm btn-attach">').html(__("Attach")).prependTo(me.input_area).on("click", function () {
|
|
me.onclick();
|
|
});
|
|
this.$value = $('<div style="margin-top: 5px;">\
|
|
<div class="ellipsis" style="display: inline-block; width: 90%;">\
|
|
<i class="fa fa-paper-clip"></i> \
|
|
<a class="attached-file" target="_blank"></a>\
|
|
</div>\
|
|
<a class="close">×</a></div>').prependTo(me.input_area).toggle(false);
|
|
this.input = this.$input.get(0);
|
|
this.set_input_attributes();
|
|
this.has_input = true;
|
|
|
|
this.$value.find(".close").on("click", function () {
|
|
me.clear_attachment();
|
|
});
|
|
},
|
|
clear_attachment: function clear_attachment() {
|
|
var me = this;
|
|
if (this.frm) {
|
|
me.frm.attachments.remove_attachment_by_filename(me.value, function () {
|
|
me.parse_validate_and_set_in_model(null);
|
|
me.refresh();
|
|
me.frm.save();
|
|
});
|
|
} else {
|
|
this.dataurl = null;
|
|
this.fileobj = null;
|
|
this.set_input(null);
|
|
this.refresh();
|
|
}
|
|
},
|
|
onclick: function onclick() {
|
|
var me = this;
|
|
if (this.doc) {
|
|
var doc = this.doc.parent && frappe.model.get_doc(this.doc.parenttype, this.doc.parent) || this.doc;
|
|
if (doc.__islocal) {
|
|
frappe.msgprint(__("Please save the document before uploading."));
|
|
return;
|
|
}
|
|
}
|
|
if (!this.dialog) {
|
|
this.dialog = new frappe.ui.Dialog({
|
|
title: __(this.df.label || __("Upload")),
|
|
fields: [{ fieldtype: "HTML", fieldname: "upload_area" }, { fieldtype: "HTML", fieldname: "or_attach", options: __("Or") }, { fieldtype: "Select", fieldname: "select", label: __("Select from existing attachments") }, { fieldtype: "Button", fieldname: "clear",
|
|
label: __("Clear Attachment"), click: function click() {
|
|
me.clear_attachment();
|
|
me.dialog.hide();
|
|
}
|
|
}]
|
|
});
|
|
}
|
|
|
|
this.dialog.show();
|
|
|
|
this.dialog.get_field("upload_area").$wrapper.empty();
|
|
|
|
var attachments = this.frm && this.frm.attachments.get_attachments() || [];
|
|
var select = this.dialog.get_field("select");
|
|
if (attachments.length) {
|
|
attachments = $.map(attachments, function (o) {
|
|
return o.file_url;
|
|
});
|
|
select.df.options = [""].concat(attachments);
|
|
select.toggle(true);
|
|
this.dialog.get_field("or_attach").toggle(true);
|
|
select.refresh();
|
|
} else {
|
|
this.dialog.get_field("or_attach").toggle(false);
|
|
select.toggle(false);
|
|
}
|
|
select.$input.val("");
|
|
|
|
this.dialog.get_field('clear').$wrapper.toggle(this.get_model_value() ? true : false);
|
|
|
|
this.set_upload_options();
|
|
frappe.upload.make(this.upload_options);
|
|
},
|
|
|
|
set_upload_options: function set_upload_options() {
|
|
var me = this;
|
|
this.upload_options = {
|
|
parent: this.dialog.get_field("upload_area").$wrapper,
|
|
args: {},
|
|
allow_multiple: 0,
|
|
max_width: this.df.max_width,
|
|
max_height: this.df.max_height,
|
|
options: this.df.options,
|
|
btn: this.dialog.set_primary_action(__("Upload")),
|
|
on_no_attach: function on_no_attach() {
|
|
var selected = me.dialog.get_field("select").get_value();
|
|
if (selected) {
|
|
me.parse_validate_and_set_in_model(selected);
|
|
me.dialog.hide();
|
|
me.frm.save();
|
|
} else {
|
|
frappe.msgprint(__("Please attach a file or set a URL"));
|
|
}
|
|
},
|
|
callback: function callback(attachment, r) {
|
|
me.on_upload_complete(attachment);
|
|
me.dialog.hide();
|
|
},
|
|
onerror: function onerror() {
|
|
me.dialog.hide();
|
|
}
|
|
};
|
|
|
|
if ("is_private" in this.df) {
|
|
this.upload_options.is_private = this.df.is_private;
|
|
}
|
|
|
|
if (this.frm) {
|
|
this.upload_options.args = {
|
|
from_form: 1,
|
|
doctype: this.frm.doctype,
|
|
docname: this.frm.docname
|
|
};
|
|
} else {
|
|
this.upload_options.on_attach = function (fileobj, dataurl) {
|
|
me.dialog.hide();
|
|
me.fileobj = fileobj;
|
|
me.dataurl = dataurl;
|
|
if (me.on_attach) {
|
|
me.on_attach();
|
|
}
|
|
if (me.df.on_attach) {
|
|
me.df.on_attach(fileobj, dataurl);
|
|
}
|
|
me.on_upload_complete();
|
|
};
|
|
}
|
|
},
|
|
|
|
set_input: function set_input(value, dataurl) {
|
|
this.value = value;
|
|
if (this.value) {
|
|
this.$input.toggle(false);
|
|
if (this.value.indexOf(",") !== -1) {
|
|
var parts = this.value.split(",");
|
|
var filename = parts[0];
|
|
var dataurl = parts[1];
|
|
}
|
|
this.$value.toggle(true).find(".attached-file").html(filename || this.value).attr("href", dataurl || this.value);
|
|
} else {
|
|
this.$input.toggle(true);
|
|
this.$value.toggle(false);
|
|
}
|
|
},
|
|
|
|
get_value: function get_value() {
|
|
if (this.frm) {
|
|
return this.value;
|
|
} else {
|
|
return this.fileobj ? this.fileobj.filename + "," + this.dataurl : null;
|
|
}
|
|
},
|
|
|
|
on_upload_complete: function on_upload_complete(attachment) {
|
|
if (this.frm) {
|
|
this.parse_validate_and_set_in_model(attachment.file_url);
|
|
this.refresh();
|
|
this.frm.attachments.update_attachment(attachment);
|
|
this.frm.save();
|
|
} else {
|
|
this.value = this.get_value();
|
|
this.refresh();
|
|
}
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlAttachImage = frappe.ui.form.ControlAttach.extend({
|
|
make: function make() {
|
|
var me = this;
|
|
this._super();
|
|
|
|
this.container = $('<div class="control-container">').insertAfter($(this.wrapper));
|
|
$(this.wrapper).detach();
|
|
this.container.attr('data-fieldtype', this.df.fieldtype).append(this.wrapper);
|
|
if (this.df.align === 'center') {
|
|
this.container.addClass("flex-justify-center");
|
|
} else if (this.df.align === 'right') {
|
|
this.container.addClass("flex-justify-end");
|
|
}
|
|
|
|
this.img_wrapper = $('<div style="width: 100%; height: calc(100% - 40px); position: relative;">\
|
|
<div class="missing-image attach-missing-image"><i class="octicon octicon-device-camera"></i></div></div>').appendTo(this.wrapper);
|
|
|
|
this.img_container = $("<div class='img-container'></div>");
|
|
this.img = $("<img class='img-responsive attach-image-display'>").appendTo(this.img_container);
|
|
|
|
this.img_overlay = $("<div class='img-overlay'>\n\t\t\t\t<span class=\"overlay-text\">Change</span>\n\t\t\t</div>").appendTo(this.img_container);
|
|
|
|
this.remove_image_link = $('<a style="font-size: 12px;color: #8D99A6;">Remove</a>');
|
|
|
|
this.img_wrapper.append(this.img_container).append(this.remove_image_link);
|
|
|
|
this.img_container.toggle(false);
|
|
this.remove_image_link.toggle(false);
|
|
|
|
this.img_wrapper.find(".missing-image").on("click", function () {
|
|
me.$input.click();
|
|
});
|
|
this.img_container.on("click", function () {
|
|
me.$input.click();
|
|
});
|
|
this.remove_image_link.on("click", function () {
|
|
me.$value.find(".close").click();
|
|
});
|
|
|
|
this.set_image();
|
|
},
|
|
refresh_input: function refresh_input() {
|
|
this._super();
|
|
$(this.wrapper).find('.btn-attach').addClass('hidden');
|
|
this.set_image();
|
|
if (this.get_status() == "Read") {
|
|
$(this.disp_area).toggle(false);
|
|
}
|
|
},
|
|
set_image: function set_image() {
|
|
if (this.get_value()) {
|
|
$(this.img_wrapper).find(".missing-image").toggle(false);
|
|
|
|
this.img.attr("src", this.dataurl ? this.dataurl : this.value);
|
|
this.img_container.toggle(true);
|
|
this.remove_image_link.toggle(true);
|
|
} else {
|
|
$(this.img_wrapper).find(".missing-image").toggle(true);
|
|
|
|
this.img_container.toggle(false);
|
|
this.remove_image_link.toggle(false);
|
|
}
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlSelect = frappe.ui.form.ControlData.extend({
|
|
html_element: "select",
|
|
make_input: function make_input() {
|
|
this._super();
|
|
this.set_options();
|
|
},
|
|
set_formatted_input: function set_formatted_input(value) {
|
|
if (value == null) value = '';
|
|
this.set_options(value);
|
|
|
|
this._super(value);
|
|
|
|
var input_value = '';
|
|
if (this.$input) {
|
|
input_value = this.$input.val();
|
|
}
|
|
|
|
if (value && input_value && value !== input_value) {
|
|
this.set_model_value(input_value);
|
|
}
|
|
},
|
|
set_options: function set_options(value) {
|
|
var options = this.df.options || [];
|
|
if (typeof this.df.options === "string") {
|
|
options = this.df.options.split("\n");
|
|
}
|
|
|
|
if (options.toString() === this.last_options) {
|
|
return;
|
|
}
|
|
this.last_options = options.toString();
|
|
|
|
if (this.$input) {
|
|
var selected = this.$input.find(":selected").val();
|
|
this.$input.empty().add_options(options || []);
|
|
|
|
if (value === undefined && selected) {
|
|
this.$input.val(selected);
|
|
}
|
|
}
|
|
},
|
|
get_file_attachment_list: function get_file_attachment_list() {
|
|
if (!this.frm) return;
|
|
var fl = frappe.model.docinfo[this.frm.doctype][this.frm.docname];
|
|
if (fl && fl.attachments) {
|
|
this.set_description("");
|
|
var options = [""];
|
|
$.each(fl.attachments, function (i, f) {
|
|
options.push(f.file_url);
|
|
});
|
|
return options;
|
|
} else {
|
|
this.set_description(__("Please attach a file first."));
|
|
return [""];
|
|
}
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({
|
|
make_input: function make_input() {
|
|
var me = this;
|
|
|
|
$('<div class="link-field ui-front" style="position: relative; line-height: 1;">\
|
|
<input type="text" class="input-with-feedback form-control">\
|
|
<span class="link-btn">\
|
|
<a class="btn-open no-decoration" title="' + __("Open Link") + '">\
|
|
<i class="octicon octicon-arrow-right"></i></a>\
|
|
</span>\
|
|
</div>').prependTo(this.input_area);
|
|
this.$input_area = $(this.input_area);
|
|
this.$input = this.$input_area.find('input');
|
|
this.$link = this.$input_area.find('.link-btn');
|
|
this.$link_open = this.$link.find('.btn-open');
|
|
this.set_input_attributes();
|
|
this.$input.on("focus", function () {
|
|
setTimeout(function () {
|
|
if (me.$input.val() && me.get_options()) {
|
|
me.$link.toggle(true);
|
|
me.$link_open.attr('href', '#Form/' + me.get_options() + '/' + me.$input.val());
|
|
}
|
|
|
|
if (!me.$input.val()) {
|
|
me.$input.val("").trigger("input");
|
|
}
|
|
}, 500);
|
|
});
|
|
this.$input.on("blur", function () {
|
|
setTimeout(function () {
|
|
me.$link.toggle(false);
|
|
}, 500);
|
|
});
|
|
this.input = this.$input.get(0);
|
|
this.has_input = true;
|
|
this.translate_values = true;
|
|
var me = this;
|
|
this.setup_buttons();
|
|
this.setup_awesomeplete();
|
|
if (this.df.change) {
|
|
this.$input.on("change", function () {
|
|
me.df.change.apply(this);
|
|
});
|
|
}
|
|
},
|
|
get_options: function get_options() {
|
|
return this.df.options;
|
|
},
|
|
setup_buttons: function setup_buttons() {
|
|
var me = this;
|
|
|
|
if (this.only_input && !this.with_link_btn) {
|
|
this.$input_area.find(".link-btn").remove();
|
|
}
|
|
},
|
|
open_advanced_search: function open_advanced_search() {
|
|
var doctype = this.get_options();
|
|
if (!doctype) return;
|
|
new frappe.ui.form.LinkSelector({
|
|
doctype: doctype,
|
|
target: this,
|
|
txt: this.get_input_value()
|
|
});
|
|
return false;
|
|
},
|
|
new_doc: function new_doc() {
|
|
var doctype = this.get_options();
|
|
var me = this;
|
|
|
|
if (!doctype) return;
|
|
|
|
if (this.df.get_route_options_for_new_doc) {
|
|
frappe.route_options = this.df.get_route_options_for_new_doc(this);
|
|
} else {
|
|
frappe.route_options = {};
|
|
}
|
|
|
|
frappe.route_options.name_field = this.get_value();
|
|
|
|
frappe._from_link = this;
|
|
frappe._from_link_scrollY = $(document).scrollTop();
|
|
|
|
frappe.ui.form.make_quick_entry(doctype, function (doc) {
|
|
return me.set_value(doc.name);
|
|
});
|
|
|
|
return false;
|
|
},
|
|
setup_awesomeplete: function setup_awesomeplete() {
|
|
var me = this;
|
|
|
|
this.$input.cache = {};
|
|
|
|
this.awesomplete = new Awesomplete(me.input, {
|
|
minChars: 0,
|
|
maxItems: 99,
|
|
autoFirst: true,
|
|
list: [],
|
|
data: function data(item, input) {
|
|
return {
|
|
label: item.label || item.value,
|
|
value: item.value
|
|
};
|
|
},
|
|
filter: function filter(item, input) {
|
|
return true;
|
|
},
|
|
item: function item(_item, input) {
|
|
var d = this.get_item(_item.value);
|
|
if (!d.label) {
|
|
d.label = d.value;
|
|
}
|
|
|
|
var _label = me.translate_values ? __(d.label) : d.label;
|
|
var html = "<strong>" + _label + "</strong>";
|
|
if (d.description && d.value !== d.description) {
|
|
html += '<br><span class="small">' + __(d.description) + '</span>';
|
|
}
|
|
return $('<li></li>').data('item.autocomplete', d).prop('aria-selected', 'false').html('<a><p>' + html + '</p></a>').get(0);
|
|
},
|
|
sort: function sort(a, b) {
|
|
return 0;
|
|
}
|
|
});
|
|
|
|
this.$input.on("input", function (e) {
|
|
var doctype = me.get_options();
|
|
if (!doctype) return;
|
|
if (!me.$input.cache[doctype]) {
|
|
me.$input.cache[doctype] = {};
|
|
}
|
|
|
|
var term = e.target.value;
|
|
|
|
if (me.$input.cache[doctype][term] != null) {
|
|
me.awesomplete.list = me.$input.cache[doctype][term];
|
|
}
|
|
|
|
var args = {
|
|
'txt': term,
|
|
'doctype': doctype
|
|
};
|
|
|
|
me.set_custom_query(args);
|
|
|
|
frappe.call({
|
|
type: "GET",
|
|
method: 'frappe.desk.search.search_link',
|
|
no_spinner: true,
|
|
args: args,
|
|
callback: function callback(r) {
|
|
if (!me.$input.is(":focus")) {
|
|
return;
|
|
}
|
|
|
|
if (!me.df.only_select) {
|
|
if (frappe.model.can_create(doctype) && me.df.fieldtype !== "Dynamic Link") {
|
|
r.results.push({
|
|
label: "<span class='text-primary link-option'>" + "<i class='fa fa-plus' style='margin-right: 5px;'></i> " + __("Create a new {0}", [__(me.df.options)]) + "</span>",
|
|
value: "create_new__link_option",
|
|
action: me.new_doc
|
|
});
|
|
}
|
|
|
|
r.results.push({
|
|
label: "<span class='text-primary link-option'>" + "<i class='fa fa-search' style='margin-right: 5px;'></i> " + __("Advanced Search") + "</span>",
|
|
value: "advanced_search__link_option",
|
|
action: me.open_advanced_search
|
|
});
|
|
}
|
|
me.$input.cache[doctype][term] = r.results;
|
|
me.awesomplete.list = me.$input.cache[doctype][term];
|
|
}
|
|
});
|
|
});
|
|
|
|
this.$input.on("blur", function () {
|
|
if (me.selected) {
|
|
me.selected = false;
|
|
return;
|
|
}
|
|
var value = me.get_input_value();
|
|
if (value !== me.last_value) {
|
|
me.parse_validate_and_set_in_model(value);
|
|
}
|
|
});
|
|
|
|
this.$input.on("awesomplete-open", function (e) {
|
|
me.$wrapper.css({ "z-index": 100 });
|
|
me.$wrapper.find('ul').css({ "z-index": 100 });
|
|
me.autocomplete_open = true;
|
|
});
|
|
|
|
this.$input.on("awesomplete-close", function (e) {
|
|
me.$wrapper.css({ "z-index": 1 });
|
|
me.autocomplete_open = false;
|
|
});
|
|
|
|
this.$input.on("awesomplete-select", function (e) {
|
|
var o = e.originalEvent;
|
|
var item = me.awesomplete.get_item(o.text.value);
|
|
|
|
me.autocomplete_open = false;
|
|
|
|
var TABKEY = 9;
|
|
if (e.keyCode === TABKEY) {
|
|
e.preventDefault();
|
|
me.awesomplete.close();
|
|
return false;
|
|
}
|
|
|
|
if (item.action) {
|
|
item.value = "";
|
|
item.action.apply(me);
|
|
}
|
|
|
|
if (me.df.remember_last_selected_value) {
|
|
frappe.boot.user.last_selected_values[me.df.options] = item.value;
|
|
}
|
|
|
|
me.parse_validate_and_set_in_model(item.value);
|
|
});
|
|
|
|
this.$input.on("awesomplete-selectcomplete", function (e) {
|
|
var o = e.originalEvent;
|
|
if (o.text.value.indexOf("__link_option") !== -1) {
|
|
me.$input.val("");
|
|
}
|
|
});
|
|
},
|
|
set_custom_query: function set_custom_query(args) {
|
|
var set_nulls = function set_nulls(obj) {
|
|
$.each(obj, function (key, value) {
|
|
if (value !== undefined) {
|
|
obj[key] = value;
|
|
}
|
|
});
|
|
return obj;
|
|
};
|
|
if (this.get_query || this.df.get_query) {
|
|
var get_query = this.get_query || this.df.get_query;
|
|
if ($.isPlainObject(get_query)) {
|
|
var filters = null;
|
|
if (get_query.filters) {
|
|
filters = get_query.filters;
|
|
} else if (get_query.query) {
|
|
args.query = get_query;
|
|
} else {
|
|
filters = get_query;
|
|
}
|
|
|
|
if (filters) {
|
|
var filters = set_nulls(filters);
|
|
|
|
$.extend(args, filters);
|
|
|
|
args.filters = filters;
|
|
}
|
|
} else if (typeof get_query === "string") {
|
|
args.query = get_query;
|
|
} else {
|
|
var q = get_query(this.frm && this.frm.doc || this.doc, this.doctype, this.docname);
|
|
|
|
if (typeof q === "string") {
|
|
args.query = q;
|
|
} else if ($.isPlainObject(q)) {
|
|
if (q.filters) {
|
|
set_nulls(q.filters);
|
|
}
|
|
|
|
if (q.translate_values !== undefined) {
|
|
this.translate_values = q.translate_values;
|
|
}
|
|
|
|
$.extend(args, q);
|
|
|
|
args.filters = q.filters;
|
|
}
|
|
}
|
|
}
|
|
if (this.df.filters) {
|
|
set_nulls(this.df.filters);
|
|
if (!args.filters) args.filters = {};
|
|
$.extend(args.filters, this.df.filters);
|
|
}
|
|
},
|
|
validate: function validate(value) {
|
|
if (this.df.options == "[Select]" || this.df.ignore_link_validation) {
|
|
return value;
|
|
}
|
|
|
|
return this.validate_link_and_fetch(this.df, this.get_options(), this.docname, value);
|
|
},
|
|
validate_link_and_fetch: function validate_link_and_fetch(df, doctype, docname, value) {
|
|
var _this4 = this;
|
|
|
|
var me = this;
|
|
|
|
if (value) {
|
|
return new Promise(function (resolve) {
|
|
var fetch = '';
|
|
|
|
if (_this4.frm && _this4.frm.fetch_dict[df.fieldname]) {
|
|
fetch = _this4.frm.fetch_dict[df.fieldname].columns.join(', ');
|
|
}
|
|
|
|
return frappe.call({
|
|
method: 'frappe.desk.form.utils.validate_link',
|
|
type: "GET",
|
|
args: {
|
|
'value': value,
|
|
'options': doctype,
|
|
'fetch': fetch
|
|
},
|
|
no_spinner: true,
|
|
callback: function callback(r) {
|
|
if (r.message == 'Ok') {
|
|
if (r.fetch_values && docname) {
|
|
me.set_fetch_values(df, docname, r.fetch_values);
|
|
}
|
|
resolve(r.valid_value);
|
|
} else {
|
|
resolve("");
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
},
|
|
set_fetch_values: function set_fetch_values(df, docname, fetch_values) {
|
|
var fl = this.frm.fetch_dict[df.fieldname].fields;
|
|
for (var i = 0; i < fl.length; i++) {
|
|
frappe.model.set_value(df.parent, docname, fl[i], fetch_values[i], df.fieldtype);
|
|
}
|
|
}
|
|
});
|
|
|
|
if (Awesomplete) {
|
|
Awesomplete.prototype.get_item = function (value) {
|
|
return this._list.find(function (item) {
|
|
return item.value === value;
|
|
});
|
|
};
|
|
}
|
|
|
|
frappe.ui.form.ControlDynamicLink = frappe.ui.form.ControlLink.extend({
|
|
get_options: function get_options() {
|
|
if (this.df.get_options) {
|
|
return this.df.get_options();
|
|
}
|
|
if (this.docname == null && cur_dialog) {
|
|
return cur_dialog.get_value(this.df.options);
|
|
}
|
|
if (cur_frm == null && cur_list) {
|
|
return cur_list.wrapper.find("input[data-fieldname*=" + this.df.options + "]").val();
|
|
}
|
|
var options = frappe.model.get_value(this.df.parent, this.docname, this.df.options);
|
|
|
|
return options;
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
|
|
make_input: function make_input() {
|
|
this._super();
|
|
$(this.input_area).find("textarea").allowTabs().css({ "height": "400px", "font-family": "Monaco, \"Courier New\", monospace" });
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({
|
|
make_input: function make_input() {
|
|
this.has_input = true;
|
|
this.make_editor();
|
|
this.hide_elements_on_mobile();
|
|
this.setup_drag_drop();
|
|
this.setup_image_dialog();
|
|
this.setting_count = 0;
|
|
},
|
|
make_editor: function make_editor() {
|
|
var me = this;
|
|
this.editor = $("<div>").appendTo(this.input_area);
|
|
|
|
this.editor.summernote({
|
|
minHeight: 400,
|
|
toolbar: [['magic', ['style']], ['style', ['bold', 'italic', 'underline', 'clear']], ['fontsize', ['fontsize']], ['color', ['color']], ['para', ['ul', 'ol', 'paragraph', 'hr']], ['media', ['link', 'picture', 'video', 'table']], ['misc', ['fullscreen', 'codeview']]],
|
|
keyMap: {
|
|
pc: {
|
|
'CTRL+ENTER': ''
|
|
},
|
|
mac: {
|
|
'CMD+ENTER': ''
|
|
}
|
|
},
|
|
prettifyHtml: true,
|
|
dialogsInBody: true,
|
|
callbacks: {
|
|
onInit: function onInit() {
|
|
$(".note-editable[contenteditable='true']").one('focus', function () {
|
|
var $this = $(this);
|
|
$this.html($this.html() + '<br>');
|
|
});
|
|
},
|
|
onChange: function onChange(value) {
|
|
me.parse_validate_and_set_in_model(value);
|
|
},
|
|
onKeydown: function onKeydown(e) {
|
|
me._last_change_on = new Date();
|
|
var key = frappe.ui.keys.get_key(e);
|
|
|
|
if (['ctrl+b', 'meta+b'].indexOf(key) !== -1) {
|
|
e.stopPropagation();
|
|
}
|
|
if (key.indexOf('escape') !== -1) {
|
|
if (me.note_editor.hasClass('fullscreen')) {
|
|
me.note_editor.find('.note-btn.btn-fullscreen').trigger('click');
|
|
}
|
|
}
|
|
}
|
|
},
|
|
icons: {
|
|
'align': 'fa fa-align',
|
|
'alignCenter': 'fa fa-align-center',
|
|
'alignJustify': 'fa fa-align-justify',
|
|
'alignLeft': 'fa fa-align-left',
|
|
'alignRight': 'fa fa-align-right',
|
|
'indent': 'fa fa-indent',
|
|
'outdent': 'fa fa-outdent',
|
|
'arrowsAlt': 'fa fa-arrows-alt',
|
|
'bold': 'fa fa-bold',
|
|
'caret': 'caret',
|
|
'circle': 'fa fa-circle',
|
|
'close': 'fa fa-close',
|
|
'code': 'fa fa-code',
|
|
'eraser': 'fa fa-eraser',
|
|
'font': 'fa fa-font',
|
|
'frame': 'fa fa-frame',
|
|
'italic': 'fa fa-italic',
|
|
'link': 'fa fa-link',
|
|
'unlink': 'fa fa-chain-broken',
|
|
'magic': 'fa fa-magic',
|
|
'menuCheck': 'fa fa-check',
|
|
'minus': 'fa fa-minus',
|
|
'orderedlist': 'fa fa-list-ol',
|
|
'pencil': 'fa fa-pencil',
|
|
'picture': 'fa fa-image',
|
|
'question': 'fa fa-question',
|
|
'redo': 'fa fa-redo',
|
|
'square': 'fa fa-square',
|
|
'strikethrough': 'fa fa-strikethrough',
|
|
'subscript': 'fa fa-subscript',
|
|
'superscript': 'fa fa-superscript',
|
|
'table': 'fa fa-table',
|
|
'textHeight': 'fa fa-text-height',
|
|
'trash': 'fa fa-trash',
|
|
'underline': 'fa fa-underline',
|
|
'undo': 'fa fa-undo',
|
|
'unorderedlist': 'fa fa-list-ul',
|
|
'video': 'fa fa-video-camera'
|
|
}
|
|
});
|
|
this.note_editor = $(this.input_area).find('.note-editor');
|
|
},
|
|
setup_drag_drop: function setup_drag_drop() {
|
|
var me = this;
|
|
this.note_editor.on('dragenter dragover', false).on('drop', function (e) {
|
|
var dataTransfer = e.originalEvent.dataTransfer;
|
|
|
|
if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
|
|
me.note_editor.focus();
|
|
|
|
var files = [].slice.call(dataTransfer.files);
|
|
|
|
files.forEach(function (file) {
|
|
me.get_image(file, function (url) {
|
|
me.editor.summernote('insertImage', url, file.name);
|
|
});
|
|
});
|
|
}
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
});
|
|
},
|
|
get_image: function get_image(fileobj, callback) {
|
|
var freader = new FileReader(),
|
|
me = this;
|
|
|
|
freader.onload = function () {
|
|
var dataurl = freader.result;
|
|
|
|
var parts = dataurl.split(",");
|
|
parts[0] += ";filename=" + fileobj.name;
|
|
dataurl = parts[0] + ',' + parts[1];
|
|
callback(dataurl);
|
|
};
|
|
freader.readAsDataURL(fileobj);
|
|
},
|
|
hide_elements_on_mobile: function hide_elements_on_mobile() {
|
|
this.note_editor.find('.note-btn-underline,\
|
|
.note-btn-italic, .note-fontsize,\
|
|
.note-color, .note-height, .btn-codeview').addClass('hidden-xs');
|
|
if ($('.toggle-sidebar').is(':visible')) {
|
|
this.note_editor.find('.note-btn').attr('data-original-title', '');
|
|
}
|
|
},
|
|
get_input_value: function get_input_value() {
|
|
return this.editor ? this.editor.summernote('code') : '';
|
|
},
|
|
parse: function parse(value) {
|
|
if (value == null) value = "";
|
|
return frappe.dom.remove_script_and_style(value);
|
|
},
|
|
set_formatted_input: function set_formatted_input(value) {
|
|
if (value !== this.get_input_value()) {
|
|
this.set_in_editor(value);
|
|
}
|
|
},
|
|
set_in_editor: function set_in_editor(value) {
|
|
var _this5 = this;
|
|
|
|
if (this.setting_count > 2) {
|
|
return;
|
|
}
|
|
|
|
this.setting_count += 1;
|
|
|
|
var time_since_last_keystroke = moment() - moment(this._last_change_on);
|
|
|
|
if (!this._last_change_on || time_since_last_keystroke > 3000) {
|
|
setTimeout(function () {
|
|
return _this5.setting_count = 0;
|
|
}, 500);
|
|
this.editor.summernote('code', value || '');
|
|
} else {
|
|
this._setting_value = setInterval(function () {
|
|
if (time_since_last_keystroke > 3000) {
|
|
if (_this5.last_value !== _this5.get_input_value()) {
|
|
_this5.editor.summernote('code', _this5.last_value || '');
|
|
}
|
|
clearInterval(_this5._setting_value);
|
|
_this5._setting_value = null;
|
|
_this5.setting_count = 0;
|
|
}
|
|
}, 1000);
|
|
}
|
|
},
|
|
set_focus: function set_focus() {
|
|
return this.editor.summernote('focus');
|
|
},
|
|
set_upload_options: function set_upload_options() {
|
|
var me = this;
|
|
this.upload_options = {
|
|
parent: this.image_dialog.get_field("upload_area").$wrapper,
|
|
args: {},
|
|
max_width: this.df.max_width,
|
|
max_height: this.df.max_height,
|
|
options: "Image",
|
|
btn: this.image_dialog.set_primary_action(__("Insert")),
|
|
on_no_attach: function on_no_attach() {
|
|
var selected = me.image_dialog.get_field("select").get_value();
|
|
if (selected) {
|
|
me.editor.summernote('insertImage', selected);
|
|
me.image_dialog.hide();
|
|
} else {
|
|
frappe.msgprint(__("Please attach a file or set a URL"));
|
|
}
|
|
},
|
|
callback: function callback(attachment, r) {
|
|
me.editor.summernote('insertImage', attachment.file_url, attachment.file_name);
|
|
me.image_dialog.hide();
|
|
},
|
|
onerror: function onerror() {
|
|
me.image_dialog.hide();
|
|
}
|
|
};
|
|
|
|
if ("is_private" in this.df) {
|
|
this.upload_options.is_private = this.df.is_private;
|
|
}
|
|
|
|
if (this.frm) {
|
|
this.upload_options.args = {
|
|
from_form: 1,
|
|
doctype: this.frm.doctype,
|
|
docname: this.frm.docname
|
|
};
|
|
} else {
|
|
this.upload_options.on_attach = function (fileobj, dataurl) {
|
|
me.editor.summernote('insertImage', dataurl);
|
|
me.image_dialog.hide();
|
|
frappe.hide_progress();
|
|
};
|
|
}
|
|
},
|
|
|
|
setup_image_dialog: function setup_image_dialog() {
|
|
var _this6 = this;
|
|
|
|
this.note_editor.find('[data-original-title="Image"]').on('click', function (e) {
|
|
if (!_this6.image_dialog) {
|
|
_this6.image_dialog = new frappe.ui.Dialog({
|
|
title: __("Image"),
|
|
fields: [{ fieldtype: "HTML", fieldname: "upload_area" }, { fieldtype: "HTML", fieldname: "or_attach", options: __("Or") }, { fieldtype: "Select", fieldname: "select", label: __("Select from existing attachments") }]
|
|
});
|
|
}
|
|
|
|
_this6.image_dialog.show();
|
|
_this6.image_dialog.get_field("upload_area").$wrapper.empty();
|
|
|
|
var attachments = _this6.frm && _this6.frm.attachments.get_attachments() || [];
|
|
var select = _this6.image_dialog.get_field("select");
|
|
if (attachments.length) {
|
|
attachments = $.map(attachments, function (o) {
|
|
return o.file_url;
|
|
});
|
|
select.df.options = [""].concat(attachments);
|
|
select.toggle(true);
|
|
_this6.image_dialog.get_field("or_attach").toggle(true);
|
|
select.refresh();
|
|
} else {
|
|
_this6.image_dialog.get_field("or_attach").toggle(false);
|
|
select.toggle(false);
|
|
}
|
|
select.$input.val("");
|
|
|
|
_this6.set_upload_options();
|
|
frappe.upload.make(_this6.upload_options);
|
|
});
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlTable = frappe.ui.form.Control.extend({
|
|
make: function make() {
|
|
this._super();
|
|
|
|
this.grid = new frappe.ui.form.Grid({
|
|
frm: this.frm,
|
|
df: this.df,
|
|
perm: this.perm || this.frm && this.frm.perm || this.df.perm,
|
|
parent: this.wrapper
|
|
});
|
|
if (this.frm) {
|
|
this.frm.grids[this.frm.grids.length] = this;
|
|
}
|
|
|
|
if (this.df.description) {
|
|
$('<p class="text-muted small">' + __(this.df.description) + '</p>').appendTo(this.wrapper);
|
|
}
|
|
},
|
|
refresh_input: function refresh_input() {
|
|
this.grid.refresh();
|
|
},
|
|
get_value: function get_value() {
|
|
if (this.grid) {
|
|
return this.grid.get_data();
|
|
}
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.ControlSignature = frappe.ui.form.ControlData.extend({
|
|
saving: false,
|
|
loading: false,
|
|
make: function make() {
|
|
var me = this;
|
|
this._super();
|
|
|
|
this.$pad = $('<div class="signature-field"></div>').appendTo(me.wrapper).jSignature({ height: 300, width: "100%", "lineWidth": 0.8 }).on('change', this.on_save_sign.bind(this));
|
|
|
|
this.img_wrapper = $("<div class=\"signature-display\">\n\t\t\t<div class=\"missing-image attach-missing-image\">\n\t\t\t\t<i class=\"octicon octicon-circle-slash\"></i>\n\t\t\t</div></div>").appendTo(this.wrapper);
|
|
this.img = $("<img class='img-responsive attach-image-display'>").appendTo(this.img_wrapper).toggle(false);
|
|
|
|
this.$btnWrapper = $("<div class=\"signature-btn-row\">\n\t\t\t<a href=\"#\" type=\"button\" class=\"signature-reset btn btn-default\">\n\t\t\t<i class=\"glyphicon glyphicon-repeat\"></i></a>").appendTo(this.$pad).on("click", '.signature-reset', function () {
|
|
me.on_reset_sign();
|
|
return false;
|
|
});
|
|
},
|
|
refresh_input: function refresh_input(e) {
|
|
this.$wrapper.find(".control-input").toggle(false);
|
|
this.set_editable(this.get_status() == "Write");
|
|
this.load_pad();
|
|
if (this.get_status() == "Read") {
|
|
$(this.disp_area).toggle(false);
|
|
}
|
|
},
|
|
set_image: function set_image(value) {
|
|
if (value) {
|
|
$(this.img_wrapper).find(".missing-image").toggle(false);
|
|
this.img.attr("src", value).toggle(true);
|
|
} else {
|
|
$(this.img_wrapper).find(".missing-image").toggle(true);
|
|
this.img.toggle(false);
|
|
}
|
|
},
|
|
load_pad: function load_pad() {
|
|
if (this.saving) return;
|
|
|
|
var value = this.get_value();
|
|
|
|
if (this.$pad) {
|
|
this.loading = true;
|
|
|
|
this.$pad.jSignature('reset');
|
|
if (value) {
|
|
try {
|
|
this.$pad.jSignature('setData', value);
|
|
this.set_image(value);
|
|
} catch (e) {
|
|
console.log("Cannot set data for signature", value, e);
|
|
}
|
|
}
|
|
|
|
this.loading = false;
|
|
}
|
|
},
|
|
set_editable: function set_editable(editable) {
|
|
this.$pad.toggle(editable);
|
|
this.img_wrapper.toggle(!editable);
|
|
this.$btnWrapper.toggle(editable);
|
|
if (editable) {
|
|
this.$btnWrapper.addClass('editing');
|
|
} else {
|
|
this.$btnWrapper.removeClass('editing');
|
|
}
|
|
},
|
|
set_my_value: function set_my_value(value) {
|
|
if (this.saving || this.loading) return;
|
|
this.saving = true;
|
|
this.set_value(value);
|
|
this.value = value;
|
|
this.saving = false;
|
|
},
|
|
get_value: function get_value() {
|
|
return this.value ? this.value : this.get_model_value();
|
|
},
|
|
|
|
on_reset_sign: function on_reset_sign() {
|
|
this.$pad.jSignature("reset");
|
|
this.set_my_value("");
|
|
},
|
|
|
|
on_save_sign: function on_save_sign() {
|
|
if (this.saving || this.loading) return;
|
|
var base64_img = this.$pad.jSignature("getData");
|
|
this.set_my_value(base64_img);
|
|
this.set_image(this.get_value());
|
|
}
|
|
});
|
|
|
|
frappe.ui.form.fieldtype_icons = {
|
|
"Date": "fa fa-calendar",
|
|
"Time": "fa fa-time",
|
|
"Datetime": "fa fa-time",
|
|
"Code": "fa fa-code",
|
|
"Select": "fa fa-flag"
|
|
};
|
|
|
|
frappe.ui.form.LinkSelector = Class.extend({
|
|
init: function init(opts) {
|
|
$.extend(this, opts);
|
|
|
|
var me = this;
|
|
if (this.doctype != "[Select]") {
|
|
frappe.model.with_doctype(this.doctype, function (r) {
|
|
me.make();
|
|
});
|
|
} else {
|
|
this.make();
|
|
}
|
|
},
|
|
make: function make() {
|
|
var me = this;
|
|
|
|
this.dialog = new frappe.ui.Dialog({
|
|
title: __("Select {0}", [this.doctype == '[Select]' ? __("value") : __(this.doctype)]),
|
|
fields: [{
|
|
fieldtype: "Data", fieldname: "txt", label: __("Beginning with"),
|
|
description: __("You can use wildcard %")
|
|
}, {
|
|
fieldtype: "HTML", fieldname: "results"
|
|
}],
|
|
primary_action_label: __("Search"),
|
|
primary_action: function primary_action() {
|
|
me.search();
|
|
}
|
|
});
|
|
|
|
if (this.txt) this.dialog.fields_dict.txt.set_input(this.txt);
|
|
|
|
this.dialog.get_input("txt").on("keypress", function (e) {
|
|
if (e.which === 13) {
|
|
me.search();
|
|
}
|
|
});
|
|
this.dialog.show();
|
|
this.search();
|
|
},
|
|
search: function search() {
|
|
var args = {
|
|
txt: this.dialog.fields_dict.txt.get_value(),
|
|
searchfield: "name"
|
|
};
|
|
var me = this;
|
|
|
|
if (this.target.set_custom_query) {
|
|
this.target.set_custom_query(args);
|
|
}
|
|
|
|
if (this.target.is_grid && this.target.fieldinfo[this.fieldname] && this.target.fieldinfo[this.fieldname].get_query) {
|
|
$.extend(args, this.target.fieldinfo[this.fieldname].get_query(cur_frm.doc));
|
|
}
|
|
|
|
frappe.link_search(this.doctype, args, function (r) {
|
|
var parent = me.dialog.fields_dict.results.$wrapper;
|
|
parent.empty();
|
|
if (r.values.length) {
|
|
$.each(r.values, function (i, v) {
|
|
var row = $(repl('<div class="row link-select-row">\
|
|
<div class="col-xs-4">\
|
|
<b><a href="#">%(name)s</a></b></div>\
|
|
<div class="col-xs-8">\
|
|
<span class="text-muted">%(values)s</span></div>\
|
|
</div>', {
|
|
name: v[0],
|
|
values: v.splice(1).join(", ")
|
|
})).appendTo(parent);
|
|
|
|
row.find("a").attr('data-value', v[0]).click(function () {
|
|
var value = $(this).attr("data-value");
|
|
var $link = this;
|
|
if (me.target.is_grid) {
|
|
me.set_in_grid(value);
|
|
} else {
|
|
if (me.target.doctype) me.target.parse_validate_and_set_in_model(value);else {
|
|
me.target.set_input(value);
|
|
me.target.$input.trigger("change");
|
|
}
|
|
me.dialog.hide();
|
|
}
|
|
return false;
|
|
});
|
|
});
|
|
} else {
|
|
$('<p><br><span class="text-muted">' + __("No Results") + '</span>' + (frappe.model.can_create(me.doctype) ? '<br><br><a class="new-doc btn btn-default btn-sm">' + __("Make a new {0}", [__(me.doctype)]) + "</a>" : '') + '</p>').appendTo(parent).find(".new-doc").click(function () {
|
|
me.target.new_doc();
|
|
});
|
|
}
|
|
}, this.dialog.get_primary_btn());
|
|
},
|
|
set_in_grid: function set_in_grid(value) {
|
|
var me = this,
|
|
updated = false;
|
|
if (this.qty_fieldname) {
|
|
frappe.prompt({
|
|
fieldname: "qty", fieldtype: "Float", label: "Qty",
|
|
"default": 1, reqd: 1
|
|
}, function (data) {
|
|
$.each(me.target.frm.doc[me.target.df.fieldname] || [], function (i, d) {
|
|
if (d[me.fieldname] === value) {
|
|
frappe.model.set_value(d.doctype, d.name, me.qty_fieldname, data.qty);
|
|
frappe.show_alert(__("Added {0} ({1})", [value, d[me.qty_fieldname]]));
|
|
updated = true;
|
|
return false;
|
|
}
|
|
});
|
|
if (!updated) {
|
|
var d = me.target.add_new_row();
|
|
frappe.model.set_value(d.doctype, d.name, me.fieldname, value);
|
|
frappe.after_ajax(function () {
|
|
setTimeout(function () {
|
|
frappe.model.set_value(d.doctype, d.name, me.qty_fieldname, data.qty);
|
|
frappe.show_alert(__("Added {0} ({1})", [value, data.qty]));
|
|
}, 100);
|
|
});
|
|
}
|
|
}, __("Set Quantity"), __("Set"));
|
|
} else {
|
|
var d = me.target.add_new_row();
|
|
frappe.model.set_value(d.doctype, d.name, me.fieldname, value);
|
|
frappe.show_alert(__("{0} added", [value]));
|
|
}
|
|
}
|
|
});
|
|
|
|
frappe.link_search = function (doctype, args, _callback, btn) {
|
|
if (!args) {
|
|
args = {
|
|
txt: ''
|
|
};
|
|
}
|
|
args.doctype = doctype;
|
|
if (!args.searchfield) {
|
|
args.searchfield = 'name';
|
|
}
|
|
|
|
frappe.call({
|
|
method: "frappe.desk.search.search_widget",
|
|
type: "GET",
|
|
args: args,
|
|
callback: function callback(r) {
|
|
_callback && _callback(r);
|
|
},
|
|
btn: btn
|
|
});
|
|
};
|
|
|
|
frappe.ui.form.MultiSelectDialog = Class.extend({
|
|
init: function init(opts) {
|
|
$.extend(this, opts);
|
|
|
|
var me = this;
|
|
if (this.doctype != "[Select]") {
|
|
frappe.model.with_doctype(this.doctype, function (r) {
|
|
me.make();
|
|
});
|
|
} else {
|
|
this.make();
|
|
}
|
|
},
|
|
make: function make() {
|
|
var me = this;
|
|
|
|
this.page_length = 20;
|
|
|
|
var fields = [{
|
|
fieldtype: "Data",
|
|
label: __("Search Term"),
|
|
fieldname: "search_term"
|
|
}, {
|
|
fieldtype: "Column Break"
|
|
}];
|
|
var count = 0;
|
|
if (!this.date_field) {
|
|
this.date_field = "transaction_date";
|
|
}
|
|
Object.keys(this.setters).forEach(function (setter) {
|
|
fields.push({
|
|
fieldtype: me.target.fields_dict[setter].df.fieldtype,
|
|
label: me.target.fields_dict[setter].df.label,
|
|
fieldname: setter,
|
|
options: me.target.fields_dict[setter].df.options,
|
|
default: me.setters[setter]
|
|
});
|
|
if (count++ < Object.keys(me.setters).length) {
|
|
fields.push({ fieldtype: "Column Break" });
|
|
}
|
|
});
|
|
|
|
fields = fields.concat([{
|
|
"fieldname": "date_range",
|
|
"label": __("Date Range"),
|
|
"fieldtype": "DateRange"
|
|
}, { fieldtype: "Section Break" }, { fieldtype: "HTML", fieldname: "results_area" }, { fieldtype: "Button", fieldname: "make_new", label: __("Make a new " + me.doctype) }]);
|
|
|
|
var doctype_plural = !this.doctype.endsWith('y') ? this.doctype + 's' : this.doctype.slice(0, -1) + 'ies';
|
|
|
|
this.dialog = new frappe.ui.Dialog({
|
|
title: __("Select {0}", [this.doctype == '[Select]' ? __("value") : __(doctype_plural)]),
|
|
fields: fields,
|
|
primary_action_label: __("Get Items"),
|
|
primary_action: function primary_action() {
|
|
me.action(me.get_checked_values(), me.args);
|
|
}
|
|
});
|
|
|
|
this.$parent = $(this.dialog.body);
|
|
this.$wrapper = this.dialog.fields_dict.results_area.$wrapper.append("<div class=\"results\"\n\t\t\tstyle=\"border: 1px solid #d1d8dd; border-radius: 3px; height: 300px; overflow: auto;\"></div>");
|
|
this.$results = this.$wrapper.find('.results');
|
|
this.$make_new_btn = this.dialog.fields_dict.make_new.$wrapper;
|
|
|
|
this.$placeholder = $("<div class=\"multiselect-empty-state\">\n\t\t\t\t\t<span class=\"text-center\" style=\"margin-top: -40px;\">\n\t\t\t\t\t\t<i class=\"fa fa-2x fa-tags text-extra-muted\"></i>\n\t\t\t\t\t\t<p class=\"text-extra-muted\">No " + this.doctype + " found</p>\n\t\t\t\t\t\t<button class=\"btn btn-default btn-xs text-muted\" data-fieldtype=\"Button\"\n\t\t\t\t\t\t\tdata-fieldname=\"make_new\" placeholder=\"\" value=\"\">Make a new " + this.doctype + "</button>\n\t\t\t\t\t</span>\n\t\t\t\t</div>");
|
|
|
|
this.args = {};
|
|
|
|
this.bind_events();
|
|
this.get_results();
|
|
this.dialog.show();
|
|
},
|
|
|
|
bind_events: function bind_events() {
|
|
var _this = this;
|
|
|
|
var me = this;
|
|
this.$results.on('click', '.list-item-container', function (e) {
|
|
if (!$(e.target).is(':checkbox') && !$(e.target).is('a')) {
|
|
$(this).find(':checkbox').trigger('click');
|
|
}
|
|
});
|
|
this.$results.on('click', '.list-item--head :checkbox', function (e) {
|
|
_this.$results.find('.list-item-container .list-row-check').prop("checked", $(e.target).is(':checked'));
|
|
});
|
|
|
|
this.$parent.find('.input-with-feedback').on('change', function (e) {
|
|
_this.get_results();
|
|
});
|
|
|
|
this.$parent.find('[data-fieldname="date_range"]').on('blur', function (e) {
|
|
_this.get_results();
|
|
});
|
|
|
|
this.$parent.find('[data-fieldname="search_term"]').on('input', function (e) {
|
|
var $this = $(_this);
|
|
clearTimeout($this.data('timeout'));
|
|
$this.data('timeout', setTimeout(function () {
|
|
me.get_results();
|
|
}, 300));
|
|
});
|
|
|
|
this.$parent.on('click', '.btn[data-fieldname="make_new"]', function (e) {
|
|
frappe.route_options = {};
|
|
Object.keys(_this.setters).forEach(function (setter) {
|
|
frappe.route_options[setter] = me.dialog.fields_dict[setter].get_value() || undefined;
|
|
});
|
|
frappe.new_doc(_this.doctype, true);
|
|
});
|
|
},
|
|
|
|
get_checked_values: function get_checked_values() {
|
|
return this.$results.find('.list-item-container').map(function () {
|
|
if ($(this).find('.list-row-check:checkbox:checked').length > 0) {
|
|
return $(this).attr('data-item-name');
|
|
}
|
|
}).get();
|
|
},
|
|
|
|
make_list_row: function make_list_row() {
|
|
var result = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
|
|
var me = this;
|
|
|
|
var head = Object.keys(result).length === 0;
|
|
|
|
var contents = "";
|
|
var columns = ["name"].concat(Object.keys(this.setters)).concat("Date");
|
|
columns.forEach(function (column) {
|
|
contents += "<div class=\"list-item__content ellipsis\">\n\t\t\t\t" + (head ? "<span class=\"ellipsis\">" + __(frappe.model.unscrub(column)) + "</span>" : column !== "name" ? "<span class=\"ellipsis\">" + __(result[column]) + "</span>" : "<a href=\"" + ("#Form/" + me.doctype + "/" + result[column]) + "\" class=\"list-id ellipsis\">\n\t\t\t\t\t\t\t" + __(result[column]) + "</a>") + "\n\t\t\t</div>";
|
|
});
|
|
|
|
var $row = $("<div class=\"list-item\">\n\t\t\t<div class=\"list-item__content\" style=\"flex: 0 0 10px;\">\n\t\t\t\t<input type=\"checkbox\" class=\"list-row-check\" " + (result.checked ? 'checked' : '') + ">\n\t\t\t</div>\n\t\t\t" + contents + "\n\t\t</div>");
|
|
|
|
head ? $row.addClass('list-item--head') : $row = $("<div class=\"list-item-container\" data-item-name=\"" + result.name + "\"></div>").append($row);
|
|
return $row;
|
|
},
|
|
|
|
render_result_list: function render_result_list(results) {
|
|
var more = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
|
|
var me = this;
|
|
this.$results.empty();
|
|
if (results.length === 0) {
|
|
this.$make_new_btn.addClass('hide');
|
|
this.$results.append(me.$placeholder);
|
|
return;
|
|
}
|
|
this.$make_new_btn.removeClass('hide');
|
|
|
|
this.$results.append(this.make_list_row());
|
|
results.forEach(function (result) {
|
|
me.$results.append(me.make_list_row(result));
|
|
});
|
|
if (more) {
|
|
var message = __("Only {0} entries shown. Please filter for more specific results.", [this.page_length]);
|
|
me.$results.append($("<div class=\"text-muted small\" style=\"text-align: center;\n\t\t\t\tmargin: 10px;\">" + message + "</div>"));
|
|
}
|
|
},
|
|
|
|
get_results: function get_results() {
|
|
var me = this;
|
|
|
|
var filters = this.get_query().filters;
|
|
Object.keys(this.setters).forEach(function (setter) {
|
|
filters[setter] = me.dialog.fields_dict[setter].get_value() || undefined;
|
|
me.args[setter] = filters[setter];
|
|
});
|
|
|
|
var date_val = this.dialog.fields_dict["date_range"].get_value();
|
|
if (date_val) {
|
|
filters[this.date_field] = ['Between', me.dialog.fields_dict["date_range"].parse(date_val)];
|
|
}
|
|
|
|
var args = {
|
|
doctype: me.doctype,
|
|
txt: me.dialog.fields_dict["search_term"].get_value(),
|
|
filters: filters,
|
|
filter_fields: Object.keys(me.setters).concat([me.date_field]),
|
|
page_length: this.page_length + 1,
|
|
query: this.get_query().query,
|
|
as_dict: 1
|
|
};
|
|
frappe.call({
|
|
type: "GET",
|
|
method: 'frappe.desk.search.search_widget',
|
|
no_spinner: true,
|
|
args: args,
|
|
callback: function callback(r) {
|
|
var results = [],
|
|
more = 0;
|
|
if (r.values.length) {
|
|
if (r.values.length > me.page_length) {
|
|
r.values.pop();
|
|
more = 1;
|
|
}
|
|
r.values.forEach(function (result) {
|
|
if (me.date_field in result) {
|
|
result["Date"] = result[me.date_field];
|
|
}
|
|
result.checked = 0;
|
|
result.parsed_date = Date.parse(result["Date"]);
|
|
results.push(result);
|
|
});
|
|
results.map(function (result) {
|
|
result["Date"] = frappe.format(result["Date"], { "fieldtype": "Date" });
|
|
});
|
|
|
|
results.sort(function (a, b) {
|
|
return a.parsed_date - b.parsed_date;
|
|
});
|
|
|
|
results[0].checked = 1;
|
|
}
|
|
me.render_result_list(results, more);
|
|
}
|
|
});
|
|
}
|
|
|
|
});
|
|
|
|
frappe.provide('frappe.ui');
|
|
|
|
var cur_dialog;
|
|
|
|
frappe.ui.open_dialogs = [];
|
|
frappe.ui.Dialog = frappe.ui.FieldGroup.extend({
|
|
init: function init(opts) {
|
|
this.display = false;
|
|
this.is_dialog = true;
|
|
|
|
$.extend(this, opts);
|
|
this._super();
|
|
this.make();
|
|
},
|
|
make: function make() {
|
|
this.$wrapper = frappe.get_modal("", "");
|
|
this.wrapper = this.$wrapper.find('.modal-dialog').get(0);
|
|
this.make_head();
|
|
this.body = this.$wrapper.find(".modal-body").get(0);
|
|
this.header = this.$wrapper.find(".modal-header");
|
|
|
|
this._super();
|
|
|
|
if (this.primary_action) {
|
|
this.set_primary_action(this.primary_action_label || __("Submit"), this.primary_action);
|
|
}
|
|
|
|
if (this.secondary_action_label) {
|
|
this.get_close_btn().html(this.secondary_action_label);
|
|
}
|
|
|
|
var me = this;
|
|
this.$wrapper.on("hide.bs.modal", function () {
|
|
me.display = false;
|
|
if (frappe.ui.open_dialogs[frappe.ui.open_dialogs.length - 1] === me) {
|
|
frappe.ui.open_dialogs.pop();
|
|
if (frappe.ui.open_dialogs.length) {
|
|
cur_dialog = frappe.ui.open_dialogs[frappe.ui.open_dialogs.length - 1];
|
|
} else {
|
|
cur_dialog = null;
|
|
}
|
|
}
|
|
me.onhide && me.onhide();
|
|
me.on_hide && me.on_hide();
|
|
}).on("shown.bs.modal", function () {
|
|
me.display = true;
|
|
cur_dialog = me;
|
|
frappe.ui.open_dialogs.push(me);
|
|
me.focus_on_first_input();
|
|
me.on_page_show && me.on_page_show();
|
|
}).on('scroll', function () {
|
|
var $input = $('input:focus');
|
|
if ($input.length && ['Date', 'Datetime', 'Time'].includes($input.attr('data-fieldtype'))) {
|
|
$input.blur();
|
|
}
|
|
});
|
|
},
|
|
focus_on_first_input: function focus_on_first_input() {
|
|
if (this.no_focus) return;
|
|
$.each(this.fields_list, function (i, f) {
|
|
if (!in_list(['Date', 'Datetime', 'Time'], f.df.fieldtype) && f.set_focus) {
|
|
f.set_focus();
|
|
return false;
|
|
}
|
|
});
|
|
},
|
|
get_primary_btn: function get_primary_btn() {
|
|
return this.$wrapper.find(".modal-header .btn-primary");
|
|
},
|
|
set_primary_action: function set_primary_action(label, click) {
|
|
this.has_primary_action = true;
|
|
var me = this;
|
|
return this.get_primary_btn().removeClass("hide").html(label).click(function () {
|
|
me.primary_action_fulfilled = true;
|
|
|
|
var values = me.get_values();
|
|
if (!values) return;
|
|
click.apply(me, [values]);
|
|
});
|
|
},
|
|
make_head: function make_head() {
|
|
var me = this;
|
|
this.set_title(this.title);
|
|
},
|
|
set_title: function set_title(t) {
|
|
this.$wrapper.find(".modal-title").html(t);
|
|
},
|
|
show: function show() {
|
|
this.$wrapper.modal("show");
|
|
this.primary_action_fulfilled = false;
|
|
},
|
|
hide: function hide(from_event) {
|
|
this.$wrapper.modal("hide");
|
|
},
|
|
get_close_btn: function get_close_btn() {
|
|
return this.$wrapper.find(".btn-modal-close");
|
|
},
|
|
no_cancel: function no_cancel() {
|
|
this.get_close_btn().toggle(false);
|
|
},
|
|
cancel: function cancel() {
|
|
this.get_close_btn().trigger("click");
|
|
}
|
|
}); |