From 87754732e6b7aa3ffabfd177926065707975ae34 Mon Sep 17 00:00:00 2001 From: administrator Date: Thu, 25 Aug 2022 10:32:22 +0000 Subject: [PATCH] script validation --- smart_service/public/build.json | 17 +- smart_service/public/js/capture.js | 158 ++ smart_service/public/js/control.js | 55 + smart_service/public/js/datepicker.min.js | 2 + smart_service/public/js/datepicker_i18n.js | 62 + smart_service/public/js/form.js | 1796 +++++++++++++++++ smart_service/public/js/formview.js | 110 + smart_service/public/js/meta_tag.js | 20 + smart_service/public/js/request.js | 625 ++++++ .../public/js/templates/address_list.html | 22 + .../public/js/templates/contact_list.html | 54 + .../public/js/templates/form_dashboard.html | 20 + .../public/js/templates/form_footer.html | 9 + .../public/js/templates/form_links.html | 33 + .../public/js/templates/form_sidebar.html | 160 ++ .../public/js/templates/print_layout.html | 56 + .../public/js/templates/report_links.html | 23 + .../public/js/templates/set_sharing.html | 71 + .../js/templates/timeline_message_box.html | 92 + .../public/js/templates/users_in_sidebar.html | 13 + 20 files changed, 3397 insertions(+), 1 deletion(-) create mode 100644 smart_service/public/js/capture.js create mode 100644 smart_service/public/js/control.js create mode 100644 smart_service/public/js/datepicker.min.js create mode 100644 smart_service/public/js/datepicker_i18n.js create mode 100644 smart_service/public/js/form.js create mode 100644 smart_service/public/js/formview.js create mode 100644 smart_service/public/js/meta_tag.js create mode 100644 smart_service/public/js/request.js create mode 100644 smart_service/public/js/templates/address_list.html create mode 100644 smart_service/public/js/templates/contact_list.html create mode 100644 smart_service/public/js/templates/form_dashboard.html create mode 100644 smart_service/public/js/templates/form_footer.html create mode 100644 smart_service/public/js/templates/form_links.html create mode 100644 smart_service/public/js/templates/form_sidebar.html create mode 100644 smart_service/public/js/templates/print_layout.html create mode 100644 smart_service/public/js/templates/report_links.html create mode 100644 smart_service/public/js/templates/set_sharing.html create mode 100644 smart_service/public/js/templates/timeline_message_box.html create mode 100644 smart_service/public/js/templates/users_in_sidebar.html diff --git a/smart_service/public/build.json b/smart_service/public/build.json index 1940a31..7360c8f 100644 --- a/smart_service/public/build.json +++ b/smart_service/public/build.json @@ -1,6 +1,21 @@ { "css/smart_service.min.css": [ "public/css/smart_service.css" + ], + "js/control.min.js": [ + "public/js/datepicker.min.js", + "public/js/datepicker_i18n.js", + "public/js/capture.js", + "public/js/control.js" + ], + "js/form.min.js": [ + "public/js/templates/**.html", + "public/js/control.js", + "public/js/formview.js", + "public/js/form.js", + "public/js/meta_tag.js" + ], + "js/request.min.js": [ + "public/js/request.js" ] - } \ No newline at end of file diff --git a/smart_service/public/js/capture.js b/smart_service/public/js/capture.js new file mode 100644 index 0000000..771acd7 --- /dev/null +++ b/smart_service/public/js/capture.js @@ -0,0 +1,158 @@ +// frappe.ui.Capture +// Author - Achilles Rasquinha + +/** + * @description Converts a canvas, image or a video to a data URL string. + * + * @param {HTMLElement} element - canvas, img or video. + * @returns {string} - The data URL string. + * + * @example + * frappe._.get_data_uri(video) + * // returns "data:image/pngbase64,..." + */ + frappe._.get_data_uri = element => { + + const width = element.videoWidth; + const height = element.videoHeight; + + const $canvas = $(''); + $canvas[0].width = width; + $canvas[0].height = height; + + const context = $canvas[0].getContext('2d'); + context.drawImage(element, 0, 0, width, height); + + const data_uri = $canvas[0].toDataURL('image/png'); + + return data_uri; +}; + +/** + * @description Frappe's Capture object. + * + * @example + * const capture = frappe.ui.Capture() + * capture.show() + * + * capture.click((data_uri) => { + * // do stuff + * }) + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Taking_still_photos + */ +frappe.ui.Capture = class { + constructor(options = {}) { + this.options = frappe.ui.Capture.OPTIONS; + this.set_options(options); + } + + set_options(options) { + this.options = { ...frappe.ui.Capture.OPTIONS, ...options }; + + return this; + } + + render() { + return navigator.mediaDevices.getUserMedia({ video: true }).then(stream => { + this.stream = stream; + + this.dialog = new frappe.ui.Dialog({ + title: this.options.title, + animate: this.options.animate, + on_hide: () => this.stop_media_stream() + }); + + this.dialog.get_close_btn().on('click', () => { + this.hide(); + }); + + const set_take_photo_action = () => { + this.dialog.set_primary_action(__('Take Photo'), () => { + const data_url = frappe._.get_data_uri(video); + $e.find('.fc-p').attr('src', data_url); + + $e.find('.fc-s').hide(); + $e.find('.fc-p').show(); + + this.dialog.set_secondary_action_label(__('Retake')); + this.dialog.get_secondary_btn().show(); + + this.dialog.set_primary_action(__('Submit'), () => { + this.hide(); + if (this.callback) this.callback(data_url); + }); + }); + }; + + set_take_photo_action(); + + this.dialog.set_secondary_action(() => { + $e.find('.fc-p').hide(); + $e.find('.fc-s').show(); + + this.dialog.get_secondary_btn().hide(); + this.dialog.get_primary_btn().off('click'); + set_take_photo_action(); + }); + + this.dialog.get_secondary_btn().hide(); + + const $e = $(frappe.ui.Capture.TEMPLATE); + + const video = $e.find('video')[0]; + video.srcObject = this.stream; + video.play(); + const $container = $(this.dialog.body); + + $container.html($e); + }); + } + + show() { + this.render() + .then(() => { + this.dialog.show(); + }) + .catch(err => { + if (this.options.error) { + frappe.show_alert(frappe.ui.Capture.ERR_MESSAGE, 3); + } + + throw err; + }); + } + + hide() { + if (this.dialog) this.dialog.hide(); + this.stop_media_stream(); + } + + stop_media_stream() { + if (this.stream) { + this.stream.getTracks().forEach((track) => { + track.stop(); + }); + } + } + + submit(fn) { + this.callback = fn; + } +}; +frappe.ui.Capture.OPTIONS = { + title: __("Camera"), + animate: false, + error: false +}; +frappe.ui.Capture.ERR_MESSAGE = __('Unable to load camera.'); +frappe.ui.Capture.TEMPLATE = ` +
+
+
+ + +
+
+
+`; diff --git a/smart_service/public/js/control.js b/smart_service/public/js/control.js new file mode 100644 index 0000000..a250e04 --- /dev/null +++ b/smart_service/public/js/control.js @@ -0,0 +1,55 @@ +import 'frappe/public/js/frappe/form/controls/base_control'; +import 'frappe/public/js/frappe/form/controls/base_input'; +import 'frappe/public/js/frappe/form/controls/data'; +import 'frappe/public/js/frappe/form/controls/int'; +import 'frappe/public/js/frappe/form/controls/float'; +import 'frappe/public/js/frappe/form/controls/currency'; +import 'frappe/public/js/frappe/form/controls/date'; +import 'frappe/public/js/frappe/form/controls/time'; +import 'frappe/public/js/frappe/form/controls/datetime'; +import 'frappe/public/js/frappe/form/controls/date_range'; +import 'frappe/public/js/frappe/form/controls/select'; +import 'frappe/public/js/frappe/form/controls/link'; +import 'frappe/public/js/frappe/form/controls/dynamic_link'; +import 'frappe/public/js/frappe/form/controls/text'; +import 'frappe/public/js/frappe/form/controls/code'; +import 'frappe/public/js/frappe/form/controls/text_editor'; +import 'frappe/public/js/frappe/form/controls/comment'; +import 'frappe/public/js/frappe/form/controls/check'; +import 'frappe/public/js/frappe/form/controls/image'; +import 'frappe/public/js/frappe/form/controls/attach'; +import 'frappe/public/js/frappe/form/controls/attach_image'; +import 'frappe/public/js/frappe/form/controls/table'; +import 'frappe/public/js/frappe/form/controls/color'; +import 'frappe/public/js/frappe/form/controls/signature'; +import 'frappe/public/js/frappe/form/controls/password'; +import 'frappe/public/js/frappe/form/controls/read_only'; +import 'frappe/public/js/frappe/form/controls/button'; +import 'frappe/public/js/frappe/form/controls/html'; +import 'frappe/public/js/frappe/form/controls/markdown_editor'; +import 'frappe/public/js/frappe/form/controls/html_editor'; +import 'frappe/public/js/frappe/form/controls/heading'; +import 'frappe/public/js/frappe/form/controls/autocomplete'; +import 'frappe/public/js/frappe/form/controls/barcode'; +import 'frappe/public/js/frappe/form/controls/geolocation'; +import 'frappe/public/js/frappe/form/controls/multiselect'; +import 'frappe/public/js/frappe/form/controls/multicheck'; +import 'frappe/public/js/frappe/form/controls/table_multiselect'; +import 'frappe/public/js/frappe/form/controls/multiselect_pills'; +import 'frappe/public/js/frappe/form/controls/multiselect_list'; +import 'frappe/public/js/frappe/form/controls/rating'; +import 'frappe/public/js/frappe/form/controls/duration'; +import 'frappe/public/js/frappe/form/controls/icon'; +frappe.ui.form.make_control = function (opts) { + debugger; + 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 { + // eslint-disable-next-line + console.log("Invalid Control Name: " + opts.df.fieldtype); + } +}; + + + diff --git a/smart_service/public/js/datepicker.min.js b/smart_service/public/js/datepicker.min.js new file mode 100644 index 0000000..c612ede --- /dev/null +++ b/smart_service/public/js/datepicker.min.js @@ -0,0 +1,2 @@ +!function(t,e,s){!function(){var i,a,n,h="2.2.3",o="datepicker",r=".datepicker-here",c=!1,d='
',l={classes:"",inline:!1,language:"ru",startDate:new Date,firstDay:"",weekends:[6,0],dateFormat:"",altField:"",altFieldDateFormat:"@",toggleSelected:!0,keyboardNav:!0,position:"bottom left",offset:12,view:"days",minView:"days",showOtherMonths:!0,selectOtherMonths:!0,moveToOtherMonthsOnSelect:!0,showOtherYears:!0,selectOtherYears:!0,moveToOtherYearsOnSelect:!0,minDate:"",maxDate:"",disableNavWhenOutOfRange:!0,multipleDates:!1,multipleDatesSeparator:",",range:!1,todayButton:!1,clearButton:!1,showEvent:"focus",autoClose:!1,monthsField:"monthsShort",prevHtml:'',nextHtml:'',navTitles:{days:"MM, yyyy",months:"yyyy",years:"yyyy1 - yyyy2"},timepicker:!1,onlyTimepicker:!1,dateTimeSeparator:" ",timeFormat:"",minHours:0,maxHours:24,minMinutes:0,maxMinutes:59,minSeconds:0,maxSeconds:59,hoursStep:1,minutesStep:1,secondsStep:1,onSelect:"",onShow:"",onHide:"",onChangeMonth:"",onChangeYear:"",onChangeDecade:"",onChangeView:"",onRenderCell:""},u={ctrlRight:[17,39],ctrlUp:[17,38],ctrlLeft:[17,37],ctrlDown:[17,40],shiftRight:[16,39],shiftUp:[16,38],shiftLeft:[16,37],shiftDown:[16,40],altUp:[18,38],altRight:[18,39],altLeft:[18,37],altDown:[18,40],ctrlShiftUp:[16,17,38]},m=function(t,a){this.el=t,this.$el=e(t),this.opts=e.extend(!0,{},l,a,this.$el.data()),i==s&&(i=e("body")),this.opts.startDate||(this.opts.startDate=new Date),"INPUT"==this.el.nodeName&&(this.elIsInput=!0),this.opts.altField&&(this.$altField="string"==typeof this.opts.altField?e(this.opts.altField):this.opts.altField),this.inited=!1,this.visible=!1,this.silent=!1,this.currentDate=this.opts.startDate,this.currentView=this.opts.view,this._createShortCuts(),this.selectedDates=[],this.views={},this.keys=[],this.minRange="",this.maxRange="",this._prevOnSelectValue="",this.init()};n=m,n.prototype={VERSION:h,viewIndexes:["days","months","years"],init:function(){c||this.opts.inline||!this.elIsInput||this._buildDatepickersContainer(),this._buildBaseHtml(),this._defineLocale(this.opts.language),this._syncWithMinMaxDates(),this.elIsInput&&(this.opts.inline||(this._setPositionClasses(this.opts.position),this._bindEvents()),this.opts.keyboardNav&&!this.opts.onlyTimepicker&&this._bindKeyboardEvents(),this.$datepicker.on("mousedown",this._onMouseDownDatepicker.bind(this)),this.$datepicker.on("mouseup",this._onMouseUpDatepicker.bind(this))),this.opts.classes&&this.$datepicker.addClass(this.opts.classes),this.opts.timepicker&&(this.timepicker=new e.fn.datepicker.Timepicker(this,this.opts),this._bindTimepickerEvents()),this.opts.onlyTimepicker&&this.$datepicker.addClass("-only-timepicker-"),this.views[this.currentView]=new e.fn.datepicker.Body(this,this.currentView,this.opts),this.views[this.currentView].show(),this.nav=new e.fn.datepicker.Navigation(this,this.opts),this.view=this.currentView,this.$el.on("clickCell.adp",this._onClickCell.bind(this)),this.$datepicker.on("mouseenter",".datepicker--cell",this._onMouseEnterCell.bind(this)),this.$datepicker.on("mouseleave",".datepicker--cell",this._onMouseLeaveCell.bind(this)),this.inited=!0},_createShortCuts:function(){this.minDate=this.opts.minDate?this.opts.minDate:new Date((-86399999136e5)),this.maxDate=this.opts.maxDate?this.opts.maxDate:new Date(86399999136e5)},_bindEvents:function(){this.$el.on(this.opts.showEvent+".adp",this._onShowEvent.bind(this)),this.$el.on("mouseup.adp",this._onMouseUpEl.bind(this)),this.$el.on("blur.adp",this._onBlur.bind(this)),this.$el.on("keyup.adp",this._onKeyUpGeneral.bind(this)),e(t).on("resize.adp",this._onResize.bind(this)),e("body").on("mouseup.adp",this._onMouseUpBody.bind(this))},_bindKeyboardEvents:function(){this.$el.on("keydown.adp",this._onKeyDown.bind(this)),this.$el.on("keyup.adp",this._onKeyUp.bind(this)),this.$el.on("hotKey.adp",this._onHotKey.bind(this))},_bindTimepickerEvents:function(){this.$el.on("timeChange.adp",this._onTimeChange.bind(this))},isWeekend:function(t){return this.opts.weekends.indexOf(t)!==-1},_defineLocale:function(t){"string"==typeof t?(this.loc=e.fn.datepicker.language[t],this.loc||(console.warn("Can't find language \""+t+'" in Datepicker.language, will use "ru" instead'),this.loc=e.extend(!0,{},e.fn.datepicker.language.ru)),this.loc=e.extend(!0,{},e.fn.datepicker.language.ru,e.fn.datepicker.language[t])):this.loc=e.extend(!0,{},e.fn.datepicker.language.ru,t),this.opts.dateFormat&&(this.loc.dateFormat=this.opts.dateFormat),this.opts.timeFormat&&(this.loc.timeFormat=this.opts.timeFormat),""!==this.opts.firstDay&&(this.loc.firstDay=this.opts.firstDay),this.opts.timepicker&&(this.loc.dateFormat=[this.loc.dateFormat,this.loc.timeFormat].join(this.opts.dateTimeSeparator)),this.opts.onlyTimepicker&&(this.loc.dateFormat=this.loc.timeFormat);var s=this._getWordBoundaryRegExp;(this.loc.timeFormat.match(s("aa"))||this.loc.timeFormat.match(s("AA")))&&(this.ampm=!0)},_buildDatepickersContainer:function(){c=!0,i.append('
'),a=e("#datepickers-container")},_buildBaseHtml:function(){var t,s=e('
');t="INPUT"==this.el.nodeName?this.opts.inline?s.insertAfter(this.$el):a:s.appendTo(this.$el),this.$datepicker=e(d).appendTo(t),this.$content=e(".datepicker--content",this.$datepicker),this.$nav=e(".datepicker--nav",this.$datepicker)},_triggerOnChange:function(){if(!this.selectedDates.length){if(""===this._prevOnSelectValue)return;return this._prevOnSelectValue="",this.opts.onSelect("","",this)}var t,e=this.selectedDates,s=n.getParsedDate(e[0]),i=this,a=new Date(s.year,s.month,s.date,s.hours,s.minutes,s.seconds);t=e.map(function(t){return i.formatDate(i.loc.dateFormat,t)}).join(this.opts.multipleDatesSeparator),(this.opts.multipleDates||this.opts.range)&&(a=e.map(function(t){var e=n.getParsedDate(t);return new Date(e.year,e.month,e.date,e.hours,e.minutes,e.seconds)})),this._prevOnSelectValue=t,this.opts.onSelect(t,a,this)},next:function(){var t=this.parsedDate,e=this.opts;switch(this.view){case"days":this.date=new Date(t.year,t.month+1,1),e.onChangeMonth&&e.onChangeMonth(this.parsedDate.month,this.parsedDate.year);break;case"months":this.date=new Date(t.year+1,t.month,1),e.onChangeYear&&e.onChangeYear(this.parsedDate.year);break;case"years":this.date=new Date(t.year+10,0,1),e.onChangeDecade&&e.onChangeDecade(this.curDecade)}},prev:function(){var t=this.parsedDate,e=this.opts;switch(this.view){case"days":this.date=new Date(t.year,t.month-1,1),e.onChangeMonth&&e.onChangeMonth(this.parsedDate.month,this.parsedDate.year);break;case"months":this.date=new Date(t.year-1,t.month,1),e.onChangeYear&&e.onChangeYear(this.parsedDate.year);break;case"years":this.date=new Date(t.year-10,0,1),e.onChangeDecade&&e.onChangeDecade(this.curDecade)}},formatDate:function(t,e){e=e||this.date;var s,i=t,a=this._getWordBoundaryRegExp,h=this.loc,o=n.getLeadingZeroNum,r=n.getDecade(e),c=n.getParsedDate(e),d=c.fullHours,l=c.hours,u=t.match(a("aa"))||t.match(a("AA")),m="am",p=this._replacer;switch(this.opts.timepicker&&this.timepicker&&u&&(s=this.timepicker._getValidHoursFromDate(e,u),d=o(s.hours),l=s.hours,m=s.dayPeriod),!0){case/@/.test(i):i=i.replace(/@/,e.getTime());case/aa/.test(i):i=p(i,a("aa"),m);case/AA/.test(i):i=p(i,a("AA"),m.toUpperCase());case/dd/.test(i):i=p(i,a("dd"),c.fullDate);case/d/.test(i):i=p(i,a("d"),c.date);case/DD/.test(i):i=p(i,a("DD"),h.days[c.day]);case/D/.test(i):i=p(i,a("D"),h.daysShort[c.day]);case/mm/.test(i):i=p(i,a("mm"),c.fullMonth);case/m/.test(i):i=p(i,a("m"),c.month+1);case/MM/.test(i):i=p(i,a("MM"),this.loc.months[c.month]);case/M/.test(i):i=p(i,a("M"),h.monthsShort[c.month]);case/ss/.test(i):i=p(i,a("ss"),c.fullSeconds);case/s/.test(i):i=p(i,a("s"),c.seconds);case/ii/.test(i):i=p(i,a("ii"),c.fullMinutes);case/i/.test(i):i=p(i,a("i"),c.minutes);case/hh/.test(i):i=p(i,a("hh"),d);case/h/.test(i):i=p(i,a("h"),l);case/yyyy/.test(i):i=p(i,a("yyyy"),c.year);case/yyyy1/.test(i):i=p(i,a("yyyy1"),r[0]);case/yyyy2/.test(i):i=p(i,a("yyyy2"),r[1]);case/yy/.test(i):i=p(i,a("yy"),c.year.toString().slice(-2))}return i},_replacer:function(t,e,s){return t.replace(e,function(t,e,i,a){return e+s+a})},_getWordBoundaryRegExp:function(t){var e="\\s|\\.|-|/|\\\\|,|\\$|\\!|\\?|:|;";return new RegExp("(^|>|"+e+")("+t+")($|<|"+e+")","g")},selectDate:function(t){var e=this,s=e.opts,i=e.parsedDate,a=e.selectedDates,h=a.length,o="";if(Array.isArray(t))return void t.forEach(function(t){e.selectDate(t)});if(t instanceof Date){if(this.lastSelectedDate=t,this.timepicker&&this.timepicker._setTime(t),e._trigger("selectDate",t),this.timepicker&&(t.setHours(this.timepicker.hours),t.setMinutes(this.timepicker.minutes),t.setSeconds(this.timepicker.seconds)),"days"==e.view&&t.getMonth()!=i.month&&s.moveToOtherMonthsOnSelect&&(o=new Date(t.getFullYear(),t.getMonth(),1)),"years"==e.view&&t.getFullYear()!=i.year&&s.moveToOtherYearsOnSelect&&(o=new Date(t.getFullYear(),0,1)),o&&(e.silent=!0,e.date=o,e.silent=!1,e.nav._render()),s.multipleDates&&!s.range){if(h===s.multipleDates)return;e._isSelected(t)||e.selectedDates.push(t)}else s.range?2==h?(e.selectedDates=[t],e.minRange=t,e.maxRange=""):1==h?(e.selectedDates.push(t),e.maxRange?e.minRange=t:e.maxRange=t,n.bigger(e.maxRange,e.minRange)&&(e.maxRange=e.minRange,e.minRange=t),e.selectedDates=[e.minRange,e.maxRange]):(e.selectedDates=[t],e.minRange=t):e.selectedDates=[t];e._setInputValue(),s.onSelect&&e._triggerOnChange(),s.autoClose&&!this.timepickerIsActive&&(s.multipleDates||s.range?s.range&&2==e.selectedDates.length&&e.hide():e.hide()),e.views[this.currentView]._render()}},removeDate:function(t){var e=this.selectedDates,s=this;if(t instanceof Date)return e.some(function(i,a){if(n.isSame(i,t))return e.splice(a,1),s.selectedDates.length?s.lastSelectedDate=s.selectedDates[s.selectedDates.length-1]:(s.minRange="",s.maxRange="",s.lastSelectedDate=""),s.views[s.currentView]._render(),s._setInputValue(),s.opts.onSelect&&s._triggerOnChange(),!0})},today:function(){this.silent=!0,this.view=this.opts.minView,this.silent=!1,this.date=new Date,this.opts.todayButton instanceof Date&&this.selectDate(this.opts.todayButton)},clear:function(){this.selectedDates=[],this.minRange="",this.maxRange="",this.views[this.currentView]._render(),this._setInputValue(),this.opts.onSelect&&this._triggerOnChange()},update:function(t,s){var i=arguments.length,a=this.lastSelectedDate;return 2==i?this.opts[t]=s:1==i&&"object"==typeof t&&(this.opts=e.extend(!0,this.opts,t)),this._createShortCuts(),this._syncWithMinMaxDates(),this._defineLocale(this.opts.language),this.nav._addButtonsIfNeed(),this.opts.onlyTimepicker||this.nav._render(),this.views[this.currentView]._render(),this.elIsInput&&!this.opts.inline&&(this._setPositionClasses(this.opts.position),this.visible&&this.setPosition(this.opts.position)),this.opts.classes&&this.$datepicker.addClass(this.opts.classes),this.opts.onlyTimepicker&&this.$datepicker.addClass("-only-timepicker-"),this.opts.timepicker&&(a&&this.timepicker._handleDate(a),this.timepicker._updateRanges(),this.timepicker._updateCurrentTime(),a&&(a.setHours(this.timepicker.hours),a.setMinutes(this.timepicker.minutes),a.setSeconds(this.timepicker.seconds))),this._setInputValue(),this},_syncWithMinMaxDates:function(){var t=this.date.getTime();this.silent=!0,this.minTime>t&&(this.date=this.minDate),this.maxTime=this.minTime&&s<=this.maxTime,month:o>=this.minTime&&r<=this.maxTime,year:i.year>=a.year&&i.year<=h.year};return e?c[e]:c.day},_getDimensions:function(t){var e=t.offset();return{width:t.outerWidth(),height:t.outerHeight(),left:e.left,top:e.top}},_getDateFromCell:function(t){var e=this.parsedDate,i=t.data("year")||e.year,a=t.data("month")==s?e.month:t.data("month"),n=t.data("date")||1;return new Date(i,a,n)},_setPositionClasses:function(t){t=t.split(" ");var e=t[0],s=t[1],i="datepicker -"+e+"-"+s+"- -from-"+e+"-";this.visible&&(i+=" active"),this.$datepicker.removeAttr("class").addClass(i)},setPosition:function(t){t=t||this.opts.position;var e,s,i=this._getDimensions(this.$el),a=this._getDimensions(this.$datepicker),n=t.split(" "),h=this.opts.offset,o=n[0],r=n[1];switch(o){case"top":e=i.top-a.height-h;break;case"right":s=i.left+i.width+h;break;case"bottom":e=i.top+i.height+h;break;case"left":s=i.left-a.width-h}switch(r){case"top":e=i.top;break;case"right":s=i.left+i.width-a.width;break;case"bottom":e=i.top+i.height-a.height;break;case"left":s=i.left;break;case"center":/left|right/.test(o)?e=i.top+i.height/2-a.height/2:s=i.left+i.width/2-a.width/2}this.$datepicker.css({left:s,top:e})},show:function(){var t=this.opts.onShow;this.setPosition(this.opts.position),this.$datepicker.addClass("active"),this.visible=!0,t&&this._bindVisionEvents(t)},hide:function(){var t=this.opts.onHide;this.$datepicker.removeClass("active").css({left:"-100000px"}),this.focused="",this.keys=[],this.inFocus=!1,this.visible=!1,this.$el.blur(),t&&this._bindVisionEvents(t)},down:function(t){this._changeView(t,"down")},up:function(t){this._changeView(t,"up")},_bindVisionEvents:function(t){this.$datepicker.off("transitionend.dp"),t(this,!1),this.$datepicker.one("transitionend.dp",t.bind(this,this,!0))},_changeView:function(t,e){t=t||this.focused||this.date;var s="up"==e?this.viewIndex+1:this.viewIndex-1;s>2&&(s=2),s<0&&(s=0),this.silent=!0,this.date=new Date(t.getFullYear(),t.getMonth(),1),this.silent=!1,this.view=this.viewIndexes[s]},_handleHotKey:function(t){var e,s,i,a=n.getParsedDate(this._getFocusedDate()),h=this.opts,o=!1,r=!1,c=!1,d=a.year,l=a.month,u=a.date;switch(t){case"ctrlRight":case"ctrlUp":l+=1,o=!0;break;case"ctrlLeft":case"ctrlDown":l-=1,o=!0;break;case"shiftRight":case"shiftUp":r=!0,d+=1;break;case"shiftLeft":case"shiftDown":r=!0,d-=1;break;case"altRight":case"altUp":c=!0,d+=10;break;case"altLeft":case"altDown":c=!0,d-=10;break;case"ctrlShiftUp":this.up()}i=n.getDaysCount(new Date(d,l)),s=new Date(d,l,u),ithis.maxTime&&(s=this.maxDate),this.focused=s,e=n.getParsedDate(s),o&&h.onChangeMonth&&h.onChangeMonth(e.month,e.year),r&&h.onChangeYear&&h.onChangeYear(e.year),c&&h.onChangeDecade&&h.onChangeDecade(this.curDecade)},_registerKey:function(t){var e=this.keys.some(function(e){return e==t});e||this.keys.push(t)},_unRegisterKey:function(t){var e=this.keys.indexOf(t);this.keys.splice(e,1)},_isHotKeyPressed:function(){var t,e=!1,s=this,i=this.keys.sort();for(var a in u)t=u[a],i.length==t.length&&t.every(function(t,e){return t==i[e]})&&(s._trigger("hotKey",a),e=!0);return e},_trigger:function(t,e){this.$el.trigger(t,e)},_focusNextCell:function(t,e){e=e||this.cellType;var s=n.getParsedDate(this._getFocusedDate()),i=s.year,a=s.month,h=s.date;if(!this._isHotKeyPressed()){switch(t){case 37:"day"==e?h-=1:"","month"==e?a-=1:"","year"==e?i-=1:"";break;case 38:"day"==e?h-=7:"","month"==e?a-=3:"","year"==e?i-=4:"";break;case 39:"day"==e?h+=1:"","month"==e?a+=1:"","year"==e?i+=1:"";break;case 40:"day"==e?h+=7:"","month"==e?a+=3:"","year"==e?i+=4:""}var o=new Date(i,a,h);o.getTime()this.maxTime&&(o=this.maxDate),this.focused=o}},_getFocusedDate:function(){var t=this.focused||this.selectedDates[this.selectedDates.length-1],e=this.parsedDate;if(!t)switch(this.view){case"days":t=new Date(e.year,e.month,(new Date).getDate());break;case"months":t=new Date(e.year,e.month,1);break;case"years":t=new Date(e.year,0,1)}return t},_getCell:function(t,s){s=s||this.cellType;var i,a=n.getParsedDate(t),h='.datepicker--cell[data-year="'+a.year+'"]';switch(s){case"month":h='[data-month="'+a.month+'"]';break;case"day":h+='[data-month="'+a.month+'"][data-date="'+a.date+'"]'}return i=this.views[this.currentView].$el.find(h),i.length?i:e("")},destroy:function(){var t=this;t.$el.off(".adp").data("datepicker",""),t.selectedDates=[],t.focused="",t.views={},t.keys=[],t.minRange="",t.maxRange="",t.opts.inline||!t.elIsInput?t.$datepicker.closest(".datepicker-inline").remove():t.$datepicker.remove()},_handleAlreadySelectedDates:function(t,e){this.opts.range?this.opts.toggleSelected?this.removeDate(e):2!=this.selectedDates.length&&this._trigger("clickCell",e):this.opts.toggleSelected&&this.removeDate(e),this.opts.toggleSelected||(this.lastSelectedDate=t,this.opts.timepicker&&(this.timepicker._setTime(t),this.timepicker.update()))},_onShowEvent:function(t){this.visible||this.show()},_onBlur:function(){!this.inFocus&&this.visible&&this.hide()},_onMouseDownDatepicker:function(t){this.inFocus=!0},_onMouseUpDatepicker:function(t){this.inFocus=!1,t.originalEvent.inFocus=!0,t.originalEvent.timepickerFocus||this.$el.focus()},_onKeyUpGeneral:function(t){var e=this.$el.val();e||this.clear()},_onResize:function(){this.visible&&this.setPosition()},_onMouseUpBody:function(t){t.originalEvent.inFocus||this.visible&&!this.inFocus&&this.hide()},_onMouseUpEl:function(t){t.originalEvent.inFocus=!0,setTimeout(this._onKeyUpGeneral.bind(this),4)},_onKeyDown:function(t){var e=t.which;if(this._registerKey(e),e>=37&&e<=40&&(t.preventDefault(),this._focusNextCell(e)),13==e&&this.focused){if(this._getCell(this.focused).hasClass("-disabled-"))return;if(this.view!=this.opts.minView)this.down();else{var s=this._isSelected(this.focused,this.cellType);if(!s)return this.timepicker&&(this.focused.setHours(this.timepicker.hours),this.focused.setMinutes(this.timepicker.minutes),this.focused.setSeconds(this.timepicker.seconds)),void this.selectDate(this.focused);this._handleAlreadySelectedDates(s,this.focused)}}27==e&&this.hide()},_onKeyUp:function(t){var e=t.which;this._unRegisterKey(e)},_onHotKey:function(t,e){this._handleHotKey(e)},_onMouseEnterCell:function(t){var s=e(t.target).closest(".datepicker--cell"),i=this._getDateFromCell(s);this.silent=!0,this.focused&&(this.focused=""),s.addClass("-focus-"),this.focused=i,this.silent=!1,this.opts.range&&1==this.selectedDates.length&&(this.minRange=this.selectedDates[0],this.maxRange="",n.less(this.minRange,this.focused)&&(this.maxRange=this.minRange,this.minRange=""),this.views[this.currentView]._update())},_onMouseLeaveCell:function(t){var s=e(t.target).closest(".datepicker--cell");s.removeClass("-focus-"),this.silent=!0,this.focused="",this.silent=!1},_onTimeChange:function(t,e,s,i){var a=new Date,n=this.selectedDates,h=!1;n.length&&(h=!0,a=this.lastSelectedDate),a.setHours(e),a.setMinutes(s),a.setSeconds(i),h||this._getCell(a).hasClass("-disabled-")?(this._setInputValue(),this.opts.onSelect&&this._triggerOnChange()):this.selectDate(a)},_onClickCell:function(t,e){this.timepicker&&(e.setHours(this.timepicker.hours),e.setMinutes(this.timepicker.minutes),e.setSeconds(this.timepicker.seconds)),this.selectDate(e)},set focused(t){if(!t&&this.focused){var e=this._getCell(this.focused);e.length&&e.removeClass("-focus-")}this._focused=t,this.opts.range&&1==this.selectedDates.length&&(this.minRange=this.selectedDates[0],this.maxRange="",n.less(this.minRange,this._focused)&&(this.maxRange=this.minRange,this.minRange="")),this.silent||(this.date=t)},get focused(){return this._focused},get parsedDate(){return n.getParsedDate(this.date)},set date(t){if(t instanceof Date)return this.currentDate=t,this.inited&&!this.silent&&(this.views[this.view]._render(),this.nav._render(),this.visible&&this.elIsInput&&this.setPosition()),t},get date(){return this.currentDate},set view(t){if(this.viewIndex=this.viewIndexes.indexOf(t),!(this.viewIndex<0))return this.prevView=this.currentView,this.currentView=t,this.inited&&(this.views[t]?this.views[t]._render():this.views[t]=new e.fn.datepicker.Body(this,t,this.opts),this.views[this.prevView].hide(),this.views[t].show(),this.nav._render(),this.opts.onChangeView&&this.opts.onChangeView(t),this.elIsInput&&this.visible&&this.setPosition()),t},get view(){return this.currentView},get cellType(){return this.view.substring(0,this.view.length-1)},get minTime(){var t=n.getParsedDate(this.minDate);return new Date(t.year,t.month,t.date).getTime()},get maxTime(){var t=n.getParsedDate(this.maxDate);return new Date(t.year,t.month,t.date).getTime()},get curDecade(){return n.getDecade(this.date)}},n.getDaysCount=function(t){return new Date(t.getFullYear(),t.getMonth()+1,0).getDate()},n.getParsedDate=function(t){return{year:t.getFullYear(),month:t.getMonth(),fullMonth:t.getMonth()+1<10?"0"+(t.getMonth()+1):t.getMonth()+1,date:t.getDate(),fullDate:t.getDate()<10?"0"+t.getDate():t.getDate(),day:t.getDay(),hours:t.getHours(),fullHours:t.getHours()<10?"0"+t.getHours():t.getHours(),minutes:t.getMinutes(),fullMinutes:t.getMinutes()<10?"0"+t.getMinutes():t.getMinutes(),seconds:t.getSeconds(),fullSeconds:t.getSeconds()<10?"0"+t.getSeconds():t.getSeconds()}},n.getDecade=function(t){var e=10*Math.floor(t.getFullYear()/10);return[e,e+9]},n.template=function(t,e){return t.replace(/#\{([\w]+)\}/g,function(t,s){if(e[s]||0===e[s])return e[s]})},n.isSame=function(t,e,s){if(!t||!e)return!1;var i=n.getParsedDate(t),a=n.getParsedDate(e),h=s?s:"day",o={day:i.date==a.date&&i.month==a.month&&i.year==a.year,month:i.month==a.month&&i.year==a.year,year:i.year==a.year};return o[h]},n.less=function(t,e,s){return!(!t||!e)&&e.getTime()t.getTime()},n.getLeadingZeroNum=function(t){return parseInt(t)<10?"0"+t:t},n.resetTime=function(t){if("object"==typeof t)return t=n.getParsedDate(t),new Date(t.year,t.month,t.date)},e.fn.datepicker=function(t){return this.each(function(){if(e.data(this,o)){var s=e.data(this,o);s.opts=e.extend(!0,s.opts,t),s.update()}else e.data(this,o,new m(this,t))})},e.fn.datepicker.Constructor=m,e.fn.datepicker.language={ru:{days:["Воскресенье","Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"],daysShort:["Вос","Пон","Вто","Сре","Чет","Пят","Суб"],daysMin:["Вс","Пн","Вт","Ср","Чт","Пт","Сб"],months:["Январь","Февраль","Март","Апрель","Май","Июнь","Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь"],monthsShort:["Янв","Фев","Мар","Апр","Май","Июн","Июл","Авг","Сен","Окт","Ноя","Дек"],today:"Сегодня",clear:"Очистить",dateFormat:"dd.mm.yyyy",timeFormat:"hh:ii",firstDay:1}},e(function(){e(r).datepicker()})}(),function(){var t={days:'
',months:'
',years:'
'},i=e.fn.datepicker,a=i.Constructor;i.Body=function(t,s,i){this.d=t,this.type=s,this.opts=i,this.$el=e(""),this.opts.onlyTimepicker||this.init()},i.Body.prototype={init:function(){this._buildBaseHtml(),this._render(),this._bindEvents()},_bindEvents:function(){this.$el.on("click",".datepicker--cell",e.proxy(this._onClickCell,this))},_buildBaseHtml:function(){this.$el=e(t[this.type]).appendTo(this.d.$content),this.$names=e(".datepicker--days-names",this.$el),this.$cells=e(".datepicker--cells",this.$el)},_getDayNamesHtml:function(t,e,i,a){return e=e!=s?e:t,i=i?i:"",a=a!=s?a:0,a>7?i:7==e?this._getDayNamesHtml(t,0,i,++a):(i+='
'+this.d.loc.daysMin[e]+"
",this._getDayNamesHtml(t,++e,i,++a))},_getCellContents:function(t,e){var s="datepicker--cell datepicker--cell-"+e,i=new Date,n=this.d,h=a.resetTime(n.minRange),o=a.resetTime(n.maxRange),r=n.opts,c=a.getParsedDate(t),d={},l=c.date;switch(e){case"day":n.isWeekend(c.day)&&(s+=" -weekend-"),c.month!=this.d.parsedDate.month&&(s+=" -other-month-",r.selectOtherMonths||(s+=" -disabled-"),r.showOtherMonths||(l=""));break;case"month":l=n.loc[n.opts.monthsField][c.month];break;case"year":var u=n.curDecade;l=c.year,(c.yearu[1])&&(s+=" -other-decade-",r.selectOtherYears||(s+=" -disabled-"),r.showOtherYears||(l=""))}return r.onRenderCell&&(d=r.onRenderCell(t,e)||{},l=d.html?d.html:l,s+=d.classes?" "+d.classes:""),r.range&&(a.isSame(h,t,e)&&(s+=" -range-from-"),a.isSame(o,t,e)&&(s+=" -range-to-"),1==n.selectedDates.length&&n.focused?((a.bigger(h,t)&&a.less(n.focused,t)||a.less(o,t)&&a.bigger(n.focused,t))&&(s+=" -in-range-"),a.less(o,t)&&a.isSame(n.focused,t)&&(s+=" -range-from-"),a.bigger(h,t)&&a.isSame(n.focused,t)&&(s+=" -range-to-")):2==n.selectedDates.length&&a.bigger(h,t)&&a.less(o,t)&&(s+=" -in-range-")),a.isSame(i,t,e)&&(s+=" -current-"),n.focused&&a.isSame(t,n.focused,e)&&(s+=" -focus-"),n._isSelected(t,e)&&(s+=" -selected-"),n._isInRange(t,e)&&!d.disabled||(s+=" -disabled-"),{html:l,classes:s}},_getDaysHtml:function(t){var e=a.getDaysCount(t),s=new Date(t.getFullYear(),t.getMonth(),1).getDay(),i=new Date(t.getFullYear(),t.getMonth(),e).getDay(),n=s-this.d.loc.firstDay,h=6-i+this.d.loc.firstDay;n=n<0?n+7:n,h=h>6?h-7:h;for(var o,r,c=-n+1,d="",l=c,u=e+h;l<=u;l++)r=t.getFullYear(),o=t.getMonth(),d+=this._getDayHtml(new Date(r,o,l));return d},_getDayHtml:function(t){var e=this._getCellContents(t,"day");return'
'+e.html+"
"},_getMonthsHtml:function(t){for(var e="",s=a.getParsedDate(t),i=0;i<12;)e+=this._getMonthHtml(new Date(s.year,i)),i++;return e},_getMonthHtml:function(t){var e=this._getCellContents(t,"month");return'
'+e.html+"
"},_getYearsHtml:function(t){var e=(a.getParsedDate(t),a.getDecade(t)),s=e[0]-1,i="",n=s;for(n;n<=e[1]+1;n++)i+=this._getYearHtml(new Date(n,0));return i},_getYearHtml:function(t){var e=this._getCellContents(t,"year");return'
'+e.html+"
"},_renderTypes:{days:function(){var t=this._getDayNamesHtml(this.d.loc.firstDay),e=this._getDaysHtml(this.d.currentDate);this.$cells.html(e),this.$names.html(t)},months:function(){var t=this._getMonthsHtml(this.d.currentDate);this.$cells.html(t)},years:function(){var t=this._getYearsHtml(this.d.currentDate);this.$cells.html(t)}},_render:function(){this.opts.onlyTimepicker||this._renderTypes[this.type].bind(this)()},_update:function(){var t,s,i,a=e(".datepicker--cell",this.$cells),n=this;a.each(function(a,h){s=e(this),i=n.d._getDateFromCell(e(this)),t=n._getCellContents(i,n.d.cellType),s.attr("class",t.classes)})},show:function(){this.opts.onlyTimepicker||(this.$el.addClass("active"),this.acitve=!0)},hide:function(){this.$el.removeClass("active"),this.active=!1},_handleClick:function(t){var e=t.data("date")||1,s=t.data("month")||0,i=t.data("year")||this.d.parsedDate.year,a=this.d;if(a.view!=this.opts.minView)return void a.down(new Date(i,s,e));var n=new Date(i,s,e),h=this.d._isSelected(n,this.d.cellType);return h?void a._handleAlreadySelectedDates.bind(a,h,n)():void a._trigger("clickCell",n)},_onClickCell:function(t){var s=e(t.target).closest(".datepicker--cell");s.hasClass("-disabled-")||this._handleClick.bind(this)(s)}}}(),function(){var t='
#{prevHtml}
#{title}
#{nextHtml}
',s='
',i='#{label}',a=e.fn.datepicker,n=a.Constructor;a.Navigation=function(t,e){this.d=t,this.opts=e,this.$buttonsContainer="",this.init()},a.Navigation.prototype={init:function(){this._buildBaseHtml(),this._bindEvents()},_bindEvents:function(){this.d.$nav.on("click",".datepicker--nav-action",e.proxy(this._onClickNavButton,this)),this.d.$nav.on("click",".datepicker--nav-title",e.proxy(this._onClickNavTitle,this)),this.d.$datepicker.on("click",".datepicker--button",e.proxy(this._onClickNavButton,this))},_buildBaseHtml:function(){this.opts.onlyTimepicker||this._render(),this._addButtonsIfNeed()},_addButtonsIfNeed:function(){this.opts.todayButton&&this._addButton("today"),this.opts.clearButton&&this._addButton("clear")},_render:function(){var s=this._getTitle(this.d.currentDate),i=n.template(t,e.extend({title:s},this.opts));this.d.$nav.html(i),"years"==this.d.view&&e(".datepicker--nav-title",this.d.$nav).addClass("-disabled-"),this.setNavStatus()},_getTitle:function(t){return this.d.formatDate(this.opts.navTitles[this.d.view],t)},_addButton:function(t){this.$buttonsContainer.length||this._addButtonsContainer();var s={action:t,label:this.d.loc[t]},a=n.template(i,s);e("[data-action="+t+"]",this.$buttonsContainer).length||this.$buttonsContainer.append(a)},_addButtonsContainer:function(){this.d.$datepicker.append(s),this.$buttonsContainer=e(".datepicker--buttons",this.d.$datepicker)},setNavStatus:function(){if((this.opts.minDate||this.opts.maxDate)&&this.opts.disableNavWhenOutOfRange){var t=this.d.parsedDate,e=t.month,s=t.year,i=t.date;switch(this.d.view){case"days":this.d._isInRange(new Date(s,e-1,1),"month")||this._disableNav("prev"),this.d._isInRange(new Date(s,e+1,1),"month")||this._disableNav("next");break;case"months":this.d._isInRange(new Date(s-1,e,i),"year")||this._disableNav("prev"),this.d._isInRange(new Date(s+1,e,i),"year")||this._disableNav("next");break;case"years":var a=n.getDecade(this.d.date);this.d._isInRange(new Date(a[0]-1,0,1),"year")||this._disableNav("prev"),this.d._isInRange(new Date(a[1]+1,0,1),"year")||this._disableNav("next")}}},_disableNav:function(t){e('[data-action="'+t+'"]',this.d.$nav).addClass("-disabled-")},_activateNav:function(t){e('[data-action="'+t+'"]',this.d.$nav).removeClass("-disabled-")},_onClickNavButton:function(t){var s=e(t.target).closest("[data-action]"),i=s.data("action");this.d[i]()},_onClickNavTitle:function(t){if(!e(t.target).hasClass("-disabled-"))return"days"==this.d.view?this.d.view="months":void(this.d.view="years")}}}(),function(){var t='
#{hourVisible} : #{minValue} : #{secValue}
',s=e.fn.datepicker,i=s.Constructor;s.Timepicker=function(t,e){this.d=t,this.opts=e,this.init()},s.Timepicker.prototype={init:function(){var t="input";this._setTime(this.d.date),this._buildHTML(),navigator.userAgent.match(/trident/gi)&&(t="change"),this.d.$el.on("selectDate",this._onSelectDate.bind(this)), +this.$ranges.on(t,this._onChangeRange.bind(this)),this.$ranges.on("mouseup",this._onMouseUpRange.bind(this)),this.$ranges.on("mousemove focus ",this._onMouseEnterRange.bind(this)),this.$ranges.on("mouseout blur",this._onMouseOutRange.bind(this))},_setTime:function(t){var e=i.getParsedDate(t);this._handleDate(t),this.hours=e.hourst.getHours()&&(this.minMinutes=this.opts.minMinutes)},_setMaxTimeFromDate:function(t){this.maxHours=t.getHours(),this.maxMinutes=t.getMinutes(),this.maxSeconds=t.getSeconds(),this.d.lastSelectedDate&&this.d.lastSelectedDate.getHours()t?0:i.minHours,this.minMinutes=i.minMinutes<0||i.minMinutes>e?0:i.minMinutes,this.maxHours=i.maxHours<0||i.maxHours>t?t:i.maxHours,this.maxMinutes=i.maxMinutes<0||i.maxMinutes>e?e:i.maxMinutes,this.minSeconds=i.minSeconds<0||i.minSeconds>s?0:i.minSeconds,this.maxSeconds=i.maxSeconds<0||i.maxSeconds>s?s:i.maxSeconds},_validateHoursMinutes:function(t){this.hoursthis.maxHours&&(this.hours=this.maxHours),this.minutesthis.maxMinutes&&(this.minutes=this.maxMinutes),this.secondsthis.maxSeconds&&(this.seconds=this.maxSeconds)},_buildHTML:function(){var s=i.getLeadingZeroNum,a={hourMin:this.minHours,hourMax:s(this.maxHours),hourStep:this.opts.hoursStep,hourValue:this.hours,hourVisible:s(this.displayHours),minMin:this.minMinutes,minMax:s(this.maxMinutes),minStep:this.opts.minutesStep,minValue:s(this.minutes),secMin:this.minSeconds,secMax:s(this.maxSeconds),secStep:this.opts.secondsStep,secValue:s(this.seconds)},n=i.template(t,a);this.$timepicker=e(n).appendTo(this.d.$datepicker),this.$ranges=e('[type="range"]',this.$timepicker),this.$hours=e('[name="hours"]',this.$timepicker),this.$minutes=e('[name="minutes"]',this.$timepicker),this.$seconds=e('[name="seconds"]',this.$timepicker),this.$hoursText=e(".datepicker--time-current-hours",this.$timepicker),this.$minutesText=e(".datepicker--time-current-minutes",this.$timepicker),this.$secondsText=e(".datepicker--time-current-seconds",this.$timepicker),this.d.ampm&&(this.$ampm=e('').appendTo(e(".datepicker--time-current",this.$timepicker)).html(this.dayPeriod),this.$timepicker.addClass("-am-pm-"))},_updateCurrentTime:function(){var t=i.getLeadingZeroNum(this.displayHours),e=i.getLeadingZeroNum(this.minutes),s=i.getLeadingZeroNum(this.seconds);this.$hoursText.html(t),this.$minutesText.html(e),this.$secondsText.html(s),this.d.ampm&&this.$ampm.html(this.dayPeriod)},_updateRanges:function(){this.$hours.attr({min:this.minHours,max:this.maxHours}).val(this.hours),this.$minutes.attr({min:this.minMinutes,max:this.maxMinutes}).val(this.minutes),this.$seconds.attr({min:this.minSeconds,max:this.maxSeconds}).val(this.seconds)},_handleDate:function(t){this._setDefaultMinMaxTime(),t&&(i.isSame(t,this.d.opts.minDate)?this._setMinTimeFromDate(this.d.opts.minDate):i.isSame(t,this.d.opts.maxDate)&&this._setMaxTimeFromDate(this.d.opts.maxDate)),this._validateHoursMinutes(t)},update:function(){this._updateRanges(),this._updateCurrentTime()},_getValidHoursFromDate:function(t,e){var s=t,a=t;t instanceof Date&&(s=i.getParsedDate(t),a=s.hours);var n=e||this.d.ampm,h="am";if(n)switch(!0){case 0==a:a=12;break;case 12==a:h="pm";break;case a>11:a-=12,h="pm"}return{hours:a,dayPeriod:h}},set hours(t){this._hours=t;var e=this._getValidHoursFromDate(t);this.displayHours=e.hours,this.dayPeriod=e.dayPeriod},get hours(){return this._hours},_onChangeRange:function(t){var s=e(t.target),i=s.attr("name");this.d.timepickerIsActive=!0,this[i]=s.val(),this._updateCurrentTime(),this.d._trigger("timeChange",[this.hours,this.minutes,this.seconds]),this._handleDate(this.d.lastSelectedDate),this.update()},_onSelectDate:function(t,e){this._handleDate(e),this.update()},_onMouseEnterRange:function(t){var s=e(t.target).attr("name");e(".datepicker--time-current-"+s,this.$timepicker).addClass("-focus-")},_onMouseOutRange:function(t){var s=e(t.target).attr("name");this.d.inFocus||e(".datepicker--time-current-"+s,this.$timepicker).removeClass("-focus-")},_onMouseUpRange:function(t){this.d.timepickerIsActive=!1}}}()}(window,jQuery); \ No newline at end of file diff --git a/smart_service/public/js/datepicker_i18n.js b/smart_service/public/js/datepicker_i18n.js new file mode 100644 index 0000000..f010325 --- /dev/null +++ b/smart_service/public/js/datepicker_i18n.js @@ -0,0 +1,62 @@ +import "air-datepicker/dist/js/i18n/datepicker.cs.js"; +import "air-datepicker/dist/js/i18n/datepicker.da.js"; +import "air-datepicker/dist/js/i18n/datepicker.de.js"; +import "air-datepicker/dist/js/i18n/datepicker.en.js"; +import "air-datepicker/dist/js/i18n/datepicker.es.js"; +import "air-datepicker/dist/js/i18n/datepicker.fi.js"; +import "air-datepicker/dist/js/i18n/datepicker.fr.js"; +import "air-datepicker/dist/js/i18n/datepicker.hu.js"; +import "air-datepicker/dist/js/i18n/datepicker.nl.js"; +import "air-datepicker/dist/js/i18n/datepicker.pl.js"; +import "air-datepicker/dist/js/i18n/datepicker.pt-BR.js"; +import "air-datepicker/dist/js/i18n/datepicker.pt.js"; +import "air-datepicker/dist/js/i18n/datepicker.ro.js"; +import "air-datepicker/dist/js/i18n/datepicker.sk.js"; +import "air-datepicker/dist/js/i18n/datepicker.zh.js"; + +(function ($) { + $.fn.datepicker.language['ar'] = { + days: ['الأحد', 'الأثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعه', 'السبت'], + daysShort: ['الأحد', 'الأثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعه', 'السبت'], + daysMin: ['الأحد', 'الأثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعه', 'السبت'], + months: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'اكتوبر', 'نوفمبر', 'ديسمبر'], + monthsShort: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'اكتوبر', 'نوفمبر', 'ديسمبر'], + today: 'اليوم', + clear: 'Clear', + dateFormat: 'dd/mm/yyyy', + timeFormat: 'hh:ii aa', + firstDay: 0 + }; +})(jQuery); + +(function ($) { + $.fn.datepicker.language['gr'] = { + days: ['Κυριακή', 'Δευτέρα', 'Τρίτη', 'Τετάρτη', 'Πέμπτη', 'Παρασκευή', 'Σάββατο'], + daysShort: ['Κυρ', 'Δευ', 'Τρι', 'Τετ', 'Πεμ', 'Παρ', 'Σαβ'], + daysMin: ['Κυ', 'Δε', 'Τρ', 'Τε', 'Πε', 'Πα', 'Σα'], + months: ['Ιανουάριος', 'Φεβρουάριος', 'Μάρτιος', 'Απρίλιος', 'Μάιος', 'Ιούνιος', 'Ιούλιος', 'Αύγουστος', 'Σεπτέμβριος', 'Οκτώβριος', 'Νοέμβριος', 'Δεκέμβριος'], + monthsShort: ['Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μάι', 'Ι/ν', 'Ι/λ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ'], + today: 'Σήμερα', + clear: 'Καθαρισμός', + dateFormat: 'dd/mm/yyyy', + timeFormat: 'hh:ii aa', + firstDay: 0 + }; +})(jQuery); + + +(function ($) { + $.fn.datepicker.language['it'] = { + days: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'], + daysShort: ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'], + daysMin: ['Do', 'Lu', 'Ma', 'Me', 'Gi', 'Ve', 'Sa'], + months: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', + 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'], + monthsShort: ['Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago', 'Set', 'Ott', 'Nov', 'Dic'], + today: 'Oggi', + clear: 'Reset', + dateFormat: 'dd/mm/yyyy', + timeFormat: 'hh:ii', + firstDay: 1 + }; +})(jQuery); diff --git a/smart_service/public/js/form.js b/smart_service/public/js/form.js new file mode 100644 index 0000000..dce5d8d --- /dev/null +++ b/smart_service/public/js/form.js @@ -0,0 +1,1796 @@ +frappe.provide('frappe.ui.form'); +frappe.provide('frappe.model.docinfo'); + +import 'frappe/public/js/frappe/form/quick_entry'; +import 'frappe/public/js/frappe/form/toolbar'; +import 'frappe/public/js/frappe/form/dashboard'; +import 'frappe/public/js/frappe/form/workflow'; +import 'frappe/public/js/frappe/form/save'; +import 'frappe/public/js/frappe/form/print_utils'; +import 'frappe/public/js/frappe/form/success_action'; +import 'frappe/public/js/frappe/form/script_manager'; +import 'frappe/public/js/frappe/form/script_helpers'; +import 'frappe/public/js/frappe/form/sidebar/form_sidebar'; +import 'frappe/public/js/frappe/form/footer/footer'; + +frappe.ui.form.Controller = Class.extend({ + init: function(opts) { + $.extend(this, opts); + } +}); + +frappe.ui.form.Form = class FrappeForm { + constructor(doctype, parent, in_form, doctype_layout_name) { + this.docname = ''; + this.doctype = doctype; + this.doctype_layout_name = doctype_layout_name; + this.in_form = in_form ? true : false; + + this.hidden = false; + this.refresh_if_stale_for = 120; + this.opendocs = {}; + this.custom_buttons = {}; + this.sections = []; + this.grids = []; + this.cscript = new frappe.ui.form.Controller({ frm: this }); + this.events = {}; + this.fetch_dict = {}; + this.parent = parent; + this.doctype_layout = frappe.get_doc('DocType Layout', doctype_layout_name); + this.setup_meta(doctype); + } + + setup_meta() { + this.meta = frappe.get_doc('DocType', this.doctype); + + if(this.meta.istable) { + this.meta.in_dialog = 1; + } + + this.perm = frappe.perm.get_perm(this.doctype); // for create + this.action_perm_type_map = { + "Create": "create", + "Save": "write", + "Submit": "submit", + "Update": "submit", + "Cancel": "cancel", + "Amend": "amend", + "Delete": "delete" + }; + } + + setup() { + this.fields = []; + this.fields_dict = {}; + this.state_fieldname = frappe.workflow.get_state_fieldname(this.doctype); + + // wrapper + this.wrapper = this.parent; + this.$wrapper = $(this.wrapper); + frappe.ui.make_app_page({ + parent: this.wrapper, + single_column: this.meta.hide_toolbar + }); + this.page = this.wrapper.page; + this.layout_main = this.page.main.get(0); + + this.toolbar = new frappe.ui.form.Toolbar({ + frm: this, + page: this.page + }); + + // navigate records keyboard shortcuts + this.add_form_keyboard_shortcuts(); + + // 2 column layout + this.setup_std_layout(); + + // client script must be called after "setup" - there are no fields_dict attached to the frm otherwise + this.script_manager = new frappe.ui.form.ScriptManager({ + frm: this + }); + this.script_manager.setup(); + this.watch_model_updates(); + + if (!this.meta.hide_toolbar && frappe.boot.desk_settings.timeline) { + this.footer = new frappe.ui.form.Footer({ + frm: this, + parent: $('
').appendTo(this.page.main.parent()) + }); + $("body").attr("data-sidebar", 1); + } + this.setup_file_drop(); + this.setup_doctype_actions(); + this.setup_notify_on_rename(); + + this.setup_done = true; + } + + add_form_keyboard_shortcuts() { + // Navigate to next record + frappe.ui.keys.add_shortcut({ + shortcut: 'shift+ctrl+>', + action: () => this.navigate_records(0), + page: this.page, + description: __('Go to next record'), + ignore_inputs: true, + condition: () => !this.is_new() + }); + + // Navigate to previous record + frappe.ui.keys.add_shortcut({ + shortcut: 'shift+ctrl+<', + action: () => this.navigate_records(1), + page: this.page, + description: __('Go to previous record'), + ignore_inputs: true, + condition: () => !this.is_new() + }); + + let grid_shortcut_keys = [ + { + 'shortcut': 'Up Arrow', + 'description': __('Move cursor to above row') + }, + { + 'shortcut': 'Down Arrow', + 'description': __('Move cursor to below row') + }, + { + 'shortcut': 'tab', + 'description': __('Move cursor to next column') + }, + { + 'shortcut': 'shift+tab', + 'description': __('Move cursor to previous column') + }, + { + 'shortcut': 'Ctrl+up', + 'description': __('Add a row above the current row') + }, + { + 'shortcut': 'Ctrl+down', + 'description': __('Add a row below the current row') + }, + { + 'shortcut': 'Ctrl+shift+up', + 'description': __('Add a row at the top') + }, + { + 'shortcut': 'Ctrl+shift+down', + 'description': __('Add a row at the bottom') + }, + { + 'shortcut': 'shift+alt+down', + 'description': __('To duplcate current row') + } + ]; + + grid_shortcut_keys.forEach(row => { + frappe.ui.keys.add_shortcut({ + shortcut: row.shortcut, + page: this, + description: __(row.description), + ignore_inputs: true, + condition: () => !this.is_new() + }); + }); + + } + + setup_std_layout() { + this.form_wrapper = $('
').appendTo(this.layout_main); + this.body = $('
').appendTo(this.form_wrapper); + + // only tray + this.meta.section_style='Simple'; // always simple! + + // layout + this.layout = new frappe.ui.form.Layout({ + parent: this.body, + doctype: this.doctype, + doctype_layout: this.doctype_layout, + frm: this, + with_dashboard: true, + card_layout: true, + }); + this.layout.make(); + + this.fields_dict = this.layout.fields_dict; + this.fields = this.layout.fields_list; + + this.dashboard = new frappe.ui.form.Dashboard({ + frm: this, + parent: $('
').insertAfter(this.layout.wrapper.find('.form-message')) + }); + + // workflow state + this.states = new frappe.ui.form.States({ + frm: this + }); + } + + watch_model_updates() { + // watch model updates + var me = this; + + // on main doc + frappe.model.on(me.doctype, "*", function(fieldname, value, doc) { + // set input + if(doc.name===me.docname) { + me.dirty(); + + let field = me.fields_dict[fieldname]; + field && field.refresh(fieldname); + + // Validate value for link field explicitly + field && ["Link", "Dynamic Link"].includes(field.df.fieldtype) && field.validate && field.validate(value); + + me.layout.refresh_dependency(); + me.layout.refresh_sections(); + return me.script_manager.trigger(fieldname, doc.doctype, doc.name); + } + }); + + // on table fields + var table_fields = frappe.get_children("DocType", me.doctype, "fields", { + fieldtype: ["in", frappe.model.table_fields] + }); + + // using $.each to preserve df via closure + $.each(table_fields, function(i, df) { + frappe.model.on(df.options, "*", function(fieldname, value, doc) { + if(doc.parent===me.docname && doc.parentfield===df.fieldname) { + me.dirty(); + me.fields_dict[df.fieldname].grid.set_value(fieldname, value, doc); + return me.script_manager.trigger(fieldname, doc.doctype, doc.name); + } + }); + }); + } + + setup_notify_on_rename() { + $(document).on('rename', (ev, dt, old_name, new_name) => { + if (dt==this.doctype) + this.rename_notify(dt, old_name, new_name); + }); + } + + setup_file_drop() { + var me = this; + this.$wrapper.on('dragenter dragover', false) + .on('drop', function(e) { + var dataTransfer = e.originalEvent.dataTransfer; + if (!(dataTransfer && dataTransfer.files && dataTransfer.files.length > 0)) { + return; + } + + e.stopPropagation(); + e.preventDefault(); + + if(me.doc.__islocal) { + frappe.msgprint(__("Please save before attaching.")); + throw "attach error"; + } + + new frappe.ui.FileUploader({ + doctype: me.doctype, + docname: me.docname, + frm: me, + files: dataTransfer.files, + folder: 'Home/Attachments', + on_success(file_doc) { + me.attachments.attachment_uploaded(file_doc); + } + }); + }); + } + + // REFRESH + + refresh(docname) { + var switched = docname ? true : false; + + if(docname) { + this.switch_doc(docname); + } + + cur_frm = this; + + if(this.docname) { // document to show + this.save_disabled = false; + // set the doc + this.doc = frappe.get_doc(this.doctype, this.docname); + + // check permissions + if (!this.has_read_permission()) { + frappe.show_not_permitted(__(this.doctype) + " " + __(this.docname)); + return; + } + + // read only (workflow) + this.read_only = frappe.workflow.is_read_only(this.doctype, this.docname); + if (this.read_only) this.set_read_only(true); + + // check if doctype is already open + if (!this.opendocs[this.docname]) { + this.check_doctype_conflict(this.docname); + } else { + if (this.check_reload()) { + return; + } + } + + // do setup + if(!this.setup_done) { + this.setup(); + } + + // load the record for the first time, if not loaded (call 'onload') + this.trigger_onload(switched); + + // if print format is shown, refresh the format + // if(this.print_preview.wrapper.is(":visible")) { + // this.print_preview.preview(); + // } + + if(switched) { + if(this.show_print_first && this.doc.docstatus===1) { + // show print view + this.print_doc(); + } + } + + // set status classes + this.$wrapper.removeClass('validated-form') + .toggleClass('editable-form', this.doc.docstatus===0) + .toggleClass('submitted-form', this.doc.docstatus===1) + .toggleClass('cancelled-form', this.doc.docstatus===2); + + this.show_conflict_message(); + } + } + + // sets up the refresh event for custom buttons + // added via configuration + setup_doctype_actions() { + if (this.meta.actions) { + for (let action of this.meta.actions) { + frappe.ui.form.on(this.doctype, 'refresh', () => { + if (!this.is_new()) { + if (!action.hidden) { + this.add_custom_button(action.label, () => { + this.execute_action(action); + }, action.group); + } + } + }); + } + } + } + + execute_action(action) { + if (typeof action === 'string') { + // called by label - maybe via custom script + // frm.execute_action('Action') + for (let _action of this.meta.actions) { + if (_action.label === action) { + action = _action; + break; + } + } + + if (typeof action === 'string') { + frappe.throw(`Action ${action} not found`); + } + } + if (action.action_type==='Server Action') { + frappe.xcall(action.action, {'doc': this.doc}).then((doc) => { + if (doc.doctype) { + // document is returned by the method, + // apply the changes locally and refresh + frappe.model.sync(doc); + this.refresh(); + } + + // feedback + frappe.msgprint({ + message: __('{} Complete', [action.label]), + alert: true + }); + }); + } else if (action.action_type==='Route') { + frappe.set_route(action.action); + } + } + + switch_doc(docname) { + // reset visible columns, since column headings can change in different docs + this.grids.forEach(grid_obj => { + grid_obj.grid.visible_columns = null + // reset page number to 1 + grid_obj.grid.grid_pagination.go_to_page(1, true); + }); + frappe.ui.form.close_grid_form(); + this.viewers && this.viewers.parent.empty(); + this.docname = docname; + this.setup_docinfo_change_listener(); + } + + check_reload() { + if(this.doc && (!this.doc.__unsaved) && this.doc.__last_sync_on && + (new Date() - this.doc.__last_sync_on) > (this.refresh_if_stale_for * 1000)) { + this.reload_doc(); + return true; + } + } + + trigger_onload(switched) { + this.cscript.is_onload = false; + if(!this.opendocs[this.docname]) { + var me = this; + this.cscript.is_onload = true; + this.initialize_new_doc(); + $(document).trigger("form-load", [this]); + $(this.page.wrapper).on('hide', function() { + $(document).trigger("form-unload", [me]); + }); + } else { + this.render_form(switched); + if (this.doc.localname) { + // trigger form-rename and remove .localname + delete this.doc.localname; + $(document).trigger("form-rename", [this]); + } + } + } + + initialize_new_doc() { + // moved this call to refresh function + // this.check_doctype_conflict(docname); + var me = this; + + // hide any open grid + this.script_manager.trigger("before_load", this.doctype, this.docname) + .then(() => { + me.script_manager.trigger("onload"); + me.opendocs[me.docname] = true; + me.render_form(); + + frappe.after_ajax(function() { + me.trigger_link_fields(); + }); + + frappe.breadcrumbs.add(me.meta.module, me.doctype); + }); + + // update seen + if(this.meta.track_seen) { + $('.list-id[data-name="'+ me.docname +'"]').addClass('seen'); + } + } + + render_form(switched) { + if(!this.meta.istable) { + this.layout.doc = this.doc; + this.layout.attach_doc_and_docfields(); + + if (frappe.boot.desk_settings.form_sidebar) { + this.sidebar = new frappe.ui.form.Sidebar({ + frm: this, + page: this.page + }); + this.sidebar.make(); + } + + // clear layout message + this.layout.show_message(); + + frappe.run_serially([ + // header must be refreshed before client methods + // because add_custom_button + () => this.refresh_header(switched), + // trigger global trigger + // to use this + () => $(document).trigger('form-refresh', [this]), + // fields + () => this.refresh_fields(), + // call trigger + () => this.script_manager.trigger("refresh"), + // call onload post render for callbacks to be fired + () => { + if(this.cscript.is_onload) { + return this.script_manager.trigger("onload_post_render"); + } + }, + () => this.cscript.is_onload && this.is_new() && this.focus_on_first_input(), + () => this.run_after_load_hook(), + () => this.dashboard.after_refresh() + ]); + + } else { + this.refresh_header(switched); + } + + this.$wrapper.trigger('render_complete'); + + if(!this.hidden) { + this.layout.show_empty_form_message(); + } + + this.scroll_to_element(); + } + + focus_on_first_input() { + let first = this.form_wrapper.find('.form-layout :input:visible:first'); + if (!in_list(["Date", "Datetime"], first.attr("data-fieldtype"))) { + first.focus(); + } + } + + run_after_load_hook() { + if (frappe.route_hooks.after_load) { + let route_callback = frappe.route_hooks.after_load; + delete frappe.route_hooks.after_load; + + route_callback(this); + } + } + + refresh_fields() { + this.layout.refresh(this.doc); + this.layout.primary_button = this.$wrapper.find(".btn-primary"); + + // cleanup activities after refresh + this.cleanup_refresh(this); + } + + cleanup_refresh() { + if(this.fields_dict['amended_from']) { + if (this.doc.amended_from) { + unhide_field('amended_from'); + if (this.fields_dict['amendment_date']) unhide_field('amendment_date'); + } else { + hide_field('amended_from'); + if (this.fields_dict['amendment_date']) hide_field('amendment_date'); + } + } + + if(this.fields_dict['trash_reason']) { + if(this.doc.trash_reason && this.doc.docstatus == 2) { + unhide_field('trash_reason'); + } else { + hide_field('trash_reason'); + } + } + + if(this.meta.autoname && this.meta.autoname.substr(0,6)=='field:' && !this.doc.__islocal) { + var fn = this.meta.autoname.substr(6); + + if (this.doc[fn]) { + this.toggle_display(fn, false); + } + } + + if(this.meta.autoname=="naming_series:" && !this.doc.__islocal) { + this.toggle_display("naming_series", false); + } + } + + refresh_header(switched) { + // set title + // main title + if(!this.meta.in_dialog || this.in_form) { + frappe.utils.set_title(this.meta.issingle ? this.doctype : this.docname); + } + + // show / hide buttons + if(this.toolbar) { + if (switched) { + this.toolbar.current_status = undefined; + } + this.toolbar.refresh(); + } + + this.dashboard.refresh(); + frappe.breadcrumbs.update(); + + this.show_submit_message(); + this.clear_custom_buttons(); + this.show_web_link(); + } + + // SAVE + + save_or_update() { + if(this.save_disabled) return; + + if(this.doc.docstatus===0) { + this.save(); + } else if(this.doc.docstatus===1 && this.doc.__unsaved) { + this.save("Update"); + } + } + + save(save_action, callback, btn, on_error) { + let me = this; + return new Promise((resolve, reject) => { + btn && $(btn).prop("disabled", true); + frappe.ui.form.close_grid_form(); + me.validate_and_save(save_action, callback, btn, on_error, resolve, reject); + }).then(() => { + me.show_success_action(); + }).catch((e) => { + console.error(e); // eslint-disable-line + }); + } + + validate_and_save(save_action, callback, btn, on_error, resolve, reject) { + var me = this; + if(!save_action) save_action = "Save"; + this.validate_form_action(save_action, resolve); + + var after_save = function(r) { + if(!r.exc) { + if (["Save", "Update", "Amend"].indexOf(save_action)!==-1) { + frappe.utils.play_sound("click"); + } + + me.script_manager.trigger("after_save"); + + if (frappe.route_hooks.after_save) { + let route_callback = frappe.route_hooks.after_save; + delete frappe.route_hooks.after_save; + + route_callback(me); + } + // submit comment if entered + if (me.comment_box) { + me.comment_box.submit(); + } + me.refresh(); + } else { + if(on_error) { + on_error(); + reject(); + } + } + callback && callback(r); + resolve(); + }; + + var fail = (e) => { + if (e) { + console.error(e) + } + btn && $(btn).prop("disabled", false); + if(on_error) { + on_error(); + reject(); + } + }; + + if(save_action != "Update") { + // validate + frappe.validated = true; + frappe.run_serially([ + () => this.script_manager.trigger("validate"), + () => this.script_manager.trigger("before_save"), + () => { + if(!frappe.validated) { + fail(); + return; + } + + frappe.ui.form.save(me, save_action, after_save, btn); + } + ]).catch(fail); + } else { + frappe.ui.form.save(me, save_action, after_save, btn); + } + } + + savesubmit(btn, callback, on_error) { + var me = this; + return new Promise(resolve => { + this.validate_form_action("Submit"); + frappe.confirm(__("Permanently Submit {0}?", [this.docname]), function() { + frappe.validated = true; + me.script_manager.trigger("before_submit").then(function() { + if(!frappe.validated) { + return me.handle_save_fail(btn, on_error); + } + + me.save('Submit', function(r) { + if(r.exc) { + me.handle_save_fail(btn, on_error); + } else { + frappe.utils.play_sound("submit"); + callback && callback(); + me.script_manager.trigger("on_submit") + .then(() => resolve(me)) + .then(() => { + if (frappe.route_hooks.after_submit) { + let route_callback = frappe.route_hooks.after_submit; + delete frappe.route_hooks.after_submit; + route_callback(me); + } + }); + } + }, btn, () => me.handle_save_fail(btn, on_error), resolve); + }); + }, () => me.handle_save_fail(btn, on_error) ); + }); + } + + savecancel(btn, callback, on_error) { + const me = this; + this.validate_form_action('Cancel'); + me.ignore_doctypes_on_cancel_all = me.ignore_doctypes_on_cancel_all || []; + frappe.call({ + method: "frappe.desk.form.linked_with.get_submitted_linked_docs", + args: { + doctype: me.doc.doctype, + name: me.doc.name + }, + freeze: true + }).then(r => { + if (!r.exc) { + let doctypes_to_cancel = (r.message.docs || []).map(value => { + return value.doctype; + }).filter(value => { + return !me.ignore_doctypes_on_cancel_all.includes(value); + }); + + if (doctypes_to_cancel.length) { + return me._cancel_all(r, btn, callback, on_error); + } + } + return me._cancel(btn, callback, on_error, false); + } + ); + } + + _cancel_all(r, btn, callback, on_error) { + const me = this; + + // add confirmation message for cancelling all linked docs + let links_text = ""; + let links = r.message.docs; + const doctypes = Array.from(new Set(links.map(link => link.doctype))); + + me.ignore_doctypes_on_cancel_all = me.ignore_doctypes_on_cancel_all || []; + + for (let doctype of doctypes) { + if (!me.ignore_doctypes_on_cancel_all.includes(doctype)) { + let docnames = links + .filter((link) => link.doctype == doctype) + .map((link) => frappe.utils.get_form_link(link.doctype, link.name, true)) + .join(", "); + links_text += `
  • ${doctype}: ${docnames}
  • `; + } + } + links_text = `
      ${links_text}
    `; + + let confirm_message = __('{0} {1} is linked with the following submitted documents: {2}', + [(me.doc.doctype).bold(), me.doc.name, links_text]); + + let can_cancel = links.every((link) => frappe.model.can_cancel(link.doctype)); + if (can_cancel) { + confirm_message += __('Do you want to cancel all linked documents?'); + } else { + confirm_message += __('You do not have permissions to cancel all linked documents.'); + } + + // generate dialog box to cancel all linked docs + let d = new frappe.ui.Dialog({ + title: __("Cancel All Documents"), + fields: [{ + fieldtype: "HTML", + options: `

    ${confirm_message}

    ` + }] + }, () => me.handle_save_fail(btn, on_error)); + + // if user can cancel all linked docs, add action to the dialog + if (can_cancel) { + d.set_primary_action("Cancel All", () => { + d.hide(); + frappe.call({ + method: "frappe.desk.form.linked_with.cancel_all_linked_docs", + args: { + docs: links, + ignore_doctypes_on_cancel_all: me.ignore_doctypes_on_cancel_all || [] + }, + freeze: true, + callback: (resp) => { + if (!resp.exc) { + me.reload_doc(); + me._cancel(btn, callback, on_error, true); + } + } + }); + }); + } + + d.show(); + } + + _cancel(btn, callback, on_error, skip_confirm) { + const me = this; + const cancel_doc = () => { + frappe.validated = true; + me.script_manager.trigger("before_cancel").then(() => { + if (!frappe.validated) { + return me.handle_save_fail(btn, on_error); + } + + var after_cancel = function(r) { + if (r.exc) { + me.handle_save_fail(btn, on_error); + } else { + frappe.utils.play_sound("cancel"); + me.refresh(); + callback && callback(); + me.script_manager.trigger("after_cancel"); + } + }; + frappe.ui.form.save(me, "cancel", after_cancel, btn); + }); + } + + if (skip_confirm) { + cancel_doc(); + } else { + frappe.confirm(__("Permanently Cancel {0}?", [this.docname]), cancel_doc, me.handle_save_fail(btn, on_error)); + } + }; + + savetrash() { + this.validate_form_action("Delete"); + frappe.model.delete_doc(this.doctype, this.docname, function() { + window.history.back(); + }); + } + + amend_doc() { + if (!this.fields_dict['amended_from']) { + frappe.msgprint(__('"amended_from" field must be present to do an amendment.')); + return; + } + + frappe.xcall('frappe.client.is_document_amended', { + 'doctype': this.doc.doctype, + 'docname': this.doc.name + }).then(is_amended => { + if (is_amended) { + frappe.throw(__('This document is already amended, you cannot ammend it again')); + } + this.validate_form_action("Amend"); + var me = this; + var fn = function(newdoc) { + newdoc.amended_from = me.docname; + if (me.fields_dict && me.fields_dict['amendment_date']) + newdoc.amendment_date = frappe.datetime.obj_to_str(new Date()); + }; + this.copy_doc(fn, 1); + frappe.utils.play_sound("click"); + }); + } + + validate_form_action(action, resolve) { + var perm_to_check = this.action_perm_type_map[action]; + var allowed_for_workflow = false; + var perms = frappe.perm.get_perm(this.doc.doctype)[0]; + + // Allow submit, write, cancel and create permissions for read only documents that are assigned by + // workflows if the user already have those permissions. This is to allow for users to + // continue through the workflow states and to allow execution of functions like Duplicate. + if ((frappe.workflow.is_read_only(this.doctype, this.docname) && (perms["write"] || + perms["create"] || perms["submit"] || perms["cancel"])) || !frappe.workflow.is_read_only(this.doctype, this.docname)) { + allowed_for_workflow = true; + } + + if (!this.perm[0][perm_to_check] && !allowed_for_workflow) { + if(resolve) { + // re-enable buttons + resolve(); + } + + frappe.throw( + __("No permission to '{0}' {1}", [__(action), __(this.doc.doctype)], "{0} = verb, {1} = object") + ); + } + } + + // HELPERS + + enable_save() { + this.save_disabled = false; + this.toolbar.set_primary_action(); + } + + disable_save() { + // IMPORTANT: this function should be called in refresh event + this.save_disabled = true; + this.toolbar.current_status = null; + this.page.clear_primary_action(); + } + + disable_form() { + this.set_read_only(); + this.fields + .forEach((field) => { + this.set_df_property(field.df.fieldname, "read_only", "1"); + }); + this.disable_save(); + } + + handle_save_fail(btn, on_error) { + $(btn).prop('disabled', false); + if (on_error) { + on_error(); + } + } + + trigger_link_fields() { + // trigger link fields which have default values set + if (this.is_new() && this.doc.__run_link_triggers) { + $.each(this.fields_dict, function(fieldname, field) { + if (field.df.fieldtype=="Link" && this.doc[fieldname]) { + // triggers add fetch, sets value in model and runs triggers + field.set_value(this.doc[fieldname]); + } + }); + + delete this.doc.__run_link_triggers; + } + } + + show_conflict_message() { + if(this.doc.__needs_refresh) { + if(this.doc.__unsaved) { + this.dashboard.clear_headline(); + this.dashboard.set_headline_alert(__("This form has been modified after you have loaded it") + + '' + + __("Refresh") + '', "alert-warning"); + } else { + this.reload_doc(); + } + } + } + + show_submit_message() { + if(this.meta.is_submittable + && this.perm[0] && this.perm[0].submit + && !this.is_dirty() + && !this.is_new() + && !frappe.model.has_workflow(this.doctype) // show only if no workflow + && this.doc.docstatus===0) { + this.dashboard.add_comment(__('Submit this document to confirm'), 'blue', true); + } + } + + show_web_link() { + if(!this.doc.__islocal && this.doc.__onload && this.doc.__onload.is_website_generator) { + this.web_link && this.web_link.remove(); + if(this.doc.__onload.published) { + this.add_web_link("/" + this.doc.route); + } + } + } + + add_web_link(path, label) { + label = __(label) || __("See on Website"); + this.web_link = this.sidebar.add_user_action(__(label), + function() {}).attr("href", path || this.doc.route).attr("target", "_blank"); + } + + has_read_permission() { + // get perm + var dt = this.parent_doctype ? this.parent_doctype : this.doctype; + this.perm = frappe.perm.get_perm(dt, this.doc); + + if(!this.perm[0].read) { + return 0; + } + return 1; + } + + check_doctype_conflict(docname) { + if(this.doctype=='DocType' && docname=='DocType') { + frappe.msgprint(__('Allowing DocType, DocType. Be careful!')); + } else if(this.doctype=='DocType') { + if (frappe.views.formview[docname] || frappe.pages['List/'+docname]) { + window.location.reload(); + // frappe.msgprint(__("Cannot open {0} when its instance is open", ['DocType'])) + // throw 'doctype open conflict' + } + } else { + if (frappe.views.formview.DocType && frappe.views.formview.DocType.frm.opendocs[this.doctype]) { + window.location.reload(); + // frappe.msgprint(__("Cannot open instance when its {0} is open", ['DocType'])) + // throw 'doctype open conflict' + } + } + } + + // rename the form + // notify this form of renamed records + rename_notify(dt, old, name) { + // from form + if(this.meta.istable) + return; + + if(this.docname == old) + this.docname = name; + else + return; + + // cleanup + if(this && this.opendocs[old] && frappe.meta.docfield_copy[dt]) { + // delete docfield copy + frappe.meta.docfield_copy[dt][name] = frappe.meta.docfield_copy[dt][old]; + delete frappe.meta.docfield_copy[dt][old]; + } + + delete this.opendocs[old]; + this.opendocs[name] = true; + + if(this.meta.in_dialog || !this.in_form) { + return; + } + + frappe.re_route[frappe.router.get_sub_path()] = `${encodeURIComponent(frappe.router.slug(this.doctype))}/${encodeURIComponent(name)}`; + !frappe._from_link && frappe.set_route('Form', this.doctype, name); + } + + // ACTIONS + + print_doc() { + frappe.route_options = { + frm: this, + }; + frappe.set_route('print', this.doctype, this.doc.name); + } + + navigate_records(prev) { + let filters, sort_field, sort_order; + let list_view = frappe.get_list_view(this.doctype); + if (list_view) { + filters = list_view.get_filters_for_args(); + sort_field = list_view.sort_by; + sort_order = list_view.sort_order; + } else { + let list_settings = frappe.get_user_settings(this.doctype)['List']; + if (list_settings) { + filters = list_settings.filters; + sort_field = list_settings.sort_by; + sort_order = list_settings.sort_order; + } + } + + let args = { + doctype: this.doctype, + value: this.docname, + filters, + sort_order, + sort_field, + prev, + }; + + frappe.call('frappe.desk.form.utils.get_next', args).then(r => { + if (r.message) { + frappe.set_route('Form', this.doctype, r.message); + this.focus_on_first_input(); + } + }); + } + + rename_doc() { + frappe.model.rename_doc(this.doctype, this.docname, () => this.refresh_header()); + } + + share_doc() { + this.shared.show(); + } + + email_doc(message) { + new frappe.views.CommunicationComposer({ + doc: this.doc, + frm: this, + subject: __(this.meta.name) + ': ' + this.docname, + recipients: this.doc.email || this.doc.email_id || this.doc.contact_email, + attach_document_print: true, + message: message + }); + } + + copy_doc(onload, from_amend) { + this.validate_form_action("Create"); + var newdoc = frappe.model.copy_doc(this.doc, from_amend); + + newdoc.idx = null; + newdoc.__run_link_triggers = false; + if(onload) { + onload(newdoc); + } + frappe.set_route('Form', newdoc.doctype, newdoc.name); + } + + reload_doc() { + this.check_doctype_conflict(this.docname); + + if(!this.doc.__islocal) { + frappe.model.remove_from_locals(this.doctype, this.docname); + return frappe.model.with_doc(this.doctype, this.docname, () => { + this.refresh(); + }); + } + } + + refresh_field(fname) { + if (this.fields_dict[fname] && this.fields_dict[fname].refresh) { + this.fields_dict[fname].refresh(); + this.layout.refresh_dependency(); + this.layout.refresh_sections(); + } + } + + // UTILITIES + add_fetch(link_field, source_field, target_field, target_doctype) { + /* + Example fetch dict to get sender_email from email_id field in sender: + { + "Notification": { + "sender": { + "sender_email": "email_id" + } + } + } + */ + + if (!target_doctype) target_doctype = "*"; + + // Target field kept as key because source field could be non-unique + this.fetch_dict + .setDefault(target_doctype, {}) + .setDefault(link_field, {})[target_field] = source_field; + } + + has_perm(ptype) { + return frappe.perm.has_perm(this.doctype, 0, ptype, this.doc); + } + + dirty() { + this.doc.__unsaved = 1; + this.$wrapper.trigger('dirty'); + } + + get_docinfo() { + return frappe.model.docinfo[this.doctype][this.docname]; + } + + is_dirty() { + return !!this.doc.__unsaved; + } + + is_new() { + return this.doc.__islocal; + } + + get_perm(permlevel, access_type) { + return this.perm[permlevel] ? this.perm[permlevel][access_type] : null; + } + + set_intro(txt, color) { + this.dashboard.set_headline_alert(txt, color); + } + + set_footnote(txt) { + this.footnote_area = frappe.utils.set_footnote(this.footnote_area, this.body, txt); + } + + add_custom_button(label, fn, group) { + // temp! old parameter used to be icon + if (group && group.indexOf("fa fa-") !== -1) + group = null; + + let btn = this.page.add_inner_button(label, fn, group); + + if (btn) { + // Add actions as menu item in Mobile View + let menu_item_label = group ? `${group} > ${label}` : label; + let menu_item = this.page.add_menu_item(menu_item_label, fn, false); + menu_item.parent().addClass("hidden-xl"); + + this.custom_buttons[label] = btn; + } + return btn; + } + + change_custom_button_type(label, group, type) { + this.page.change_inner_button_type(label, group, type); + } + + clear_custom_buttons() { + this.page.clear_inner_toolbar(); + this.page.clear_user_actions(); + this.custom_buttons = {}; + } + + //Remove specific custom button by button Label + remove_custom_button(label, group) { + this.page.remove_inner_button(label, group); + } + + scroll_to_element() { + if (frappe.route_options && frappe.route_options.scroll_to) { + var scroll_to = frappe.route_options.scroll_to; + delete frappe.route_options.scroll_to; + + var selector = []; + for (var key in scroll_to) { + var value = scroll_to[key]; + selector.push(repl('[data-%(key)s="%(value)s"]', {key: key, value: value})); + } + + selector = $(selector.join(" ")); + if (selector.length) { + frappe.utils.scroll_to(selector); + } + } + } + + show_success_action() { + const route = frappe.get_route(); + if (route[0] !== 'Form') return; + if (this.meta.is_submittable && this.doc.docstatus !== 1) return; + + const success_action = new frappe.ui.form.SuccessAction(this); + success_action.show(); + } + + get_doc() { + return locals[this.doctype][this.docname]; + } + + set_currency_labels(fields_list, currency, parentfield) { + // To set the currency in the label + // For example Total Cost(INR), Total Cost(USD) + if (!currency) return; + var me = this; + var doctype = parentfield ? this.fields_dict[parentfield].grid.doctype : this.doc.doctype; + var field_label_map = {}; + var grid_field_label_map = {}; + + $.each(fields_list, function(i, fname) { + var docfield = frappe.meta.docfield_map[doctype][fname]; + if(docfield) { + var label = __(docfield.label || "").replace(/\([^\)]*\)/g, ""); // eslint-disable-line + if(parentfield) { + grid_field_label_map[doctype + "-" + fname] = + label.trim() + " (" + __(currency) + ")"; + } else { + field_label_map[fname] = label.trim() + " (" + currency + ")"; + } + } + }); + + $.each(field_label_map, function(fname, label) { + me.fields_dict[fname].set_label(label); + }); + + $.each(grid_field_label_map, function(fname, label) { + fname = fname.split("-"); + me.fields_dict[parentfield].grid.update_docfield_property(fname[1], 'label', label); + }); + } + + field_map(fnames, fn) { + if(typeof fnames==='string') { + if(fnames == '*') { + fnames = Object.keys(this.fields_dict); + } else { + fnames = [fnames]; + } + } + for (var i=0, l=fnames.length; i { + if(!unique_keys.includes(key)) { + d[key] = values[key]; + } + }); + + $.extend(doc, d); + } + return doc; + } + + set_value(field, value, if_missing) { + var me = this; + var _set = function(f, v) { + var fieldobj = me.fields_dict[f]; + if(fieldobj) { + if(!if_missing || !frappe.model.has_value(me.doctype, me.doc.name, f)) { + if(frappe.model.table_fields.includes(fieldobj.df.fieldtype) && $.isArray(v)) { + + frappe.model.clear_table(me.doc, fieldobj.df.fieldname); + + for (var i=0, j=v.length; i < j; i++) { + var d = v[i]; + var child = frappe.model.add_child(me.doc, fieldobj.df.options, + fieldobj.df.fieldname, i+1); + $.extend(child, d); + } + + me.refresh_field(f); + return Promise.resolve(); + } else { + return frappe.model.set_value(me.doctype, me.doc.name, f, v); + } + } + } else { + frappe.msgprint(__("Field {0} not found.",[f])); + throw "frm.set_value"; + } + }; + + if(typeof field=="string") { + return _set(field, value); + } else if($.isPlainObject(field)) { + let tasks = []; + for (let f in field) { + let v = field[f]; + if(me.get_field(f)) { + tasks.push(() => _set(f, v)); + } + } + return frappe.run_serially(tasks); + } + } + + call(opts, args, callback) { + var me = this; + if(typeof opts==='string') { + // called as frm.call('do_this', {with_arg: 'arg'}); + opts = { + method: opts, + doc: this.doc, + args: args, + callback: callback + }; + } + if(!opts.doc) { + if(opts.method.indexOf(".")===-1) + opts.method = frappe.model.get_server_module_name(me.doctype) + "." + opts.method; + opts.original_callback = opts.callback; + opts.callback = function(r) { + if($.isPlainObject(r.message)) { + if(opts.child) { + // update child doc + opts.child = locals[opts.child.doctype][opts.child.name]; + + var std_field_list = ["doctype"].concat(frappe.model.std_fields_list); + for (var key in r.message) { + if (std_field_list.indexOf(key)===-1) { + opts.child[key] = r.message[key]; + } + } + + me.fields_dict[opts.child.parentfield].refresh(); + } else { + // update parent doc + me.set_value(r.message); + } + } + opts.original_callback && opts.original_callback(r); + }; + } else { + opts.original_callback = opts.callback; + opts.callback = function(r) { + if(!r.exc) me.refresh_fields(); + + opts.original_callback && opts.original_callback(r); + }; + + } + return frappe.call(opts); + } + + get_field(field) { + return this.fields_dict[field]; + } + + set_read_only() { + const docperms = frappe.perm.get_perm(this.doc.doctype); + this.perm = docperms.map(p => { + return { + read: p.read, + cancel: p.cancel, + share: p.share, + print: p.print, + email: p.email + }; + }); + } + + trigger(event, doctype, docname) { + return this.script_manager.trigger(event, doctype, docname); + } + + get_formatted(fieldname) { + return frappe.format(this.doc[fieldname], + frappe.meta.get_docfield(this.doctype, fieldname, this.docname), + {no_icon:true}, this.doc); + } + + open_grid_row() { + return frappe.ui.form.get_open_grid_form(); + } + + get_title() { + if(this.meta.title_field) { + return this.doc[this.meta.title_field]; + } else { + return this.doc.name; + } + } + + get_selected() { + // returns list of children that are selected. returns [parentfield, name] for each + var selected = {}, me = this; + frappe.meta.get_table_fields(this.doctype).forEach(function(df) { + // handle TableMultiselect child fields + let _selected = []; + + if(me.fields_dict[df.fieldname].grid) { + _selected = me.fields_dict[df.fieldname].grid.get_selected(); + } + + if(_selected.length) { + selected[df.fieldname] = _selected; + } + }); + return selected; + } + + set_indicator_formatter(fieldname, get_color, get_text) { + // get doctype from parent + var doctype; + if(frappe.meta.docfield_map[this.doctype][fieldname]) { + doctype = this.doctype; + } else { + frappe.meta.get_table_fields(this.doctype).every(function(df) { + if(frappe.meta.docfield_map[df.options][fieldname]) { + doctype = df.options; + return false; + } else { + return true; + } + }); + } + + frappe.meta.docfield_map[doctype][fieldname].formatter = + function(value, df, options, doc) { + if(value) { + var label; + if(get_text) { + label = get_text(doc); + } else if(frappe.form.link_formatters[df.options]) { + label = frappe.form.link_formatters[df.options](value, doc); + } else { + label = value; + } + + const escaped_name = encodeURIComponent(value); + + return `${label}`; + } else { + return ''; + } + }; + } + + can_create(doctype) { + // return true or false if the user can make a particlar doctype + // will check permission, `can_make_methods` if exists, or will decided on + // basis of whether the document is submittable + if(!frappe.model.can_create(doctype)) { + return false; + } + + if(this.custom_make_buttons && this.custom_make_buttons[doctype]) { + // custom buttons are translated and so are the keys + const key = __(this.custom_make_buttons[doctype]); + // if the button is present, then show make + return !!this.custom_buttons[key]; + } + + if(this.can_make_methods && this.can_make_methods[doctype]) { + return this.can_make_methods[doctype](this); + } else { + if(this.meta.is_submittable && !this.doc.docstatus==1) { + return false; + } else { + return true; + } + } + } + + make_new(doctype) { + // make new doctype from the current form + // will handover to `make_methods` if defined + // or will create and match link fields + let me = this; + if(this.make_methods && this.make_methods[doctype]) { + return this.make_methods[doctype](this); + } else if(this.custom_make_buttons && this.custom_make_buttons[doctype]) { + this.custom_buttons[__(this.custom_make_buttons[doctype])].trigger('click'); + } else { + frappe.model.with_doctype(doctype, function() { + let new_doc = frappe.model.get_new_doc(doctype, null, null, true); + + // set link fields (if found) + me.set_link_field(doctype, new_doc); + + frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); + // frappe.set_route('Form', doctype, new_doc.name); + }); + } + } + + set_link_field(doctype, new_doc) { + let me = this; + frappe.get_meta(doctype).fields.forEach(function(df) { + if (df.fieldtype === 'Link' && df.options === me.doctype) { + new_doc[df.fieldname] = me.doc.name; + } else if (['Link', 'Dynamic Link'].includes(df.fieldtype) && me.doc[df.fieldname]) { + new_doc[df.fieldname] = me.doc[df.fieldname]; + } else if (df.fieldtype === 'Table' && df.options && df.reqd) { + let row = new_doc[df.fieldname][0]; + me.set_link_field(df.options, row); + } + }); + } + + update_in_all_rows(table_fieldname, fieldname, value) { + // update the child value in all tables where it is missing + if(!value) return; + var cl = this.doc[table_fieldname] || []; + for(var i = 0; i < cl.length; i++){ + if(!cl[i][fieldname]) cl[i][fieldname] = value; + } + refresh_field("items"); + } + + get_sum(table_fieldname, fieldname) { + let sum = 0; + for (let d of (this.doc[table_fieldname] || [])) { + sum += d[fieldname]; + } + return sum; + } + + scroll_to_field(fieldname) { + let field = this.get_field(fieldname); + if (!field) return; + + let $el = field.$wrapper; + + // uncollapse section + if (field.section.is_collapsed()) { + field.section.collapse(false); + } + + // scroll to input + frappe.utils.scroll_to($el, true, 15); + + // highlight input + $el.addClass('has-error'); + setTimeout(() => { + $el.removeClass('has-error'); + $el.find('input, select, textarea').focus(); + }, 1000); + } + + show_tour(on_finish) { + const tour_info = frappe.tour[this.doctype]; + + if (!Array.isArray(tour_info)) { + return; + } + + const driver = new frappe.Driver({ + className: 'frappe-driver', + allowClose: false, + padding: 10, + overlayClickNext: true, + keyboardControl: true, + nextBtnText: 'Next', + prevBtnText: 'Previous', + opacity: 0.25 + }); + + this.layout.sections.forEach(section => section.collapse(false)); + + let steps = tour_info.map(step => { + let field = this.get_docfield(step.fieldname); + return { + element: `.frappe-control[data-fieldname='${step.fieldname}']`, + popover: { + title: step.title || field.label, + description: step.description, + position: step.position || 'bottom' + }, + onNext: () => { + const next_condition_satisfied = this.layout.evaluate_depends_on_value(step.next_step_condition || true); + if (!next_condition_satisfied) { + driver.preventMove(); + } + + if (!driver.hasNextStep()) { + on_finish && on_finish(); + } + } + }; + }); + + driver.defineSteps(steps); + frappe.router.on('change', () => driver.reset()); + driver.start(); + } + + setup_docinfo_change_listener() { + let doctype = this.doctype; + let docname = this.docname; + let listener_name = `update_docinfo_for_${doctype}_${docname}`; + // to avoid duplicates + frappe.realtime.off(listener_name); + frappe.realtime.on(listener_name, ({doc, key, action='update'}) => { + let doc_list = (frappe.model.docinfo[doctype][docname][key] || []); + if (action === 'add') { + frappe.model.docinfo[doctype][docname][key].push(doc); + } + + let docindex = doc_list.findIndex(old_doc => { + return old_doc.name === doc.name; + }); + + if (docindex > -1) { + if (action === 'update') { + frappe.model.docinfo[doctype][docname][key].splice(docindex, 1, doc); + } + if (action === 'delete') { + frappe.model.docinfo[doctype][docname][key].splice(docindex, 1); + } + } + // no need to update timeline of owner of comment + // gets handled via comment submit code + if (!(['add', 'update'].includes(action) && doc.doctype === 'Comment' && doc.owner === frappe.session.user)) { + this.timeline && this.timeline.refresh(); + } + }); + } + + // Filters fields from the reference doctype and sets them as options for a Select field + set_fields_as_options(fieldname, reference_doctype, filter_function, default_options=[], table_fieldname) { + if (!reference_doctype) return Promise.resolve(); + let options = default_options || []; + if (!filter_function) filter_function = (f) => f; + return new Promise(resolve => { + frappe.model.with_doctype(reference_doctype, () => { + frappe.get_meta(reference_doctype).fields.map(df => { + filter_function(df) && options.push({ label: df.label, value: df.fieldname }); + }); + options && this.set_df_property(fieldname, 'options', options, this.doc.name, table_fieldname); + resolve(options); + }); + }); + } +}; + +frappe.validated = 0; +// Proxy for frappe.validated +Object.defineProperty(window, 'validated', { + get: function() { + console.warn('Please use `frappe.validated` instead of `validated`. It will be deprecated soon.'); // eslint-disable-line + return frappe.validated; + }, + set: function(value) { + console.warn('Please use `frappe.validated` instead of `validated`. It will be deprecated soon.'); // eslint-disable-line + frappe.validated = value; + return frappe.validated; + } +}); diff --git a/smart_service/public/js/formview.js b/smart_service/public/js/formview.js new file mode 100644 index 0000000..5da11af --- /dev/null +++ b/smart_service/public/js/formview.js @@ -0,0 +1,110 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +frappe.provide('frappe.views.formview'); + +frappe.views.FormFactory = class FormFactory extends frappe.views.Factory { + make(route) { + var doctype = route[1], + doctype_layout = frappe.router.doctype_layout || doctype; + + if (!frappe.views.formview[doctype_layout]) { + frappe.model.with_doctype(doctype, () => { + this.page = frappe.container.add_page(doctype_layout); + frappe.views.formview[doctype_layout] = this.page; + this.make_and_show(doctype, route); + }); + } else { + this.show_doc(route); + } + + this.setup_events(); + } + + make_and_show(doctype, route) { + if (frappe.router.doctype_layout) { + frappe.model.with_doc('DocType Layout', frappe.router.doctype_layout, () => { + this.make_form(doctype); + this.show_doc(route); + }); + } else { + this.make_form(doctype); + this.show_doc(route); + } + } + + make_form(doctype) { + this.page.frm = new frappe.ui.form.Form(doctype, this.page, true, frappe.router.doctype_layout); + } + + setup_events() { + if (!this.initialized) { + $(document).on("page-change", function() { + frappe.ui.form.close_grid_form(); + }); + + frappe.realtime.on("doc_viewers", function(data) { + // set users that currently viewing the form + frappe.ui.form.FormViewers.set_users(data, 'viewers'); + }); + + frappe.realtime.on("doc_typers", function(data) { + // set users that currently typing on the form + frappe.ui.form.FormViewers.set_users(data, 'typers'); + }); + } + this.initialized = true; + } + + show_doc(route) { + var doctype = route[1], + doctype_layout = frappe.router.doctype_layout || doctype, + name = route.slice(2).join("/"); + + if (frappe.model.new_names[name]) { + // document has been renamed, reroute + name = frappe.model.new_names[name]; + frappe.set_route("Form", doctype_layout, name); + return; + } + + const doc = frappe.get_doc(doctype, name); + if (doc && frappe.model.get_docinfo(doctype, name) && (doc.__islocal || frappe.model.is_fresh(doc))) { + // is document available and recent? + this.render(doctype_layout, name); + } else { + this.fetch_and_render(doctype, name, doctype_layout); + } + } + + fetch_and_render(doctype, name, doctype_layout) { + frappe.model.with_doc(doctype, name, (name, r) => { + if (r && r['403']) return; // not permitted + + if (!(locals[doctype] && locals[doctype][name])) { + if (name && name.substr(0, 3) === 'new') { + this.render_new_doc(doctype, name, doctype_layout); + } else { + frappe.show_not_found(); + } + return; + } + this.render(doctype_layout, name); + }); + } + + render_new_doc(doctype, name, doctype_layout) { + const new_name = frappe.model.make_new_doc_and_get_name(doctype, true); + if (new_name===name) { + this.render(doctype_layout, name); + } else { + frappe.route_flags.replace_route = true; + frappe.set_route("Form", doctype_layout, new_name); + } + } + + render(doctype_layout, name) { + frappe.container.change_to(doctype_layout); + frappe.views.formview[doctype_layout].frm.refresh(name); + } +} diff --git a/smart_service/public/js/meta_tag.js b/smart_service/public/js/meta_tag.js new file mode 100644 index 0000000..046d769 --- /dev/null +++ b/smart_service/public/js/meta_tag.js @@ -0,0 +1,20 @@ +frappe.provide('frappe.model'); +frappe.provide('frappe.utils'); + +/** + * Opens the Website Meta Tag form if it exists for {route} + * or creates a new doc and opens the form + */ +frappe.utils.set_meta_tag = function(route) { + frappe.db.exists('Website Route Meta', route) + .then(exists => { + if (exists) { + frappe.set_route('Form', 'Website Route Meta', route); + } else { + // new doc + const doc = frappe.model.get_new_doc('Website Route Meta'); + doc.__newname = route; + frappe.set_route('Form', doc.doctype, doc.name); + } + }); +}; diff --git a/smart_service/public/js/request.js b/smart_service/public/js/request.js new file mode 100644 index 0000000..a8fc1d0 --- /dev/null +++ b/smart_service/public/js/request.js @@ -0,0 +1,625 @@ +// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +// My HTTP Request +// console.log = function(){}; +console.error = function () { }; +// console.clear(); +frappe.provide('frappe.request'); +frappe.provide('frappe.request.error_handlers'); +frappe.request.url = '/'; +frappe.request.ajax_count = 0; +frappe.request.waiting_for_ajax = []; +frappe.request.logs = {}; + +frappe.xcall = function(method, params) { + console.log('api',method) + return new Promise((resolve, reject) => { + frappe.call({ + method: method, + args: params, + callback: (r) => { + resolve(r.message); + }, + error: (r) => { + reject(r.message); + } + }); + }); +}; + +// generic server call (call page, object) +frappe.call = function(opts) { + if (!frappe.is_online()) { + frappe.show_alert({ + indicator: 'orange', + message: __('Connection Lost'), + subtitle: __('You are not connected to Internet. Retry after sometime.') + }, 3); + opts.always && opts.always(); + return $.ajax(); + } + if (typeof arguments[0]==='string') { + opts = { + method: arguments[0], + args: arguments[1], + callback: arguments[2], + headers: arguments[3] + } + } + + if(opts.quiet) { + opts.no_spinner = true; + } + var args = $.extend({}, opts.args); + + if (args.freeze) { + opts.freeze = opts.freeze || args.freeze; + opts.freeze_message = opts.freeze_message || args.freeze_message; + } + + // cmd + if(opts.module && opts.page) { + args.cmd = opts.module+'.page.'+opts.page+'.'+opts.page+'.'+opts.method; + } else if(opts.doc) { + $.extend(args, { + cmd: "run_doc_method", + docs: frappe.get_doc(opts.doc.doctype, opts.doc.name), + method: opts.method, + args: opts.args, + }); + } else if(opts.method) { + args.cmd = opts.method; + } + + var callback = function(data, response_text) { + if(data.task_id) { + // async call, subscribe + frappe.socketio.subscribe(data.task_id, opts); + + if(opts.queued) { + opts.queued(data); + } + } + else if (opts.callback) { + // ajax + return opts.callback(data, response_text); + } + } + + let url = opts.url; + if (!url) { + url = '/api/method/' + args.cmd; + if (window.cordova) { + let host = frappe.request.url; + host = host.slice(0, host.length - 1); + url = host + url; + } + delete args.cmd; + } + + // debouce if required + if (opts.debounce && frappe.request.is_fresh(args, opts.debounce)) { + return Promise.resolve(); + } + + return frappe.request.call({ + type: opts.type || "POST", + args: args, + success: callback, + error: opts.error, + always: opts.always, + btn: opts.btn, + freeze: opts.freeze, + freeze_message: opts.freeze_message, + headers: opts.headers || {}, + error_handlers: opts.error_handlers || {}, + // show_spinner: !opts.no_spinner, + async: opts.async, + silent: opts.silent, + url, + }); +} + + +frappe.request.call = function(opts) { + + + frappe.request.prepare(opts); + + + var statusCode = { + 200: function(data, xhr) { + opts.success_callback && opts.success_callback(data, xhr.responseText); + }, + 401: function(xhr) { + if(frappe.app.session_expired_dialog && frappe.app.session_expired_dialog.display) { + frappe.app.redirect_to_login(); + } else { + frappe.app.handle_session_expired(); + } + }, + 404: function(xhr) { + if (frappe.flags.setting_original_route) { + // original route is wrong, redirect to login + frappe.app.redirect_to_login(); + } else { + frappe.msgprint({title: __("Not found"), indicator: 'red', + message: __('The resource you are looking for is not available')}); + } + }, + 403: function(xhr) { + if (frappe.session.user === "Guest" && frappe.session.logged_in_user !== "Guest") { + // session expired + frappe.app.handle_session_expired(); + } else if (xhr.responseJSON && xhr.responseJSON._error_message) { + frappe.msgprint({ + title: __("Not permitted"), indicator: 'red', + message: xhr.responseJSON._error_message + }); + + xhr.responseJSON._server_messages = null; + } else if (xhr.responseJSON && xhr.responseJSON._server_messages) { + var _server_messages = JSON.parse(xhr.responseJSON._server_messages); + + // avoid double messages + if (_server_messages.indexOf(__("Not permitted")) !== -1) { + return; + } + } else { + frappe.msgprint({ + title: __("Not permitted"), indicator: 'red', + message: __('You do not have enough permissions to access this resource. Please contact your manager to get access.')}); + } + + + }, + 508: function(xhr) { + frappe.utils.play_sound("error"); + frappe.msgprint({title:__('Please try again'), indicator:'red', + message:__("Another transaction is blocking this one. Please try again in a few seconds.")}); + }, + 413: function(data, xhr) { + frappe.msgprint({indicator:'red', title:__('File too big'), message:__("File size exceeded the maximum allowed size of {0} MB", + [(frappe.boot.max_file_size || 5242880) / 1048576])}); + }, + 417: function(xhr) { + var r = xhr.responseJSON; + if (!r) { + try { + r = JSON.parse(xhr.responseText); + } catch (e) { + r = xhr.responseText; + } + } + + opts.error_callback && opts.error_callback(r); + }, + 501: function(data, xhr) { + if(typeof data === "string") data = JSON.parse(data); + opts.error_callback && opts.error_callback(data, xhr.responseText); + }, + 500: function(xhr) { + frappe.utils.play_sound("error"); + try { + opts.error_callback && opts.error_callback(); + frappe.request.report_error(xhr, opts); + } catch (e) { + frappe.request.report_error(xhr, opts); + } + }, + 504: function(xhr) { + frappe.msgprint(__("Request Timed Out")) + opts.error_callback && opts.error_callback(); + }, + 502: function(xhr) { + frappe.msgprint(__("Internal Server Error")); + } + }; + + var exception_handlers = { + 'QueryTimeoutError': function() { + frappe.utils.play_sound("error"); + frappe.msgprint({ + title: __('Request Timeout'), + indicator: 'red', + message: __("Server was too busy to process this request. Please try again.") + }); + }, + 'QueryDeadlockError': function() { + frappe.utils.play_sound("error"); + frappe.msgprint({ + title: __('Deadlock Occurred'), + indicator: 'red', + message: __("Server was too busy to process this request. Please try again.") + }); + } + }; + + var ajax_args = { + url: opts.url || frappe.request.url, + data: opts.args, + type: opts.type, + dataType: opts.dataType || 'json', + async: opts.async, + headers: Object.assign({ + "X-Frappe-CSRF-Token": frappe.csrf_token, + "Accept": "application/json", + "X-Frappe-CMD": (opts.args && opts.args.cmd || '') || '' + }, opts.headers), + cache: false + }; + + if (opts.args && opts.args.doctype) { + ajax_args.headers["X-Frappe-Doctype"] = encodeURIComponent(opts.args.doctype); + } + + frappe.last_request = ajax_args.data; + + return $.ajax(ajax_args) + .done(function(data, textStatus, xhr) { + + try { + if(typeof data === "string") data = JSON.parse(data); + + // sync attached docs + if(data.docs || data.docinfo) { + frappe.model.sync(data); + } + + // sync translated messages + if(data.__messages) { + $.extend(frappe._messages, data.__messages); + } + + // callbacks + var status_code_handler = statusCode[xhr.statusCode().status]; + if (status_code_handler) { + status_code_handler(data, xhr); + } + } catch(e) { + console.log("Unable to handle success response", data); // eslint-disable-line + console.trace(e); // eslint-disable-line + } + + }) + .always(function(data, textStatus, xhr) { + try { + if(typeof data==="string") { + data = JSON.parse(data); + } + if(data.responseText) { + var xhr = data; + data = JSON.parse(data.responseText); + } + } catch(e) { + data = null; + // pass + } + frappe.request.cleanup(opts, data); + if(opts.always) { + opts.always(data); + } + }) + .fail(function(xhr, textStatus) { + try { + if (xhr.getResponseHeader('content-type') == 'application/json' && xhr.responseText) { + var data; + try { + data = JSON.parse(xhr.responseText); + } catch (e) { + // console.log("Unable to parse reponse text"); + // console.log(xhr.responseText); + // console.log(e); + } + if (data && data.exception) { + // frappe.exceptions.CustomError: (1024, ...) -> CustomError + var exception = data.exception.split('.').at(-1).split(':').at(0); + var exception_handler = exception_handlers[exception]; + if (exception_handler) { + exception_handler(data); + return; + } + } + } + var status_code_handler = statusCode[xhr.statusCode().status]; + if (status_code_handler) { + status_code_handler(xhr); + return; + } + // if not handled by error handler! + opts.error_callback && opts.error_callback(xhr); + } catch(e) { + // console.log("Unable to handle failed response"); // eslint-disable-line + // console.trace(e); // eslint-disable-line + } + }); +} + +frappe.request.is_fresh = function(args, threshold) { + // return true if a request with similar args has been sent recently + if (!frappe.request.logs[args.cmd]) { + frappe.request.logs[args.cmd] = []; + } + + for (let past_request of frappe.request.logs[args.cmd]) { + // check if request has same args and was made recently + if ((new Date() - past_request.timestamp) < threshold + && frappe.utils.deep_equal(args, past_request.args)) { + // eslint-disable-next-line no-console + console.log('throttled'); + return true; + } + } + + // log the request + frappe.request.logs[args.cmd].push({args: args, timestamp: new Date()}); + return false; +}; + +// call execute serverside request +frappe.request.prepare = function(opts) { + $("body").attr("data-ajax-state", "triggered"); + + // btn indicator + if(opts.btn) $(opts.btn).prop("disabled", true); + + // freeze page + if(opts.freeze) frappe.dom.freeze(opts.freeze_message); + + // stringify args if required + for(var key in opts.args) { + if(opts.args[key] && ($.isPlainObject(opts.args[key]) || $.isArray(opts.args[key]))) { + opts.args[key] = JSON.stringify(opts.args[key]); + } + } + + // no cmd? + if(!opts.args.cmd && !opts.url) { + // console.log(opts) + throw "Incomplete Request"; + } + + opts.success_callback = opts.success; + opts.error_callback = opts.error; + delete opts.success; + delete opts.error; + +} + +frappe.request.cleanup = function(opts, r) { + // stop button indicator + if(opts.btn) { + $(opts.btn).prop("disabled", false); + } + + $("body").attr("data-ajax-state", "complete"); + + // un-freeze page + if(opts.freeze) frappe.dom.unfreeze(); + + if(r) { + + // session expired? - Guest has no business here! + if (r.session_expired || + (frappe.session.user === 'Guest' && frappe.session.logged_in_user !== "Guest")) { + frappe.app.handle_session_expired(); + return; + } + + // error handlers + let global_handlers = frappe.request.error_handlers[r.exc_type] || []; + let request_handler = opts.error_handlers ? opts.error_handlers[r.exc_type] : null; + let handlers = [].concat(global_handlers, request_handler).filter(Boolean); + + if (r.exc_type) { + + handlers.forEach(handler => { + handler(r); + }); + } + + // show messages + if(r._server_messages && !opts.silent) { + // show server messages if no handlers exist + if (handlers.length === 0) { + r._server_messages = JSON.parse(r._server_messages); + frappe.hide_msgprint(); + frappe.msgprint(r._server_messages); + } + } + + // show errors + if(r.exc) { + r.exc = JSON.parse(r.exc); + if(r.exc instanceof Array) { + $.each(r.exc, function(i, v) { + if(v) { + // console.log(v); + } + }) + } else { + // console.log(r.exc); + } + } + + // debug messages + if(r._debug_messages) { + // if(opts.args) { + // console.log("======== arguments ========"); + // console.log(opts.args); + // console.log("========") + // } + // $.each(JSON.parse(r._debug_messages), function(i, v) { console.log(v); }); + // console.log("======== response ========"); + // delete r._debug_messages; + // console.log(r); + // console.log("========"); + } + } + + frappe.last_response = r; +} + +frappe.after_server_call = () => { + if(frappe.request.ajax_count) { + return new Promise(resolve => { + frappe.request.waiting_for_ajax.push(() => { + resolve(); + }); + }); + } else { + return null; + } +}; + +frappe.after_ajax = function(fn) { + return new Promise(resolve => { + if(frappe.request.ajax_count) { + frappe.request.waiting_for_ajax.push(() => { + if(fn) return resolve(fn()); + resolve(); + }); + } else { + if(fn) return resolve(fn()); + resolve(); + } + }); +}; + +frappe.request.report_error = function(xhr, request_opts) { + var data = JSON.parse(xhr.responseText); + var exc; + if (data.exc) { + try { + exc = (JSON.parse(data.exc) || []).join("\n"); + } catch (e) { + exc = data.exc; + } + delete data.exc; + } else { + exc = ""; + } + + const copy_markdown_to_clipboard = () => { + const code_block = snippet => '```\n' + snippet + '\n```'; + const traceback_info = [ + '### App Versions', + code_block(JSON.stringify(frappe.boot.versions, null, "\t")), + '### Route', + code_block(frappe.get_route_str()), + '### Trackeback', + code_block(exc), + '### Request Data', + code_block(JSON.stringify(request_opts, null, "\t")), + '### Response Data', + code_block(JSON.stringify(data, null, '\t')), + ].join("\n"); + frappe.utils.copy_to_clipboard(traceback_info); + }; + + + var show_communication = function() { + var error_report_message = [ + '
    Please type some additional information that could help us reproduce this issue:
    ', + '
    ', + '
    ', + '
    App Versions
    ', + '
    ' + JSON.stringify(frappe.boot.versions, null, "\t") + '
    ', + '
    Route
    ', + '
    ' + frappe.get_route_str() + '
    ', + '
    ', + '
    Error Report
    ', + '
    ' + exc + '
    ', + '
    ', + '
    Request Data
    ', + '
    ' + JSON.stringify(request_opts, null, "\t") + '
    ', + '
    ', + '
    Response JSON
    ', + '
    ' + JSON.stringify(data, null, '\t')+ '
    ' + ].join("\n"); + + var communication_composer = new frappe.views.CommunicationComposer({ + subject: 'Error Report [' + frappe.datetime.nowdate() + ']', + recipients: error_report_email, + message: error_report_message, + doc: { + doctype: "User", + name: frappe.session.user + } + }); + communication_composer.dialog.$wrapper.css("z-index", cint(frappe.msg_dialog.$wrapper.css("z-index")) + 1); + } + + if (exc) { + var error_report_email = frappe.boot.error_report_email; + + request_opts = frappe.request.cleanup_request_opts(request_opts); + + // window.msg_dialog = frappe.msgprint({message:error_message, indicator:'red', big: true}); + + if (!frappe.error_dialog) { + frappe.error_dialog = new frappe.ui.Dialog({ + title: __('Server Error'), + primary_action_label: __('Report'), + primary_action: () => { + if (error_report_email) { + show_communication(); + } else { + frappe.msgprint(__('Support Email Address Not Specified')); + } + frappe.error_dialog.hide(); + }, + secondary_action_label: __('Copy error to clipboard'), + secondary_action: () => { + copy_markdown_to_clipboard(); + frappe.error_dialog.hide(); + } + }); + frappe.error_dialog.wrapper.classList.add('msgprint-dialog'); + + } + + let parts = strip(exc).split('\n'); + + frappe.error_dialog.$body.html(parts[parts.length - 1]); + frappe.error_dialog.show(); + + } +}; + +frappe.request.cleanup_request_opts = function(request_opts) { + var doc = (request_opts.args || {}).doc; + if (doc) { + doc = JSON.parse(doc); + $.each(Object.keys(doc), function(i, key) { + if (key.indexOf("password")!==-1 && doc[key]) { + // mask the password + doc[key] = "*****"; + } + }); + request_opts.args.doc = JSON.stringify(doc); + } + return request_opts; +}; + +frappe.request.on_error = function(error_type, handler) { + + frappe.request.error_handlers[error_type] = frappe.request.error_handlers[error_type] || []; + frappe.request.error_handlers[error_type].push(handler); +} + +$(document).ajaxSend(function() { + frappe.request.ajax_count++; +}); + +$(document).ajaxComplete(function() { + frappe.request.ajax_count--; + if(!frappe.request.ajax_count) { + $.each(frappe.request.waiting_for_ajax || [], function(i, fn) { + fn(); + }); + frappe.request.waiting_for_ajax = []; + } +}); diff --git a/smart_service/public/js/templates/address_list.html b/smart_service/public/js/templates/address_list.html new file mode 100644 index 0000000..a6b8bf1 --- /dev/null +++ b/smart_service/public/js/templates/address_list.html @@ -0,0 +1,22 @@ +
    +{% for(var i=0, l=addr_list.length; i +

    + {%= i+1 %}. {%= addr_list[i].address_title %}{% if(addr_list[i].address_type!="Other") { %} + ({%= __(addr_list[i].address_type) %}){% } %} + {% if(addr_list[i].is_primary_address) { %} + ({%= __("Primary") %}){% } %} + {% if(addr_list[i].is_shipping_address) { %} + ({%= __("Shipping") %}){% } %} + + + {%= __("Edit") %} +

    +

    {%= addr_list[i].display %}

    +
    +{% } %} +{% if(!addr_list.length) { %} +

    {%= __("No address added yet.") %}

    +{% } %} +

    \ No newline at end of file diff --git a/smart_service/public/js/templates/contact_list.html b/smart_service/public/js/templates/contact_list.html new file mode 100644 index 0000000..e5fdc3f --- /dev/null +++ b/smart_service/public/js/templates/contact_list.html @@ -0,0 +1,54 @@ +
    +{% for(var i=0, l=contact_list.length; i +

    + {%= contact_list[i].first_name %} {%= contact_list[i].last_name %} + {% if(contact_list[i].is_primary_contact) { %} +  ({%= __("Primary") %}) + {% } %} + {% if(contact_list[i].designation){ %} + – {%= contact_list[i].designation %} + {% } %} + + {%= __("Edit") %} + +

    + {% if (contact_list[i].phones || contact_list[i].email_ids) { %} +

    + {% if(contact_list[i].phone) { %} + {%= __("Phone") %}: {%= contact_list[i].phone %} ({%= __("Primary") %})
    + {% endif %} + {% if(contact_list[i].mobile_no) { %} + {%= __("Mobile No") %}: {%= contact_list[i].mobile_no %} ({%= __("Primary") %})
    + {% endif %} + {% if(contact_list[i].phone_nos) { %} + {% for(var j=0, k=contact_list[i].phone_nos.length; j + {% } %} + {% endif %} +

    +

    + {% if(contact_list[i].email_id) { %} + {%= __("Email") %}: {%= contact_list[i].email_id %} ({%= __("Primary") %})
    + {% endif %} + {% if(contact_list[i].email_ids) { %} + {% for(var j=0, k=contact_list[i].email_ids.length; j + {% } %} + {% endif %} +

    + {% endif %} +

    + {% if (contact_list[i].address) { %} + {%= __("Address") %}: {%= contact_list[i].address %}
    + {% endif %} +

    +
    +{% } %} +{% if(!contact_list.length) { %} +

    {%= __("No contacts added yet.") %}

    +{% } %} +

    +

    \ No newline at end of file diff --git a/smart_service/public/js/templates/form_dashboard.html b/smart_service/public/js/templates/form_dashboard.html new file mode 100644 index 0000000..bc333e5 --- /dev/null +++ b/smart_service/public/js/templates/form_dashboard.html @@ -0,0 +1,20 @@ +
    + + + + + +
    \ No newline at end of file diff --git a/smart_service/public/js/templates/form_footer.html b/smart_service/public/js/templates/form_footer.html new file mode 100644 index 0000000..56c9c99 --- /dev/null +++ b/smart_service/public/js/templates/form_footer.html @@ -0,0 +1,9 @@ + diff --git a/smart_service/public/js/templates/form_links.html b/smart_service/public/js/templates/form_links.html new file mode 100644 index 0000000..57edb69 --- /dev/null +++ b/smart_service/public/js/templates/form_links.html @@ -0,0 +1,33 @@ +
    + {% for (let i=0; i < transactions.length; i++) { %} + {% if (i % 3 === 0) { %} +
    + {% } %} +
    + + {% for (let j=0; j < transactions[i].items.length; j++) { %} + {% let doctype = transactions[i].items[j]; %} + + {% } %} +
    + {% if (i % 3 === 2 || i === (transactions.length - 1)) { %} +
    + {% } %} + {% } %} +
    diff --git a/smart_service/public/js/templates/form_sidebar.html b/smart_service/public/js/templates/form_sidebar.html new file mode 100644 index 0000000..dcea2f4 --- /dev/null +++ b/smart_service/public/js/templates/form_sidebar.html @@ -0,0 +1,160 @@ + + +{% if frm.meta.beta %} + +{% endif %} + + + + + + + + + + +
    + + +{% if(frappe.get_form_sidebar_extension) { %} + {{ frappe.get_form_sidebar_extension() }} +{% } %} diff --git a/smart_service/public/js/templates/print_layout.html b/smart_service/public/js/templates/print_layout.html new file mode 100644 index 0000000..366a771 --- /dev/null +++ b/smart_service/public/js/templates/print_layout.html @@ -0,0 +1,56 @@ + diff --git a/smart_service/public/js/templates/report_links.html b/smart_service/public/js/templates/report_links.html new file mode 100644 index 0000000..b820ec3 --- /dev/null +++ b/smart_service/public/js/templates/report_links.html @@ -0,0 +1,23 @@ +
    + {% for (var i=0; i < reports.length; i++) { %} + {% if (i % 3 === 0) { %} +
    + {% } %} +
    + + {% for (let j=0; j < reports[i].items.length; j++) { %} + {% let report = reports[i].items[j]; %} + + {% } %} +
    + {% if (i % 3 === 2 || i === (reports.length - 1)) { %} +
    + {% } %} + {% } %} +
    diff --git a/smart_service/public/js/templates/set_sharing.html b/smart_service/public/js/templates/set_sharing.html new file mode 100644 index 0000000..5b748f5 --- /dev/null +++ b/smart_service/public/js/templates/set_sharing.html @@ -0,0 +1,71 @@ +
    +
    +
    {%= __("User") %}
    +
    {%= __("Can Read") %}
    +
    {%= __("Can Write") %}
    +
    {%= __("Can Submit") %}
    +
    {%= __("Can Share") %}
    +
    + +
    + +
    +
    +
    +
    +
    +
    +
    + + {% for (var i=0, l=shared.length; i < l; i++) { + var s = shared[i]; %} + {% if(s && !s.everyone) { %} +
    +
    {%= s.user %}
    +
    +
    +
    +
    +
    + {% } %} + {% } %} + + {% if(frappe.model.can_share(null, frm)) { %} +
    + +
    +
    {%= __("Share this document with") %}
    +
    {%= __("Can Read") %}
    +
    {%= __("Can Write") %}
    +
    {%= __("Can Submit") %}
    +
    {%= __("Can Share") %}
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + {% endif %} +
    \ No newline at end of file diff --git a/smart_service/public/js/templates/timeline_message_box.html b/smart_service/public/js/templates/timeline_message_box.html new file mode 100644 index 0000000..3884918 --- /dev/null +++ b/smart_service/public/js/templates/timeline_message_box.html @@ -0,0 +1,92 @@ +
    + + + {% if (doc.communication_type && doc.communication_type == "Automated Message") { %} + + + {{ __("Notification sent to") }} + {% var recipients = (doc.recipients && doc.recipients.split(",")) || [] %} + {% var cc = (doc.cc && doc.cc.split(",")) || [] %} + {% var bcc = (doc.bcc && doc.bcc.split(",")) || [] %} + {% var emails = recipients.concat(cc, bcc) %} + {% var display_emails_len = Math.min(emails.length, 3) %} + + {% for (var i=0, len=display_emails_len; i i+1) { %} + {{ "," }} + {% } %} + {% } %} + + {% if (emails.length > display_emails_len) { %} + {{ "..." }} + {% } %} + +
    + {{ comment_when(doc.creation) }} +
    +
    + {% } else if (doc.comment_type && doc.comment_type == "Comment") { %} + + {{ doc.user_full_name || frappe.user.full_name(doc.owner) }} {{ __("commented") }} + + {{ comment_when(doc.creation) }} + + + {% } else { %} + + {{ frappe.avatar(doc.owner, "avatar-medium") }} + + + {{ doc.user_full_name || frappe.user.full_name(doc.owner) }} +
    + {{ comment_when(doc.creation) }} +
    +
    + {% } %} +
    + + {% if (doc._doc_status && doc._doc_status_indicator) { %} + + + + {% } %} + + {% if (doc._url) { %} + + + + + + {% } %} + +
    +
    + {{ doc.content }} +
    + {% if (doc.attachments && doc.attachments.length) { %} +
    + {% $.each(doc.attachments, function(i, a) { %} + + {% }); %} +
    + {% } %} +
    diff --git a/smart_service/public/js/templates/users_in_sidebar.html b/smart_service/public/js/templates/users_in_sidebar.html new file mode 100644 index 0000000..47dd618 --- /dev/null +++ b/smart_service/public/js/templates/users_in_sidebar.html @@ -0,0 +1,13 @@ +{% for (var i=0, l=users.length; i < l; i++) { + var u = users[i]; +%} + + {% if (u.icon) { %} + + {% } else if(u.image) { %} + {{ u.fullname }} + {% } else { %} +
    {{ u.abbr.substr(0,1) }}
    + {% } %} +
    +{% } %}