diff --git a/frappe/core/doctype/report/report.js b/frappe/core/doctype/report/report.js index 9222c86974..903a9cbac4 100644 --- a/frappe/core/doctype/report/report.js +++ b/frappe/core/doctype/report/report.js @@ -54,7 +54,7 @@ cur_frm.cscript.refresh = function(doc) { frappe.ui.form.on('Report', { refresh: function(frm) { - if(!frappe.boot.developer_mode && user != 'Administrator') { + if(!frappe.boot.developer_mode && frappe.session.user != 'Administrator') { // make the document read-only frm.set_read_only(); } diff --git a/frappe/desk/report_dump.py b/frappe/desk/report_dump.py index 296dc11baf..86b1765814 100644 --- a/frappe/desk/report_dump.py +++ b/frappe/desk/report_dump.py @@ -8,18 +8,16 @@ import json import copy @frappe.whitelist() -def get_data(doctypes, last_modified): +def get_data(doctypes, last_modified): data_map = {} for dump_report_map in frappe.get_hooks().dump_report_map: data_map.update(frappe.get_attr(dump_report_map)) - - import datetime + out = {} - + doctypes = json.loads(doctypes) last_modified = json.loads(last_modified) - - start = datetime.datetime.now() + for d in doctypes: args = copy.deepcopy(data_map[d]) dt = d.find("[") != -1 and d[:d.find("[")] or d @@ -29,7 +27,7 @@ def get_data(doctypes, last_modified): modified_table = "item." else: modified_table = "" - + conditions = order_by = "" table = args.get("from") or ("`tab%s`" % dt) @@ -39,30 +37,30 @@ def get_data(doctypes, last_modified): args['conditions'].append(modified_table + "modified > '" + last_modified[d] + "'") out[dt]["modified_names"] = frappe.db.sql_list("""select %sname from %s where %smodified > %s""" % (modified_table, table, modified_table, "%s"), last_modified[d]) - + if args.get("force_index"): conditions = " force index (%s) " % args["force_index"] if args.get("conditions"): conditions += " where " + " and ".join(args["conditions"]) if args.get("order_by"): order_by = " order by " + args["order_by"] - + out[dt]["data"] = [list(t) for t in frappe.db.sql("""select %s from %s %s %s""" \ % (",".join(args["columns"]), table, conditions, order_by))] - + # last modified modified_table = table if "," in table: modified_table = " ".join(table.split(",")[0].split(" ")[:-1]) - - tmp = frappe.db.sql("""select `modified` + + tmp = frappe.db.sql("""select `modified` from %s order by modified desc limit 1""" % modified_table) out[dt]["last_modified"] = tmp and tmp[0][0] or "" - out[dt]["columns"] = map(lambda c: c.split(" as ")[-1], args["columns"]) - + out[dt]["columns"] = list(map(lambda c: c.split(" as ")[-1], args["columns"])) + if args.get("links"): out[dt]["links"] = args["links"] - + for d in out: unused_links = [] # only compress full dumps (not partial) @@ -70,25 +68,28 @@ def get_data(doctypes, last_modified): for link_key in out[d]["links"]: link = out[d]["links"][link_key] if link[0] in out and (link[0] not in last_modified): - + # make a map of link ids # to index link_map = {} doctype_data = out[link[0]] + col_idx = doctype_data["columns"].index(link[1]) for row_idx in range(len(doctype_data["data"])): row = doctype_data["data"][row_idx] link_map[row[col_idx]] = row_idx - + for row in out[d]["data"]: - col_idx = out[d]["columns"].index(link_key) - # replace by id - if row[col_idx]: - row[col_idx] = link_map.get(row[col_idx]) + columns = list(out[d]["columns"]) + if link_key in columns: + col_idx = columns.index(link_key) + # replace by id + if row[col_idx]: + row[col_idx] = link_map.get(row[col_idx]) else: unused_links.append(link_key) - + for link in unused_links: del out[d]["links"][link] - + return out diff --git a/frappe/public/build.json b/frappe/public/build.json index 4a532ef908..76180f99ef 100755 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -357,7 +357,6 @@ ], "js/report.min.js": [ "public/js/lib/clusterize.min.js", - "public/js/lib/frappe-datatable.js", "public/js/frappe/views/reports/report_factory.js", "public/js/frappe/views/reports/report_view.js", "public/js/frappe/views/reports/reportview_footer.html", diff --git a/frappe/public/css/slickgrid.css b/frappe/public/css/slickgrid.css deleted file mode 100644 index 961da50b55..0000000000 --- a/frappe/public/css/slickgrid.css +++ /dev/null @@ -1,62 +0,0 @@ -.slick-header-column, -.slick-cell { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -.slick-wrapper, -.slick-header { - border: none !important; -} -.slick-headerrow { - border: none; - border-bottom: 1px solid #d1d8dd; -} -.slick-headerrow-column { - background-color: #F7FAFC !important; - text-overflow: clip; -} -.slick-headerrow-column input { - border: 1px solid #d1d8dd; - border-radius: 3px; - font-size: 12px; - padding: 0px 3px !important; - margin: 0; - width: 100%; - min-height: 20px; -} -.slick-cell, -.slick-headerrow-column { - font-size: 12px; - border-color: transparent #d1d8dd #d1d8dd transparent !important; - border-style: solid; - color: inherit !important; - margin-top: -1px; -} -.slick-cell pre { - border: none; - background-color: transparent; - padding: 3px; -} -.slick-header-column, -.slick-header-columns { - font-size: 12px; - font-weight: bold; - background-color: #F7FAFC; - border-color: #d1d8dd !important; - color: #8D99A6 !important; -} -.slick-header-column:hover, -.slick-header-column-active { - background-image: none; - background-color: #d9e7f1; -} -.slick-row.odd .slick-cell { - background-color: #fafbfc; -} -.frappe-rtl .slick-wrapper { - direction: ltr; -} -.slick-cell > span[data-field="_comments"] * { - display: inline-block; -} diff --git a/frappe/public/js/frappe/views/reports/grid_report.js b/frappe/public/js/frappe/views/reports/grid_report.js index df82757f82..a143c0464a 100644 --- a/frappe/public/js/frappe/views/reports/grid_report.js +++ b/frappe/public/js/frappe/views/reports/grid_report.js @@ -1,6 +1,7 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // MIT License. See license.txt +import DataTable from 'frappe-datatable'; frappe.provide("frappe.report_dump"); $.extend(frappe.report_dump, { @@ -34,15 +35,15 @@ $.extend(frappe.report_dump, { } else { row[link_key] = null; } - }) - }) + }); + }); } } }); callback(); } - }) + }); }, set_data: function(doctype, doctype_data) { var data = []; @@ -55,7 +56,7 @@ $.extend(frappe.report_dump, { row.id = row.name; row.doctype = doctype; return row; - } + }; if(frappe.report_dump.last_modified[doctype]) { // partial loading, make a name dict $.each(doctype_data.data, function(i, d) { @@ -79,7 +80,7 @@ $.extend(frappe.report_dump, { // add new records $.each(replace_dict, function(name, d) { data.push(d); - }) + }); } else { // first loading @@ -98,7 +99,6 @@ frappe.views.GridReport = Class.extend({ this.filter_inputs = {}; this.preset_checks = []; this.tree_grid = {show: false}; - var me = this; $.extend(this, opts); this.wrapper = $('
').appendTo(this.page.main); @@ -149,11 +149,9 @@ frappe.views.GridReport = Class.extend({ $.each(me.filter_inputs, function(i, v) { var opts = v.get(0).opts; if(opts.fieldtype == "Select" && in_list(me.doctypes, opts.link)) { - $(v).add_options($.map(frappe.report_dump.data[opts.link], - function(d) { return d.name; })); + $(v).add_options(frappe.report_dump.data[opts.link].map(d => d.name)); } else if(opts.fieldtype == "Link" && in_list(me.doctypes, opts.link)) { - opts.list = $.map(frappe.report_dump.data[opts.link], - function(d) { return d.name; }); + opts.list = frappe.report_dump.data[opts.link].map(d => d.name); me.set_autocomplete(v, opts.list); } }); @@ -184,10 +182,6 @@ frappe.views.GridReport = Class.extend({ this.filter_inputs.range && this.filter_inputs.range.on("change", function() { me.refresh(); }); - - // chart check - if(this.setup_chart_check) - this.setup_chart_check(); }, set_filter: function(key, value) { var filters = this.filter_inputs[key]; @@ -201,7 +195,7 @@ frappe.views.GridReport = Class.extend({ filters.val(value); } } else { - frappe.msgprint(__("Invalid Filter: {0}", [key])) + frappe.msgprint(__("Invalid Filter: {0}", [key])); } }, set_autocomplete: function($filter, list) { @@ -216,7 +210,6 @@ frappe.views.GridReport = Class.extend({ }); }, init_filter_values: function() { - var me = this; $.each(this.filter_inputs, function(key, filter) { var opts = filter.get(0).opts; if(frappe.sys_defaults[key]) { @@ -237,13 +230,13 @@ frappe.views.GridReport = Class.extend({ var values = { from_date: frappe.datetime.str_to_user(frappe.sys_defaults.year_start_date), to_date: frappe.datetime.str_to_user(frappe.sys_defaults.year_end_date) - } + }; var me = this; $.each(values, function(i, v) { if(me.filter_inputs[i] && !me.filter_inputs[i].val()) me.filter_inputs[i].val(v); - }) + }); }, make_filters: function() { @@ -281,7 +274,7 @@ frappe.views.GridReport = Class.extend({ if(e.which==13) { me.refresh(); } - }) + }); } me.filter_inputs[v.fieldname] = input; }); @@ -317,7 +310,7 @@ frappe.views.GridReport = Class.extend({ key = key || "name"; $.each(data, function(i, v) { map[v[key]] = v; - }) + }); return map; }, @@ -350,7 +343,7 @@ frappe.views.GridReport = Class.extend({ this.waiting.toggle(false); if(!this.grid_wrapper) this.make(); - this.show_zero = $('.show-zero input:checked').length; + // this.show_zero = $('.show-zero input:checked').length; this.load_filter_values(); this.setup_columns(); this.setup_dataview_columns(); @@ -364,29 +357,28 @@ frappe.views.GridReport = Class.extend({ this.setup_chart && this.setup_chart(); }, setup_dataview_columns: function() { - this.dataview_columns = $.map(this.columns, function(col) { - return !col.hidden ? col : null; + this.columns = this.columns.filter(col => !col.hidden); + this.datatable_columns = this.columns.map(column => { + return Object.assign(column, { + format: (value, row, column, data) => { + return column.formatter ? + column.formatter(row, {}, value, column, data) : + value || ''; + } + }); }); }, make: function() { - var me = this; - // chart wrapper this.chart_area = $('
').appendTo(this.wrapper); - this.page.add_menu_item(__("Export"), function() { return me.export(); }, true); + this.page.add_menu_item(__("Export"), () => this.export(), true); // grid wrapper - this.grid_wrapper = $("
") + this.grid_wrapper = $("
") .appendTo(this.wrapper); this.id = frappe.dom.set_unique_id(this.grid_wrapper.get(0)); - // zero-value check - $('
\ -
').appendTo(this.wrapper); - this.bind_show(); frappe.cur_grid_report = this; @@ -412,40 +404,33 @@ frappe.views.GridReport = Class.extend({ enableColumnReorder: false }, render: function() { - // new slick grid - this.grid = new Slick.Grid("#"+this.id, this.dataView, this.dataview_columns, this.options); - var me = this; + this.datatable = new DataTable('#' + this.id, { + columns: this.datatable_columns, + data: this.data, + layout: 'fixed', + inlineFilters: true, + treeView: true, + checkboxColumn: true, + checkedRowStatus: false, + events: { + onCheckRow: (row) => { + const rowIndex = row.meta.rowIndex; + const checked = this.datatable.rowmanager.checkMap[rowIndex]; + const data = this.datatable.datamanager.getData(rowIndex); + data.checked = Boolean(checked); - if (!frappe.dom.is_touchscreen()) { - this.grid.setSelectionModel(new Slick.CellSelectionModel()); - this.grid.registerPlugin(new Slick.CellExternalCopyManager({ - dataItemColumnValueExtractor: function(item, columnDef, value) { - return item[columnDef.field]; + this.setup_chart && this.setup_chart(); } - })); - } - - // bind events - this.dataView.onRowsChanged.subscribe(function (e, args) { - me.grid.invalidateRows(args.rows); - me.grid.render(); + } }); - this.dataView.onRowCountChanged.subscribe(function (e, args) { - me.grid.updateRowCount(); - me.grid.render(); + this.data.forEach((d, i) => { + if (d.checked) { + this.datatable.rowmanager.checkRow(i, true); + } }); - - this.tree_grid.show && this.add_tree_grid_events(); }, prepare_data_view: function() { - // initialize the model - this.dataView = new Slick.Data.DataView({ inlineFilters: true }); - this.dataView.beginUpdate(); - this.dataView.setItems(this.data); - if(this.dataview_filter) this.dataView.setFilter(this.dataview_filter); - if(this.tree_grid.show) this.dataView.setFilter(this.tree_dataview_filter); - this.dataView.endUpdate(); }, export: function() { frappe.tools.downloadify(frappe.slickgrid_tools.get_view_data(this.columns, this.dataView), @@ -501,10 +486,11 @@ frappe.views.GridReport = Class.extend({ is_default: function(fieldname) { return this[fieldname]==this[fieldname + "_default"]; }, - date_formatter: function(row, cell, value, columnDef, dataContext) { + date_formatter: function(row, cell, value) { return frappe.datetime.str_to_user(value); }, currency_formatter: function(row, cell, value, columnDef, dataContext) { + if (isNaN(value)) value = ''; return repl('
%(value)s
', { _style: dataContext._style || "", value: ((value==null || value==="") ? "" : format_number(value)) @@ -513,7 +499,7 @@ frappe.views.GridReport = Class.extend({ text_formatter: function(row, cell, value, columnDef, dataContext) { return repl('%(value)s', { _style: dataContext._style || "", - esc_value: cstr(value).replace(/"/g, '\"'), + esc_value: cstr(value).replace(/"/g, '\\"'), value: cstr(value) }); }, @@ -522,11 +508,10 @@ frappe.views.GridReport = Class.extend({ class="chart-check" %(checked)s>', { "id": dataContext.id, "checked": dataContext.checked ? 'checked="checked"' : "" - }) + }); }, apply_link_formatters: function() { - var me = this; - $.each(this.dataview_columns, function(i, col) { + $.each(this.columns, function(i, col) { if(col.link_formatter) { col.formatter = function(row, cell, value, columnDef, dataContext, for_print) { // added link and open button to links @@ -548,9 +533,10 @@ frappe.views.GridReport = Class.extend({ } // make link to add a filter + var html; var link_formatter = me.dataview_columns[cell].link_formatter; if (link_formatter.filter_input) { - var html = repl('\ %(value)s', { @@ -559,7 +545,7 @@ frappe.views.GridReport = Class.extend({ page_name: frappe.container.page.page_name }); } else { - var html = value; + html = value; } // make icon to open form @@ -570,9 +556,9 @@ frappe.views.GridReport = Class.extend({ html += me.get_link_open_icon(doctype, value); } return html; - } + }; } - }) + }); }, get_link_open_icon: function(doctype, name) { return repl(' \ @@ -601,13 +587,13 @@ frappe.views.GridReport = Class.extend({ formatter: me.currency_formatter, width: 100 }); - } + }; var build_columns = function(condition) { // add column for each date range for(var i=0; i <= date_diff; i++) { var date = frappe.datetime.add_days(me.from_date, i); - if(!condition) condition = function() { return true; } + if(!condition) condition = () => true; if(condition(date)) add_column(date); me.last_date = date; @@ -616,7 +602,7 @@ frappe.views.GridReport = Class.extend({ me.column_map[date] = me.columns[me.columns.length-1]; } } - } + }; // make columns for all date ranges if(range=='Daily') { @@ -624,17 +610,17 @@ frappe.views.GridReport = Class.extend({ } else if(range=='Weekly') { build_columns(function(date) { if(!me.last_date) return true; - return !(frappe.datetime.get_diff(date, me.from_date) % 7) + return !(frappe.datetime.get_diff(date, me.from_date) % 7); }); } else if(range=='Monthly') { build_columns(function(date) { if(!me.last_date) return true; - return frappe.datetime.str_to_obj(me.last_date).getMonth() != frappe.datetime.str_to_obj(date).getMonth() + return frappe.datetime.str_to_obj(me.last_date).getMonth() != frappe.datetime.str_to_obj(date).getMonth(); }); } else if(range=='Quarterly') { build_columns(function(date) { if(!me.last_date) return true; - return frappe.datetime.str_to_obj(date).getDate()==1 && in_list([0,3,6,9], frappe.datetime.str_to_obj(date).getMonth()) + return frappe.datetime.str_to_obj(date).getDate()==1 && in_list([0,3,6,9], frappe.datetime.str_to_obj(date).getMonth()); }); } else if(range=='Yearly') { build_columns(function(date) { @@ -665,7 +651,6 @@ frappe.views.GridReport = Class.extend({ frappe.views.GridReportWithPlot = frappe.views.GridReport.extend({ setup_chart: function() { - var me = this; if (in_list(["Daily", "Weekly"], this.filter_inputs.range.val())) { this.chart_area.toggle(false); return; @@ -674,33 +659,14 @@ frappe.views.GridReportWithPlot = frappe.views.GridReport.extend({ } var chart_data = this.get_chart_data ? this.get_chart_data() : null; - this.chart = new Chart(".chart", { + const parent = this.wrapper.find('.chart')[0]; + this.chart = new Chart(parent, { height: 200, data: chart_data, type: 'line' }); }, - setup_chart_check: function() { - var me = this; - me.wrapper.bind('make', function() { - me.wrapper.on("click", ".chart-check", function() { - var checked = $(this).prop("checked"); - var id = $(this).attr("data-id"); - if(me.item_by_name) { - if(me.item_by_name[id]) { - me.item_by_name[id].checked = checked ? true : false; - } - } else { - $.each(me.data, function(i, d) { - if(d.id==id) d.checked = checked; - }); - } - me.setup_chart(); - }); - }); - }, - get_chart_data: function() { var me = this; var plottable_cols = []; @@ -752,7 +718,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({ }, add_tree_grid_events: function() { var me = this; - this.grid.onClick.subscribe(function (e, args) { + this.grid.onClick.subscribe(function(e, args) { if ($(e.target).hasClass("toggle")) { var item = me.dataView.getItem(args.row); if (item) { @@ -768,7 +734,7 @@ frappe.views.TreeGridReport = frappe.views.GridReportWithPlot.extend({ } }); }, - tree_formatter: function (row, cell, value, columnDef, dataContext) { + tree_formatter: function(row, cell, value, columnDef, dataContext) { var me = frappe.cur_grid_report; var data = me.data; var spacer = "").appendTo(this.page.main); - $('\ - \ -
\ - ').appendTo(this.wrapper); - this.wrapper.find(".expand-all").on("click", function() { me.toggle_all(false);}); - this.wrapper.find(".collapse-all").on("click", function() { me.toggle_all(true);}); - this.chart_area = this.wrapper.find(".chart-area"); - this.make_toolbar(); - }, - toggle_expand_collapse_buttons: function(show) { - this.wrapper.find(".expand-all, .collapse-all").toggleClass('hidden', !!!show); - }, - make_toolbar: function() { - var me = this; - this.page.set_secondary_action(__('Refresh'), function() { me.refresh(); }); - - // Edit - this.page.add_menu_item(__('Edit'), function() { - if(!frappe.user.is_report_manager()) { - frappe.msgprint(__("You are not allowed to create / edit reports")); - return false; - } - frappe.set_route("Form", "Report", me.report_name); - }, true); - - this.page.add_menu_item(__("Print"), function() { - frappe.ui.get_print_settings(false, function(print_settings) { - me.print_settings = print_settings; - me.print_report(); - }, me.report_doc.letter_head); - }, true); - - this.page.add_menu_item(__("PDF"), function() { - frappe.ui.get_print_settings(true, function(print_settings) { - me.print_settings = print_settings; - me.pdf_report(); - }, me.report_doc.letter_head); - }, true); - - this.page.add_menu_item(__('Export'), function() { - me.make_export(); - }, true); - - this.page.add_menu_item(__("Setup Auto Email"), function() { - frappe.set_route('List', 'Auto Email Report', {'report' : me.report_name}); - }, true); - - if(frappe.model.can_set_user_permissions("Report")) { - this.page.add_menu_item(__("User Permissions"), function() { - frappe.route_options = { - doctype: "Report", - name: me.report_name - }; - frappe.set_route('List', 'User Permission'); - }, true); - } - - // add to desktop - this.page.add_menu_item(__("Add to Desktop"), function() { - frappe.add_to_desktop(me.report_name, null, me.report_name); - }, true); - }, - load: function() { - // load from route - var route = frappe.get_route(); - var me = this; - if(route[1]) { - if((me.report_name!=route[1]) || frappe.route_options) { - me.report_name = route[1]; - this.wrapper.find(".no-report-area").toggle(false); - me.page.set_title(__(me.report_name)); - - frappe.model.with_doc("Report", me.report_name, function() { - - me.report_doc = frappe.get_doc("Report", me.report_name); - - frappe.model.with_doctype(me.report_doc.ref_doctype, function() { - var module = locals.DocType[me.report_doc.ref_doctype].module; - frappe.breadcrumbs.add(module) - - if(!frappe.query_reports[me.report_name]) { - return frappe.call({ - method:"frappe.desk.query_report.get_script", - args: { - report_name: me.report_name - }, - callback: function(r) { - frappe.dom.eval(r.message.script || ""); - - frappe.after_ajax(function() { - var report_settings = frappe.query_reports[me.report_name]; - me.html_format = r.message.html_format; - report_settings["html_format"] = r.message.html_format; - - me.setup_report(); - }); - } - }); - } else { - me.setup_report(); - } - }); - }); - } - } else { - var msg = __("No Report Loaded. Please use query-report/[Report Name] to run a report.") - this.wrapper.find(".no-report-area").html(msg).toggle(true); - } - }, - setup_report: function() { - var me = this; - this.page.set_title(__(this.report_name)); - this.page.clear_inner_toolbar(); - this.setup_filters(); - this.chart_area.toggle(false); - this.toggle_expand_collapse_buttons(false); - this.is_tree_report = false; - - var report_settings = frappe.query_reports[this.report_name]; - - $.when(function() { - if (report_settings.onload) { - return report_settings.onload(me); - } - - }()).then(function() { - me.refresh(); - }); - }, - print_report: function() { - if(!frappe.model.can_print(this.report_doc.ref_doctype)) { - frappe.msgprint(__("You are not allowed to print this report")); - return false; - } - if(this.html_format) { - var content = frappe.render(this.html_format, { - data: frappe.slickgrid_tools.get_filtered_items(this.dataView), - filters: this.get_values(), - report: this, - data_to_be_printed: this.data_to_be_printed - }); - - frappe.render_grid({ - content: content, - title: __(this.report_name), - print_settings: this.print_settings, - columns: this.columns - }); - } else { - frappe.render_grid({ - grid: this.grid, - report: this, - title: __(this.report_name), - print_settings: this.print_settings, - }); - } - }, - pdf_report: function() { - var me = this; - var base_url = frappe.urllib.get_base_url(); - var print_css = frappe.boot.print_css; - - if(!frappe.model.can_print(this.report_doc.ref_doctype)) { - frappe.msgprint(__("You are not allowed to make PDF for this report")); - return false; - } - - var orientation = this.print_settings.orientation; - var landscape = orientation == "Landscape" ? true: false - var columns = this.grid.getColumns(); - if(this.html_format) { - var content = frappe.render(this.html_format, { - data: frappe.slickgrid_tools.get_filtered_items(this.dataView), - filters:this.get_values(), - report:this, - data_to_be_printed: this.data_to_be_printed - }); - - //Render Report in HTML - var html = frappe.render_template("print_template", { - content:content, - title:__(this.report_name), - base_url: base_url, - print_css: print_css, - print_settings: this.print_settings, - landscape: landscape, - columns: columns - }); - } else { - // rows filtered by inline_filter of slickgrid - var visible_idx = frappe.slickgrid_tools - .get_view_data(this.columns, this.dataView) - .map(row => row[0]).filter(idx => idx !== 'Sr No'); - - var data = this.grid.getData().getItems(); - data = data.filter(d => visible_idx.includes(d._id)); - - var content = frappe.render_template("print_grid", { - columns:columns, - data:data, - title:__(this.report_name) - }) - - //Render Report in HTML - var html = frappe.render_template("print_template",{ - content:content, - title:__(this.report_name), - base_url: base_url, - print_css: print_css, - print_settings: this.print_settings, - landscape: landscape, - columns: columns - }); - } - - var orientation = this.print_settings.orientation; - this.open_pdf_report(html, orientation) - }, - open_pdf_report: function(html, orientation) { - //Create a form to place the HTML content - var formData = new FormData(); - - //Push the HTML content into an element - formData.append("html", html); - formData.append("orientation", orientation); - var blob = new Blob([], { type: "text/xml"}); - //formData.append("webmasterfile", blob); - formData.append("blob", blob); - - var xhr = new XMLHttpRequest(); - xhr.open("POST", '/api/method/frappe.utils.print_format.report_to_pdf'); - xhr.setRequestHeader("X-Frappe-CSRF-Token", frappe.csrf_token); - xhr.responseType = "arraybuffer"; - - xhr.onload = function(success) { - if (this.status === 200) { - var blob = new Blob([success.currentTarget.response], {type: "application/pdf"}); - var objectUrl = URL.createObjectURL(blob); - - //Open report in a new window - window.open(objectUrl); - } - }; - xhr.send(formData); - }, - setup_filters: function() { - if(this.setting_filters) return; - - this.clear_filters(); - var me = this; - $.each(frappe.query_reports[this.report_name].filters || [], function(i, df) { - if(df.fieldtype==="Break") { - me.page.add_break(); - } else { - var f = me.page.add_field(df); - $(f.wrapper).addClass("filters pull-left"); - me.filters.push(f); - - if(df["default"]) { - f.set_input(df["default"]); - } - if(df.fieldtype=="Check") { - $(f.wrapper).find("input[type='checkbox']"); - } - - if(df.get_query) f.get_query = df.get_query; - if(df.on_change) f.on_change = df.on_change; - df.onchange = () => { - if(!me.flags.filters_set) { - // don't trigger change while setting filters - return; - } - if (f.on_change) { - f.on_change(me); - } else { - me.trigger_refresh(); - } - } - } - }); - - // hide page form if no filters - var $filters = $(this.parent).find('.page-form .filters'); - $(this.parent).find('.page-form').toggle($filters.length ? true : false); - - // set the field 'query_report_filters_by_name' first as they can be used in - // setting/triggering the filters - this.set_filters_by_name(); - - this.setting_filters = true; - this.set_route_filters(); - this.setting_filters = false; - - this.flags.filters_set = true; - }, - clear_filters: function() { - this.filters = []; - $(this.parent).find('.page-form .filters').remove(); - }, - set_filters_by_name: function() { - frappe.query_report_filters_by_name = {}; - for(var i in this.filters) { - frappe.query_report_filters_by_name[this.filters[i].df.fieldname] = this.filters[i]; - } - }, - set_route_filters: function() { - var me = this; - if(frappe.route_options) { - const fields = Object.keys(frappe.route_options); - const filters_to_set = this.filters.filter(f => fields.includes(f.df.fieldname)); - - const promises = filters_to_set.map(f => { - return () => { - const value = frappe.route_options[f.df.fieldname]; - return f.set_value(value); - } - }); - promises.push(() => { - frappe.route_options = null; - }); - - return frappe.run_serially(promises); - } - - }, - refresh: function() { - // throttle - // stop refresh from being called multiple times (from triggers ?) - if (!this.request_refresh) { - this.request_refresh = setTimeout(() => { - this._refresh(); - this.request_refresh = null; - }, 300); - } - }, - _refresh: function() { - // Run - var me = this; - - this.wrapper.find(".results").toggle(false); - try { - var filters = this.get_values(true); - } catch(e) { - // don't run report - return; - } - - this.waiting = frappe.messages.waiting(this.wrapper.find(".waiting-area").empty().toggle(true), - __("Loading Report") + "..."); - this.wrapper.find(".no-report-area").toggle(false); - - if (this.report_ajax) { - // abort previous request - this.report_ajax.abort(); - } - - this.chart_area.toggle(false); - - this.report_ajax = frappe.call({ - method: "frappe.desk.query_report.run", - type: "GET", - args: { - "report_name": me.report_name, - filters: filters - }, - callback: function(r) { - me.report_ajax = undefined; - me.make_results(r.message); - } - }); - - return this.report_ajax; - }, - trigger_refresh: function() { - var me = this; - var filters = me.get_values(); - - // check if required filters are not missing - var missing = false; - $.each(me.filters, function(k, _f) { - if (_f.df.reqd && !filters[_f.df.fieldname]) { - missing = true; - return; - } - }); - - if (!missing) { - me.refresh(); - } - }, - get_values: function(raise) { - var filters = {}; - var mandatory_fields = []; - $.each(this.filters || [], function(i, f) { - var v = f.get_value(); - // TODO: hidden fields dont have $input - if(f.df.hidden) v = f.value; - if(v === '%') v = null; - if(f.df.reqd && !v) mandatory_fields.push(f.df.label); - if(v) filters[f.df.fieldname] = v; - }) - if(raise && mandatory_fields.length) { - this.chart_area.hide(); - this.wrapper.find(".waiting-area").empty().toggle(false); - this.wrapper.find(".no-report-area").html(__("Please set filters")).toggle(true); - if(raise) { - console.log('filter missing: ' + mandatory_fields); - throw "Filters required"; - } - } - - return filters; - }, - make_results: function(res) { - this.wrapper.find(".waiting-area, .no-report-area").empty().toggle(false); - this.wrapper.find(".results").toggle(true); - this.make_columns(res.columns); - this.make_data(res.result, res.columns); - this.filter_hidden_columns(); - this.render(res); - }, - render: function(res) { - this.columnFilters = {}; - this.make_dataview(); - this.id = frappe.dom.set_unique_id(this.wrapper.find(".result-area").addClass("slick-wrapper").get(0)); - - this.grid = new Slick.Grid("#"+this.id, this.dataView, this.columns, - this.slickgrid_options); - - if (!frappe.dom.is_touchscreen()) { - this.grid.setSelectionModel(new Slick.CellSelectionModel()); - this.grid.registerPlugin(new Slick.CellExternalCopyManager({ - dataItemColumnValueExtractor: function(item, columnDef, value) { - return item[columnDef.field]; - } - })); - } - - this.setup_header_row(); - this.grid.init(); - this.setup_sort(); - - // further setup of grid like click subscription for tree - if (this.get_query_report_opts().tree) { - this.setup_tree(); - } - - this.set_message(res.message); - this.setup_chart(res); - this.set_print_data(res.data_to_be_printed); - - this.toggle_expand_collapse_buttons(this.is_tree_report); - }, - - make_columns: function(columns) { - var me = this; - var formatter = this.get_formatter(); - - this.columns = [{id: "_id", field: "_id", name: __("Sr No"), width: 60}] - .concat($.map(columns, function(c, i) { - if ($.isPlainObject(c)) { - var df = c; - } else if (c.indexOf(":")!==-1) { - var opts = c.split(":"); - var df = { - label: opts.length<=2 ? opts[0] : opts.slice(0, opts.length - 2).join(":"), - fieldtype: opts.length<=2 ? opts[1] : opts[opts.length - 2], - width: opts.length<=2 ? opts[2] : opts[opts.length - 1] - }; - if (df.fieldtype.indexOf("/")!==-1) { - var tmp = df.fieldtype.split("/"); - df.fieldtype = tmp[0]; - df.options = tmp[1]; - } - df.width = cint(df.width); - } else { - var df = { - label: c, - fieldtype: "Data" - }; - } - - if (!df.fieldtype) df.fieldtype = "Data"; - if (!cint(df.width)) df.width = 80; - - var col = $.extend({}, df, { - label: df.label || (df.fieldname && __(toTitle(df.fieldname.replace(/_/g, " ")))) || "", - sortable: true, - df: df, - formatter: formatter - }); - - col.field = df.fieldname || df.label; - df.label = __(df.label); - col.name = col.id = col.label = df.label; - - if(df.width < 0) { - col.hidden = true; - } - - return col - })); - }, - filter_hidden_columns: function() { - this.columns = $.map(this.columns, function(c, i) { - return (c.hidden==1 ? null : c); - }); - }, - get_query_report_opts: function() { - return frappe.query_reports[this.report_name] || {}; - }, - get_formatter: function() { - var formatter = function(row, cell, value, columnDef, dataContext, for_print) { - var value = frappe.format(value, columnDef.df, {for_print: for_print, always_show_decimals: true}, dataContext); - - if (columnDef.df.is_tree) { - value = frappe.query_report.tree_formatter(row, cell, value, columnDef, dataContext); - } - - return value; - }; - - var query_report_opts = this.get_query_report_opts(); - if (query_report_opts.formatter) { - var default_formatter = formatter; - - // custom formatter - formatter = function(row, cell, value, columnDef, dataContext) { - return query_report_opts.formatter(row, cell, value, columnDef, dataContext, default_formatter); - } - } - - return formatter; - }, - make_data: function(result, columns) { - var me = this; - this.data = []; - for(var row_idx=0, l=result.length; row_idx < l; row_idx++) { - var row = result[row_idx]; - if ($.isPlainObject(row)) { - var newrow = row; - } else { - var newrow = {}; - for(var i=1, j=this.columns.length; i number_fields.includes(col.fieldtype)) - .map(col => col.field); - - // reset numeric fields - let updated_totals = Object.assign({}, this.dataView.getItemById(this.total_row_id)); - fields.map(field => { - updated_totals[field] = 0.0; - }); - - const data_length = this.dataView.getLength(); - // loop all the rows except the last Total row - for (let i = 0; i < data_length - 1; i++) { - const item = this.dataView.getItem(i); - fields.map(field => { - updated_totals[field] += item[field]; - }); - } - this.dataView.updateItem(updated_totals.id, updated_totals); - }, - inline_filter: function (item) { - var me = frappe.container.page.query_report; - if(me.report_doc.add_total_row) { - // always show totals row - if(item.id === me.total_row_id) return true; - } - for (var columnId in me.columnFilters) { - if (columnId !== undefined && me.columnFilters[columnId] !== "") { - var c = me.grid.getColumns()[me.grid.getColumnIndex(columnId)]; - if (!me.compare_values(item[c.field], me.columnFilters[columnId], - me.columns[me.grid.getColumnIndex(columnId)])) { - return false; - } - } - } - return true; - }, - setup_item_by_name: function() { - this.item_by_name = {}; - this.name_field = this.get_query_report_opts().name_field; - this.parent_field = this.get_query_report_opts().parent_field; - var initial_depth = this.get_query_report_opts().initial_depth; - for (var i=0, l=this.data.length; i=(initial_depth - 1)) { - item._collapsed = true; - } - } - - }, - toggle_all: function(collapse) { - var me = this; - for(var i=0, l=this.data.length; i
") - .css("padding-left", (cint(dataContext.indent) * 21) + "px") - .html(value); - - var idx = me.dataView.getIdxById(dataContext.id); - var show_toggle = me.data[idx + 1] && (me.data[idx + 1].indent > me.data[idx].indent) - - if (dataContext[me.name_field] && show_toggle) { - $('') - .addClass(dataContext._collapsed ? "expand" : "collapse") - .css("margin-right", "7px") - .prependTo($span); - } - - return $span.wrap("

").parent().html(); - }, - compare_values: function(value, filter, columnDef) { - var invert = false; - - // check if invert - if(filter[0]=="!") { - invert = true; - filter = filter.substr(1); - } - - var out = false; - var cond = "==" - - // parse condition - if(filter[0]==">") { - filter = filter.substr(1); - cond = ">" - } else if(filter[0]=="<") { - filter = filter.substr(1); - cond = "<" - } - - if(in_list(['Float', 'Currency', 'Int', 'Date'], columnDef.df.fieldtype)) { - // non strings - if(filter.indexOf(":")==-1) { - if(columnDef.df.fieldtype=="Date") { - filter = frappe.datetime.user_to_str(filter); - } - - if(in_list(["Float", "Currency", "Int"], columnDef.df.fieldtype)) { - value = flt(value); - filter = flt(filter); - } - - out = eval("value" + cond + "filter"); - } else { - // range - filter = filter.split(":"); - if(columnDef.df.fieldtype=="Date") { - filter[0] = frappe.datetime.user_to_str(filter[0]); - filter[1] = frappe.datetime.user_to_str(filter[1]); - } - - if(in_list(["Float", "Currency", "Int"], columnDef.df.fieldtype)) { - value = flt(value); - filter[0] = flt(filter[0]); - filter[1] = flt(filter[1]); - } - - out = value >= filter[0] && value <= filter[1]; - } - } else { - // string - value = value + ""; - value = value.toLowerCase(); - filter = filter.toLowerCase(); - out = value.indexOf(filter) != -1; - } - - if(invert) - return !out; - else - return out; - }, - setup_header_row: function() { - var me = this; - - $(this.grid.getHeaderRow()).delegate(":input", "change keyup", function (e) { - var columnId = $(this).data("columnId"); - if (columnId != null) { - me.columnFilters[columnId] = $.trim($(this).val()); - me.dataView.refresh(); - } - }); - - this.grid.onHeaderRowCellRendered.subscribe(function(e, args) { - $(args.node).empty(); - $("") - .data("columnId", args.column.id) - .val(me.columnFilters[args.column.id]) - .appendTo(args.node); - }); - }, - setup_sort: function() { - var me = this; - this.grid.onSort.subscribe(function (e, args) { - var cols = args.sortCols; - - me.data.sort(function (dataRow1, dataRow2) { - // Totals row should always be last - if(me.report_doc.add_total_row) { - if(dataRow1.id === me.total_row_id) { - return 1; - } - if(dataRow2.id === me.total_row_id) { - return -1; - } - } - - for (var i = 0, l = cols.length; i < l; i++) { - var field = cols[i].sortCol.field; - var sign = cols[i].sortAsc ? 1 : -1; - var value1 = dataRow1[field], value2 = dataRow2[field]; - var result = (value1 == value2 ? 0 : (value1 > value2 ? 1 : -1)) * sign; - if (result != 0) { - return result; - } - } - return 0; - }); - me.dataView.beginUpdate(); - me.dataView.setItems(me.data); - me.dataView.endUpdate(); - me.dataView.refresh(); - }); - }, - setup_tree: function() { - // set these in frappe.query_reports[report_name] - // "tree": true, - // "name_field": "account", - // "parent_field": "parent_account", - // "initial_depth": 3 - - // also set "is_tree" true for ColumnDef - - var me = this; - this.grid.onClick.subscribe(function (e, args) { - if ($(e.target).hasClass("toggle")) { - var item = me.dataView.getItem(args.row); - if (item) { - if (!item._collapsed) { - item._collapsed = true; - } else { - item._collapsed = false; - } - - me.dataView.updateItem(item.id, item); - } - e.stopImmediatePropagation(); - } - }); - }, - - make_export: function() { - - var me = this; - this.title = this.report_name; - - if(!frappe.model.can_export(this.report_doc.ref_doctype)) { - frappe.msgprint(__("You are not allowed to export this report")); - return false; - } - - frappe.prompt({fieldtype:"Select", label: __("Select File Type"), fieldname:"file_format_type", - options:"Excel\nCSV", default:"Excel", reqd: 1}, - function(data) { - var view_data = frappe.slickgrid_tools.get_view_data(me.columns, me.dataView); - var result = view_data.map(row => row.splice(1)); - - // to download only visible rows - var visible_idx = view_data.map(row => row[0]).filter(sr_no => sr_no !== 'Sr No'); - - if (data.file_format_type == "CSV") { - frappe.tools.downloadify(result, null, me.title); - } - - else if (data.file_format_type == "Excel") { - try { - var filters = me.get_values(true); - } catch(e) { - return; - } - var args = { - cmd: 'frappe.desk.query_report.export_query', - report_name: me.report_name, - file_format_type: data.file_format_type, - filters: filters, - visible_idx: visible_idx, - } - - open_url_post(frappe.request.url, args); - } - }, __("Export Report: "+ me.title), __("Download")); - - return false; - }, - - set_message: function(msg) { - if(msg) { - this.wrapper.find(".help-msg").html(msg).toggle(true); - } else { - this.wrapper.find(".help-msg").empty().toggle(false); - } - }, - - setup_chart: function(res) { - this.chart_area.toggle(false); - - if (this.get_query_report_opts().get_chart_data) { - var opts = this.get_query_report_opts().get_chart_data(res.columns, res.result); - } else if (res.chart) { - var opts = res.chart; - } else { - return; - } - - $.extend(opts, { - height: 200 - }); - - if(opts.data && opts.data.labels && opts.data.labels.length) { - this.chart_area.toggle(true); - this.chart = new Chart(".chart-area", opts); - } - }, - - set_print_data: function(data_to_be_printed) { - this.data_to_be_printed = data_to_be_printed; - } -}) diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index b1c2936389..c4a8138201 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -147,9 +147,6 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { this.datatable = new DataTable(this.$datatable_wrapper[0], { columns: this.columns, data: this.get_data(values), - enableClusterize: true, - addCheckbox: this.can_delete, - takeAvailableSpace: true, getEditor: this.get_editing_object.bind(this), events: { onRemoveColumn: (column) => { diff --git a/frappe/public/js/lib/slickgrid/images/actions.gif b/frappe/public/js/lib/slickgrid/images/actions.gif deleted file mode 100644 index 026dd108ed..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/actions.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/ajax-loader-small.gif b/frappe/public/js/lib/slickgrid/images/ajax-loader-small.gif deleted file mode 100644 index 5b33f7e54f..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/ajax-loader-small.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/arrow_redo.png b/frappe/public/js/lib/slickgrid/images/arrow_redo.png deleted file mode 100644 index 4f7f55d6f2..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/arrow_redo.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/arrow_right_peppermint.png b/frappe/public/js/lib/slickgrid/images/arrow_right_peppermint.png deleted file mode 100644 index 8722567866..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/arrow_right_peppermint.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/arrow_right_spearmint.png b/frappe/public/js/lib/slickgrid/images/arrow_right_spearmint.png deleted file mode 100644 index 277ddde384..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/arrow_right_spearmint.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/arrow_undo.png b/frappe/public/js/lib/slickgrid/images/arrow_undo.png deleted file mode 100644 index bc9924ac07..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/arrow_undo.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/bullet_blue.png b/frappe/public/js/lib/slickgrid/images/bullet_blue.png deleted file mode 100644 index 79d978c36a..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/bullet_blue.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/bullet_star.png b/frappe/public/js/lib/slickgrid/images/bullet_star.png deleted file mode 100644 index 142ea482a5..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/bullet_star.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/bullet_toggle_minus.png b/frappe/public/js/lib/slickgrid/images/bullet_toggle_minus.png deleted file mode 100644 index f5aa0450d4..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/bullet_toggle_minus.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/bullet_toggle_plus.png b/frappe/public/js/lib/slickgrid/images/bullet_toggle_plus.png deleted file mode 100644 index a965053423..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/bullet_toggle_plus.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/calendar.gif b/frappe/public/js/lib/slickgrid/images/calendar.gif deleted file mode 100644 index 90fd2e17fe..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/calendar.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/collapse.gif b/frappe/public/js/lib/slickgrid/images/collapse.gif deleted file mode 100644 index 01e691450c..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/collapse.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/comment_yellow.gif b/frappe/public/js/lib/slickgrid/images/comment_yellow.gif deleted file mode 100644 index df7158a477..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/comment_yellow.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/down.gif b/frappe/public/js/lib/slickgrid/images/down.gif deleted file mode 100644 index 9bd9447552..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/down.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/drag-handle.png b/frappe/public/js/lib/slickgrid/images/drag-handle.png deleted file mode 100644 index ad7531cf04..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/drag-handle.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/editor-helper-bg.gif b/frappe/public/js/lib/slickgrid/images/editor-helper-bg.gif deleted file mode 100644 index 2daa973bc5..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/editor-helper-bg.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/expand.gif b/frappe/public/js/lib/slickgrid/images/expand.gif deleted file mode 100644 index 1b24ef1248..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/expand.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/header-bg.gif b/frappe/public/js/lib/slickgrid/images/header-bg.gif deleted file mode 100644 index fe7dd1c1eb..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/header-bg.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/header-columns-bg.gif b/frappe/public/js/lib/slickgrid/images/header-columns-bg.gif deleted file mode 100644 index 8d459a304e..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/header-columns-bg.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/header-columns-over-bg.gif b/frappe/public/js/lib/slickgrid/images/header-columns-over-bg.gif deleted file mode 100644 index f9c07af134..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/header-columns-over-bg.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/help.png b/frappe/public/js/lib/slickgrid/images/help.png deleted file mode 100644 index 85eca0950f..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/help.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/info.gif b/frappe/public/js/lib/slickgrid/images/info.gif deleted file mode 100644 index 5769434fb2..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/info.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/listview.gif b/frappe/public/js/lib/slickgrid/images/listview.gif deleted file mode 100644 index 3ec25ca719..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/listview.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/pencil.gif b/frappe/public/js/lib/slickgrid/images/pencil.gif deleted file mode 100644 index 29f78f433d..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/pencil.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/row-over-bg.gif b/frappe/public/js/lib/slickgrid/images/row-over-bg.gif deleted file mode 100644 index b288e38739..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/row-over-bg.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/sort-asc.gif b/frappe/public/js/lib/slickgrid/images/sort-asc.gif deleted file mode 100644 index 67a2a4c669..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/sort-asc.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/sort-asc.png b/frappe/public/js/lib/slickgrid/images/sort-asc.png deleted file mode 100644 index 8604ff4e07..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/sort-asc.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/sort-desc.gif b/frappe/public/js/lib/slickgrid/images/sort-desc.gif deleted file mode 100644 index 34db47c3b1..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/sort-desc.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/sort-desc.png b/frappe/public/js/lib/slickgrid/images/sort-desc.png deleted file mode 100644 index a2a6adf936..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/sort-desc.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/stripes.png b/frappe/public/js/lib/slickgrid/images/stripes.png deleted file mode 100644 index c3c4b28a80..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/stripes.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/tag_red.png b/frappe/public/js/lib/slickgrid/images/tag_red.png deleted file mode 100644 index d290fcd791..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/tag_red.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/tick.png b/frappe/public/js/lib/slickgrid/images/tick.png deleted file mode 100644 index 3899d71dfa..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/tick.png and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/user_identity.gif b/frappe/public/js/lib/slickgrid/images/user_identity.gif deleted file mode 100644 index 095831ba42..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/user_identity.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/images/user_identity_plus.gif b/frappe/public/js/lib/slickgrid/images/user_identity_plus.gif deleted file mode 100644 index b276a81c78..0000000000 Binary files a/frappe/public/js/lib/slickgrid/images/user_identity_plus.gif and /dev/null differ diff --git a/frappe/public/js/lib/slickgrid/jquery.event.drag.js b/frappe/public/js/lib/slickgrid/jquery.event.drag.js deleted file mode 100755 index f7d6245838..0000000000 --- a/frappe/public/js/lib/slickgrid/jquery.event.drag.js +++ /dev/null @@ -1,408 +0,0 @@ -/*! - * jquery.event.drag - v 2.2 - * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com - * Open Source MIT License - http://threedubmedia.com/code/license - */ -// Created: 2008-06-04 -// Updated: 2012-05-21 -// REQUIRES: jquery 1.7.x - -;(function( $ ){ - -// add the jquery instance method -$.fn.drag = function( str, arg, opts ){ - // figure out the event type - var type = typeof str == "string" ? str : "", - // figure out the event handler... - fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null; - // fix the event type - if ( type.indexOf("drag") !== 0 ) - type = "drag"+ type; - // were options passed - opts = ( str == fn ? arg : opts ) || {}; - // trigger or bind event handler - return fn ? this.bind( type, opts, fn ) : this.trigger( type ); -}; - -// local refs (increase compression) -var $event = $.event, -$special = $event.special, -// configure the drag special event -drag = $special.drag = { - - // these are the default settings - defaults: { - which: 1, // mouse button pressed to start drag sequence - distance: 0, // distance dragged before dragstart - not: ':input', // selector to suppress dragging on target elements - handle: null, // selector to match handle target elements - relative: false, // true to use "position", false to use "offset" - drop: true, // false to suppress drop events, true or selector to allow - click: false // false to suppress click events after dragend (no proxy) - }, - - // the key name for stored drag data - datakey: "dragdata", - - // prevent bubbling for better performance - noBubble: true, - - // count bound related events - add: function( obj ){ - // read the interaction data - var data = $.data( this, drag.datakey ), - // read any passed options - opts = obj.data || {}; - // count another realted event - data.related += 1; - // extend data options bound with this event - // don't iterate "opts" in case it is a node - $.each( drag.defaults, function( key, def ){ - if ( opts[ key ] !== undefined ) - data[ key ] = opts[ key ]; - }); - }, - - // forget unbound related events - remove: function(){ - $.data( this, drag.datakey ).related -= 1; - }, - - // configure interaction, capture settings - setup: function(){ - - // check for related events - if ( $.data( this, drag.datakey ) ) - return; - // initialize the drag data with copied defaults - var data = $.extend({ related:0 }, drag.defaults ); - // store the interaction data - $.data( this, drag.datakey, data ); - // bind the mousedown event, which starts drag interactions - - // don't attached drag event via special for fullcalendar - // return false to attach the normal way - if(this===document) return false; - - $event.add( this, "touchstart mousedown", drag.init, data ); - // prevent image dragging in IE... - if ( this.attachEvent ) - this.attachEvent("ondragstart", drag.dontstart ); - }, - - // destroy configured interaction - teardown: function(){ - var data = $.data( this, drag.datakey ) || {}; - // check for related events - if ( data.related ) - return; - // remove the stored data - $.removeData( this, drag.datakey ); - // remove the mousedown event - $event.remove( this, "touchstart mousedown", drag.init ); - // enable text selection - drag.textselect( true ); - // un-prevent image dragging in IE... - if ( this.detachEvent ) - this.detachEvent("ondragstart", drag.dontstart ); - }, - - // initialize the interaction - init: function( event ){ - // sorry, only one touch at a time - if ( drag.touched ) - return; - // the drag/drop interaction data - var dd = event.data, results; - // check the which directive - if ( event.which != 0 && dd.which > 0 && event.which != dd.which ) - return; - // check for suppressed selector - if ( $( event.target ).is( dd.not ) ) - return; - // check for handle selector - if ( dd.handle && !$( event.target ).closest( dd.handle, event.currentTarget ).length ) - return; - - drag.touched = event.type == 'touchstart' ? this : null; - dd.propagates = 1; - dd.mousedown = this; - dd.interactions = [ drag.interaction( this, dd ) ]; - dd.target = event.target; - dd.pageX = event.pageX; - dd.pageY = event.pageY; - dd.dragging = null; - // handle draginit event... - results = drag.hijack( event, "draginit", dd ); - // early cancel - if ( !dd.propagates ) - return; - // flatten the result set - results = drag.flatten( results ); - // insert new interaction elements - if ( results && results.length ){ - dd.interactions = []; - $.each( results, function(){ - dd.interactions.push( drag.interaction( this, dd ) ); - }); - } - // remember how many interactions are propagating - dd.propagates = dd.interactions.length; - // locate and init the drop targets - if ( dd.drop !== false && $special.drop ) - $special.drop.handler( event, dd ); - // disable text selection - drag.textselect( false ); - // bind additional events... - if ( drag.touched ) - $event.add( drag.touched, "touchmove touchend", drag.handler, dd ); - else - $event.add( document, "mousemove mouseup", drag.handler, dd ); - // helps prevent text selection or scrolling - if ( !drag.touched || dd.live ) - return false; - }, - - // returns an interaction object - interaction: function( elem, dd ){ - var offset = $( elem )[ dd.relative ? "position" : "offset" ]() || { top:0, left:0 }; - return { - drag: elem, - callback: new drag.callback(), - droppable: [], - offset: offset - }; - }, - - // handle drag-releatd DOM events - handler: function( event ){ - // read the data before hijacking anything - var dd = event.data; - // handle various events - switch ( event.type ){ - // mousemove, check distance, start dragging - case !dd.dragging && 'touchmove': - event.preventDefault(); - case !dd.dragging && 'mousemove': - // drag tolerance, x² + y² = distance² - if ( Math.pow( event.pageX-dd.pageX, 2 ) + Math.pow( event.pageY-dd.pageY, 2 ) < Math.pow( dd.distance, 2 ) ) - break; // distance tolerance not reached - event.target = dd.target; // force target from "mousedown" event (fix distance issue) - drag.hijack( event, "dragstart", dd ); // trigger "dragstart" - if ( dd.propagates ) // "dragstart" not rejected - dd.dragging = true; // activate interaction - // mousemove, dragging - case 'touchmove': - event.preventDefault(); - case 'mousemove': - if ( dd.dragging ){ - // trigger "drag" - drag.hijack( event, "drag", dd ); - if ( dd.propagates ){ - // manage drop events - if ( dd.drop !== false && $special.drop ) - $special.drop.handler( event, dd ); // "dropstart", "dropend" - break; // "drag" not rejected, stop - } - event.type = "mouseup"; // helps "drop" handler behave - } - // mouseup, stop dragging - case 'touchend': - case 'mouseup': - default: - if ( drag.touched ) - $event.remove( drag.touched, "touchmove touchend", drag.handler ); // remove touch events - else - $event.remove( document, "mousemove mouseup", drag.handler ); // remove page events - if ( dd.dragging ){ - if ( dd.drop !== false && $special.drop ) - $special.drop.handler( event, dd ); // "drop" - drag.hijack( event, "dragend", dd ); // trigger "dragend" - } - drag.textselect( true ); // enable text selection - // if suppressing click events... - if ( dd.click === false && dd.dragging ) - $.data( dd.mousedown, "suppress.click", new Date().getTime() + 5 ); - dd.dragging = drag.touched = false; // deactivate element - break; - } - }, - - // re-use event object for custom events - hijack: function( event, type, dd, x, elem ){ - // not configured - if ( !dd ) - return; - // remember the original event and type - var orig = { event:event.originalEvent, type:event.type }, - // is the event drag related or drog related? - mode = type.indexOf("drop") ? "drag" : "drop", - // iteration vars - result, i = x || 0, ia, $elems, callback, - len = !isNaN( x ) ? x : dd.interactions.length; - // modify the event type - event.type = type; - // remove the original event - event.originalEvent = null; - // initialize the results - dd.results = []; - // handle each interacted element - do if ( ia = dd.interactions[ i ] ){ - // validate the interaction - if ( type !== "dragend" && ia.cancelled ) - continue; - // set the dragdrop properties on the event object - callback = drag.properties( event, dd, ia ); - // prepare for more results - ia.results = []; - // handle each element - $( elem || ia[ mode ] || dd.droppable ).each(function( p, subject ){ - // identify drag or drop targets individually - callback.target = subject; - // force propagtion of the custom event - event.isPropagationStopped = function(){ return false; }; - // handle the event - result = subject ? $event.dispatch.call( subject, event, callback ) : null; - // stop the drag interaction for this element - if ( result === false ){ - if ( mode == "drag" ){ - ia.cancelled = true; - dd.propagates -= 1; - } - if ( type == "drop" ){ - ia[ mode ][p] = null; - } - } - // assign any dropinit elements - else if ( type == "dropinit" ) - ia.droppable.push( drag.element( result ) || subject ); - // accept a returned proxy element - if ( type == "dragstart" ) - ia.proxy = $( drag.element( result ) || ia.drag )[0]; - // remember this result - ia.results.push( result ); - // forget the event result, for recycling - delete event.result; - // break on cancelled handler - if ( type !== "dropinit" ) - return result; - }); - // flatten the results - dd.results[ i ] = drag.flatten( ia.results ); - // accept a set of valid drop targets - if ( type == "dropinit" ) - ia.droppable = drag.flatten( ia.droppable ); - // locate drop targets - if ( type == "dragstart" && !ia.cancelled ) - callback.update(); - } - while ( ++i < len ) - // restore the original event & type - event.type = orig.type; - event.originalEvent = orig.event; - // return all handler results - return drag.flatten( dd.results ); - }, - - // extend the callback object with drag/drop properties... - properties: function( event, dd, ia ){ - var obj = ia.callback; - // elements - obj.drag = ia.drag; - obj.proxy = ia.proxy || ia.drag; - // starting mouse position - obj.startX = dd.pageX; - obj.startY = dd.pageY; - // current distance dragged - obj.deltaX = event.pageX - dd.pageX; - obj.deltaY = event.pageY - dd.pageY; - // original element position - obj.originalX = ia.offset.left; - obj.originalY = ia.offset.top; - // adjusted element position - obj.offsetX = obj.originalX + obj.deltaX; - obj.offsetY = obj.originalY + obj.deltaY; - // assign the drop targets information - obj.drop = drag.flatten( ( ia.drop || [] ).slice() ); - obj.available = drag.flatten( ( ia.droppable || [] ).slice() ); - return obj; - }, - - // determine is the argument is an element or jquery instance - element: function( arg ){ - if ( arg && ( arg.jquery || arg.nodeType == 1 ) ) - return arg; - }, - - // flatten nested jquery objects and arrays into a single dimension array - flatten: function( arr ){ - return $.map( arr, function( member ){ - return member && member.jquery ? $.makeArray( member ) : - member && member.length ? drag.flatten( member ) : member; - }); - }, - - // toggles text selection attributes ON (true) or OFF (false) - textselect: function( bool ){ - $( document )[ bool ? "unbind" : "bind" ]("selectstart", drag.dontstart ) - .css("MozUserSelect", bool ? "" : "none" ); - // .attr("unselectable", bool ? "off" : "on" ) - document.unselectable = bool ? "off" : "on"; - }, - - // suppress "selectstart" and "ondragstart" events - dontstart: function(){ - return false; - }, - - // a callback instance contructor - callback: function(){} - -}; - -// callback methods -drag.callback.prototype = { - update: function(){ - if ( $special.drop && this.available.length ) - $.each( this.available, function( i ){ - $special.drop.locate( this, i ); - }); - } -}; - -// patch $.event.$dispatch to allow suppressing clicks -var $dispatch = $event.dispatch; -$event.dispatch = function( event ){ - if ( $.data( this, "suppress."+ event.type ) - new Date().getTime() > 0 ){ - $.removeData( this, "suppress."+ event.type ); - return; - } - return $dispatch.apply( this, arguments ); -}; - -// event fix hooks for touch events... -var touchHooks = -$event.fixHooks.touchstart = -$event.fixHooks.touchmove = -$event.fixHooks.touchend = -$event.fixHooks.touchcancel = { - props: "clientX clientY pageX pageY screenX screenY".split( " " ), - filter: function( event, orig ) { - if ( orig ){ - var touched = ( orig.touches && orig.touches[0] ) - || ( orig.changedTouches && orig.changedTouches[0] ) - || null; - // iOS webkit: touchstart, touchmove, touchend - if ( touched ) - $.each( touchHooks.props, function( i, prop ){ - event[ prop ] = touched[ prop ]; - }); - } - return event; - } -}; - -// share the same special event configuration with related events... -$special.draginit = $special.dragstart = $special.dragend = drag; - -})( jQuery ); diff --git a/frappe/public/js/lib/slickgrid/plugins/slick.autotooltips.js b/frappe/public/js/lib/slickgrid/plugins/slick.autotooltips.js deleted file mode 100644 index 955684f2aa..0000000000 --- a/frappe/public/js/lib/slickgrid/plugins/slick.autotooltips.js +++ /dev/null @@ -1,83 +0,0 @@ -(function ($) { - // Register namespace - $.extend(true, window, { - "Slick": { - "AutoTooltips": AutoTooltips - } - }); - - /** - * AutoTooltips plugin to show/hide tooltips when columns are too narrow to fit content. - * @constructor - * @param {boolean} [options.enableForCells=true] - Enable tooltip for grid cells - * @param {boolean} [options.enableForHeaderCells=false] - Enable tooltip for header cells - * @param {number} [options.maxToolTipLength=null] - The maximum length for a tooltip - */ - function AutoTooltips(options) { - var _grid; - var _self = this; - var _defaults = { - enableForCells: true, - enableForHeaderCells: false, - maxToolTipLength: null - }; - - /** - * Initialize plugin. - */ - function init(grid) { - options = $.extend(true, {}, _defaults, options); - _grid = grid; - if (options.enableForCells) _grid.onMouseEnter.subscribe(handleMouseEnter); - if (options.enableForHeaderCells) _grid.onHeaderMouseEnter.subscribe(handleHeaderMouseEnter); - } - - /** - * Destroy plugin. - */ - function destroy() { - if (options.enableForCells) _grid.onMouseEnter.unsubscribe(handleMouseEnter); - if (options.enableForHeaderCells) _grid.onHeaderMouseEnter.unsubscribe(handleHeaderMouseEnter); - } - - /** - * Handle mouse entering grid cell to add/remove tooltip. - * @param {jQuery.Event} e - The event - */ - function handleMouseEnter(e) { - var cell = _grid.getCellFromEvent(e); - if (cell) { - var $node = $(_grid.getCellNode(cell.row, cell.cell)); - var text; - if ($node.innerWidth() < $node[0].scrollWidth) { - text = $.trim($node.text()); - if (options.maxToolTipLength && text.length > options.maxToolTipLength) { - text = text.substr(0, options.maxToolTipLength - 3) + "..."; - } - } else { - text = ""; - } - $node.attr("title", text); - } - } - - /** - * Handle mouse entering header cell to add/remove tooltip. - * @param {jQuery.Event} e - The event - * @param {object} args.column - The column definition - */ - function handleHeaderMouseEnter(e, args) { - var column = args.column, - $node = $(e.target).closest(".slick-header-column"); - if (!column.toolTip) { - $node.attr("title", ($node.innerWidth() < $node[0].scrollWidth) ? column.name : ""); - } - } - - // Public API - $.extend(this, { - "init": init, - "destroy": destroy - }); - } -})(jQuery); \ No newline at end of file diff --git a/frappe/public/js/lib/slickgrid/plugins/slick.cellcopymanager.js b/frappe/public/js/lib/slickgrid/plugins/slick.cellcopymanager.js deleted file mode 100644 index 2d8ecc8e71..0000000000 --- a/frappe/public/js/lib/slickgrid/plugins/slick.cellcopymanager.js +++ /dev/null @@ -1,86 +0,0 @@ -(function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CellCopyManager": CellCopyManager - } - }); - - - function CellCopyManager() { - var _grid; - var _self = this; - var _copiedRanges; - - function init(grid) { - _grid = grid; - _grid.onKeyDown.subscribe(handleKeyDown); - } - - function destroy() { - _grid.onKeyDown.unsubscribe(handleKeyDown); - } - - function handleKeyDown(e, args) { - var ranges; - if (!_grid.getEditorLock().isActive()) { - if (e.which == frappe.ui.keyCode.ESCAPE) { - if (_copiedRanges) { - e.preventDefault(); - clearCopySelection(); - _self.onCopyCancelled.notify({ranges: _copiedRanges}); - _copiedRanges = null; - } - } - - if (e.which == 67 && (e.ctrlKey || e.metaKey)) { - ranges = _grid.getSelectionModel().getSelectedRanges(); - if (ranges.length != 0) { - e.preventDefault(); - _copiedRanges = ranges; - markCopySelection(ranges); - _self.onCopyCells.notify({ranges: ranges}); - } - } - - if (e.which == 86 && (e.ctrlKey || e.metaKey)) { - if (_copiedRanges) { - e.preventDefault(); - clearCopySelection(); - ranges = _grid.getSelectionModel().getSelectedRanges(); - _self.onPasteCells.notify({from: _copiedRanges, to: ranges}); - _copiedRanges = null; - } - } - } - } - - function markCopySelection(ranges) { - var columns = _grid.getColumns(); - var hash = {}; - for (var i = 0; i < ranges.length; i++) { - for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { - hash[j] = {}; - for (var k = ranges[i].fromCell; k <= ranges[i].toCell; k++) { - hash[j][columns[k].id] = "copied"; - } - } - } - _grid.setCellCssStyles("copy-manager", hash); - } - - function clearCopySelection() { - _grid.removeCellCssStyles("copy-manager"); - } - - $.extend(this, { - "init": init, - "destroy": destroy, - "clearCopySelection": clearCopySelection, - - "onCopyCells": new Slick.Event(), - "onCopyCancelled": new Slick.Event(), - "onPasteCells": new Slick.Event() - }); - } -})(jQuery); \ No newline at end of file diff --git a/frappe/public/js/lib/slickgrid/plugins/slick.cellexternalcopymanager.js b/frappe/public/js/lib/slickgrid/plugins/slick.cellexternalcopymanager.js deleted file mode 100644 index bf6645eb02..0000000000 --- a/frappe/public/js/lib/slickgrid/plugins/slick.cellexternalcopymanager.js +++ /dev/null @@ -1,274 +0,0 @@ -(function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CellExternalCopyManager": CellExternalCopyManager - } - }); - - - function CellExternalCopyManager(options) { - /* - This manager enables users to copy/paste data from/to an external Spreadsheet application - - Since it is not possible to access directly the clipboard in javascript, the plugin uses - a trick to do it's job. After detecting the keystroke, we dynamically create a textarea - where the browser copies/pastes the serialized data. - - options: - copiedCellStyle : sets the css className used for copied cells. default : "copied" - copiedCellStyleLayerKey : sets the layer key for setting css values of copied cells. default : "copy-manager" - dataItemColumnValueExtractor : option to specify a custom column value extractor function - dataItemColumnValueSetter : option to specify a custom column value setter function - */ - var _grid; - var _self = this; - var _copiedRanges; - var _options = options || {}; - var _copiedCellStyleLayerKey = _options.copiedCellStyleLayerKey || "copy-manager"; - var _copiedCellStyle = _options.copiedCellStyle || "copied"; - var _clearCopyTI = 0; - - var keyCodes = { - 'C':67, - 'V':86 - } - - function init(grid) { - _grid = grid; - _grid.onKeyDown.subscribe(handleKeyDown); - - // we need a cell selection model - var cellSelectionModel = grid.getSelectionModel(); - if (!cellSelectionModel){ - throw new Error("Selection model is mandatory for this plugin. Please set a selection model on the grid before adding this plugin: grid.setSelectionModel(new Slick.CellSelectionModel())"); - } - // we give focus on the grid when a selection is done on it. - // without this, if the user selects a range of cell without giving focus on a particular cell, the grid doesn't get the focus and key stroke handles (ctrl+c) don't work - cellSelectionModel.onSelectedRangesChanged.subscribe(function(e, args){ - _grid.focus(); - }); - } - - function destroy() { - _grid.onKeyDown.unsubscribe(handleKeyDown); - } - - function getDataItemValueForColumn(item, columnDef) { - if (_options.dataItemColumnValueExtractor) { - return _options.dataItemColumnValueExtractor(item, columnDef); - } - // if a custom getter is not defined, we call serializeValue of the editor to serialize - var editorArgs = { - 'container':$(document), // a dummy container - 'column':columnDef - }; - var editor = new columnDef.editor(editorArgs); - var retVal = ''; - editor.loadValue(item); - retVal = editor.serializeValue(); - editor.destroy(); - - return retVal; - } - - function setDataItemValueForColumn(item, columnDef, value) { - if (_options.dataItemColumnValueSetter) { - return _options.dataItemColumnValueSetter(item, columnDef, value); - } - // if a custom setter is not defined, we call applyValue of the editor to unserialize - var editorArgs = { - 'container':$(document), // a dummy container - 'column':columnDef - }; - var editor = new columnDef.editor(editorArgs); - editor.loadValue(item); - editor.applyValue(item, value); - editor.destroy(); - } - - - function _createTextBox(innerText){ - var ta = document.createElement('textarea'); - ta.style.position = 'absolute'; - ta.style.left = '-1000px'; - ta.style.top = '-1000px'; - ta.value = innerText; - document.body.appendChild(ta); - ta.focus(); - - return ta; - } - - function _decodeTabularData(_grid, ta){ - var columns = _grid.getColumns(); - var clipText = ta.value; - var clipRows = clipText.split(/[\n\f\r]/); - var clippedRange = []; - - document.body.removeChild(ta); - - for (var i=0; i
", {css: options.selectionCss}) - .addClass(options.selectionCssClass) - .css("position", "absolute") - .appendTo(grid.getCanvasNode()); - } - - var from = grid.getCellNodeBox(range.fromRow, range.fromCell); - var to = grid.getCellNodeBox(range.toRow, range.toCell); - - _elem.css({ - top: from.top - 1, - left: from.left - 1, - height: to.bottom - from.top - 2, - width: to.right - from.left - 2 - }); - - return _elem; - } - - function hide() { - if (_elem) { - _elem.remove(); - _elem = null; - } - } - - $.extend(this, { - "show": show, - "hide": hide - }); - } -})(jQuery); diff --git a/frappe/public/js/lib/slickgrid/plugins/slick.cellrangeselector.js b/frappe/public/js/lib/slickgrid/plugins/slick.cellrangeselector.js deleted file mode 100644 index 520b17f3c4..0000000000 --- a/frappe/public/js/lib/slickgrid/plugins/slick.cellrangeselector.js +++ /dev/null @@ -1,113 +0,0 @@ -(function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CellRangeSelector": CellRangeSelector - } - }); - - - function CellRangeSelector(options) { - var _grid; - var _canvas; - var _dragging; - var _decorator; - var _self = this; - var _handler = new Slick.EventHandler(); - var _defaults = { - selectionCss: { - "border": "2px dashed blue" - } - }; - - - function init(grid) { - options = $.extend(true, {}, _defaults, options); - _decorator = new Slick.CellRangeDecorator(grid, options); - _grid = grid; - _canvas = _grid.getCanvasNode(); - _handler - .subscribe(_grid.onDragInit, handleDragInit) - .subscribe(_grid.onDragStart, handleDragStart) - .subscribe(_grid.onDrag, handleDrag) - .subscribe(_grid.onDragEnd, handleDragEnd); - } - - function destroy() { - _handler.unsubscribeAll(); - } - - function handleDragInit(e, dd) { - // prevent the grid from cancelling drag'n'drop by default - e.stopImmediatePropagation(); - } - - function handleDragStart(e, dd) { - var cell = _grid.getCellFromEvent(e); - if (_self.onBeforeCellRangeSelected.notify(cell) !== false) { - if (_grid.canCellBeSelected(cell.row, cell.cell)) { - _dragging = true; - e.stopImmediatePropagation(); - } - } - if (!_dragging) { - return; - } - - _grid.focus(); - - var start = _grid.getCellFromPoint( - dd.startX - $(_canvas).offset().left, - dd.startY - $(_canvas).offset().top); - - dd.range = {start: start, end: {}}; - - return _decorator.show(new Slick.Range(start.row, start.cell)); - } - - function handleDrag(e, dd) { - if (!_dragging) { - return; - } - e.stopImmediatePropagation(); - - var end = _grid.getCellFromPoint( - e.pageX - $(_canvas).offset().left, - e.pageY - $(_canvas).offset().top); - - if (!_grid.canCellBeSelected(end.row, end.cell)) { - return; - } - - dd.range.end = end; - _decorator.show(new Slick.Range(dd.range.start.row, dd.range.start.cell, end.row, end.cell)); - } - - function handleDragEnd(e, dd) { - if (!_dragging) { - return; - } - - _dragging = false; - e.stopImmediatePropagation(); - - _decorator.hide(); - _self.onCellRangeSelected.notify({ - range: new Slick.Range( - dd.range.start.row, - dd.range.start.cell, - dd.range.end.row, - dd.range.end.cell - ) - }); - } - - $.extend(this, { - "init": init, - "destroy": destroy, - - "onBeforeCellRangeSelected": new Slick.Event(), - "onCellRangeSelected": new Slick.Event() - }); - } -})(jQuery); \ No newline at end of file diff --git a/frappe/public/js/lib/slickgrid/plugins/slick.cellselectionmodel.js b/frappe/public/js/lib/slickgrid/plugins/slick.cellselectionmodel.js deleted file mode 100644 index 74bc3eb70e..0000000000 --- a/frappe/public/js/lib/slickgrid/plugins/slick.cellselectionmodel.js +++ /dev/null @@ -1,154 +0,0 @@ -(function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CellSelectionModel": CellSelectionModel - } - }); - - - function CellSelectionModel(options) { - var _grid; - var _canvas; - var _ranges = []; - var _self = this; - var _selector = new Slick.CellRangeSelector({ - "selectionCss": { - "border": "2px solid black" - } - }); - var _options; - var _defaults = { - selectActiveCell: true - }; - - - function init(grid) { - _options = $.extend(true, {}, _defaults, options); - _grid = grid; - _canvas = _grid.getCanvasNode(); - _grid.onActiveCellChanged.subscribe(handleActiveCellChange); - _grid.onKeyDown.subscribe(handleKeyDown); - grid.registerPlugin(_selector); - _selector.onCellRangeSelected.subscribe(handleCellRangeSelected); - _selector.onBeforeCellRangeSelected.subscribe(handleBeforeCellRangeSelected); - } - - function destroy() { - _grid.onActiveCellChanged.unsubscribe(handleActiveCellChange); - _grid.onKeyDown.unsubscribe(handleKeyDown); - _selector.onCellRangeSelected.unsubscribe(handleCellRangeSelected); - _selector.onBeforeCellRangeSelected.unsubscribe(handleBeforeCellRangeSelected); - _grid.unregisterPlugin(_selector); - } - - function removeInvalidRanges(ranges) { - var result = []; - - for (var i = 0; i < ranges.length; i++) { - var r = ranges[i]; - if (_grid.canCellBeSelected(r.fromRow, r.fromCell) && _grid.canCellBeSelected(r.toRow, r.toCell)) { - result.push(r); - } - } - - return result; - } - - function setSelectedRanges(ranges) { - _ranges = removeInvalidRanges(ranges); - _self.onSelectedRangesChanged.notify(_ranges); - } - - function getSelectedRanges() { - return _ranges; - } - - function handleBeforeCellRangeSelected(e, args) { - if (_grid.getEditorLock().isActive()) { - e.stopPropagation(); - return false; - } - } - - function handleCellRangeSelected(e, args) { - setSelectedRanges([args.range]); - } - - function handleActiveCellChange(e, args) { - if (_options.selectActiveCell && args.row != null && args.cell != null) { - setSelectedRanges([new Slick.Range(args.row, args.cell)]); - } - } - - function handleKeyDown(e) { - /*** - * Кey codes - * 37 left - * 38 up - * 39 right - * 40 down - */ - var ranges, last; - var active = _grid.getActiveCell(); - - if ( active && e.shiftKey && !e.ctrlKey && !e.altKey && - (e.which == 37 || e.which == 39 || e.which == 38 || e.which == 40) ) { - - ranges = getSelectedRanges(); - if (!ranges.length) - ranges.push(new Slick.Range(active.row, active.cell)); - - // keyboard can work with last range only - last = ranges.pop(); - - // can't handle selection out of active cell - if (!last.contains(active.row, active.cell)) - last = new Slick.Range(active.row, active.cell); - - var dRow = last.toRow - last.fromRow, - dCell = last.toCell - last.fromCell, - // walking direction - dirRow = active.row == last.fromRow ? 1 : -1, - dirCell = active.cell == last.fromCell ? 1 : -1; - - if (e.which == 37) { - dCell -= dirCell; - } else if (e.which == 39) { - dCell += dirCell ; - } else if (e.which == 38) { - dRow -= dirRow; - } else if (e.which == 40) { - dRow += dirRow; - } - - // define new selection range - var new_last = new Slick.Range(active.row, active.cell, active.row + dirRow*dRow, active.cell + dirCell*dCell); - if (removeInvalidRanges([new_last]).length) { - ranges.push(new_last); - var viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow; - var viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell; - _grid.scrollRowIntoView(viewRow); - _grid.scrollCellIntoView(viewRow, viewCell); - } - else - ranges.push(last); - - setSelectedRanges(ranges); - - e.preventDefault(); - e.stopPropagation(); - } - } - - $.extend(this, { - "getSelectedRanges": getSelectedRanges, - "setSelectedRanges": setSelectedRanges, - - "init": init, - "destroy": destroy, - - "onSelectedRangesChanged": new Slick.Event() - }); - } -})(jQuery); diff --git a/frappe/public/js/lib/slickgrid/plugins/slick.checkboxselectcolumn.js b/frappe/public/js/lib/slickgrid/plugins/slick.checkboxselectcolumn.js deleted file mode 100644 index 83d8d5008e..0000000000 --- a/frappe/public/js/lib/slickgrid/plugins/slick.checkboxselectcolumn.js +++ /dev/null @@ -1,153 +0,0 @@ -(function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "CheckboxSelectColumn": CheckboxSelectColumn - } - }); - - - function CheckboxSelectColumn(options) { - var _grid; - var _self = this; - var _handler = new Slick.EventHandler(); - var _selectedRowsLookup = {}; - var _defaults = { - columnId: "_checkbox_selector", - cssClass: null, - toolTip: "Select/Deselect All", - width: 30 - }; - - var _options = $.extend(true, {}, _defaults, options); - - function init(grid) { - _grid = grid; - _handler - .subscribe(_grid.onSelectedRowsChanged, handleSelectedRowsChanged) - .subscribe(_grid.onClick, handleClick) - .subscribe(_grid.onHeaderClick, handleHeaderClick) - .subscribe(_grid.onKeyDown, handleKeyDown); - } - - function destroy() { - _handler.unsubscribeAll(); - } - - function handleSelectedRowsChanged(e, args) { - var selectedRows = _grid.getSelectedRows(); - var lookup = {}, row, i; - for (i = 0; i < selectedRows.length; i++) { - row = selectedRows[i]; - lookup[row] = true; - if (lookup[row] !== _selectedRowsLookup[row]) { - _grid.invalidateRow(row); - delete _selectedRowsLookup[row]; - } - } - for (i in _selectedRowsLookup) { - _grid.invalidateRow(i); - } - _selectedRowsLookup = lookup; - _grid.render(); - - if (selectedRows.length && selectedRows.length == _grid.getDataLength()) { - _grid.updateColumnHeader(_options.columnId, "", _options.toolTip); - } else { - _grid.updateColumnHeader(_options.columnId, "", _options.toolTip); - } - } - - function handleKeyDown(e, args) { - if (e.which == 32) { - if (_grid.getColumns()[args.cell].id === _options.columnId) { - // if editing, try to commit - if (!_grid.getEditorLock().isActive() || _grid.getEditorLock().commitCurrentEdit()) { - toggleRowSelection(args.row); - } - e.preventDefault(); - e.stopImmediatePropagation(); - } - } - } - - function handleClick(e, args) { - // clicking on a row select checkbox - if (_grid.getColumns()[args.cell].id === _options.columnId && $(e.target).is(":checkbox")) { - // if editing, try to commit - if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) { - e.preventDefault(); - e.stopImmediatePropagation(); - return; - } - - toggleRowSelection(args.row); - e.stopPropagation(); - e.stopImmediatePropagation(); - } - } - - function toggleRowSelection(row) { - if (_selectedRowsLookup[row]) { - _grid.setSelectedRows($.grep(_grid.getSelectedRows(), function (n) { - return n != row - })); - } else { - _grid.setSelectedRows(_grid.getSelectedRows().concat(row)); - } - } - - function handleHeaderClick(e, args) { - if (args.column.id == _options.columnId && $(e.target).is(":checkbox")) { - // if editing, try to commit - if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) { - e.preventDefault(); - e.stopImmediatePropagation(); - return; - } - - if ($(e.target).is(":checked")) { - var rows = []; - for (var i = 0; i < _grid.getDataLength(); i++) { - rows.push(i); - } - _grid.setSelectedRows(rows); - } else { - _grid.setSelectedRows([]); - } - e.stopPropagation(); - e.stopImmediatePropagation(); - } - } - - function getColumnDefinition() { - return { - id: _options.columnId, - name: "", - toolTip: _options.toolTip, - field: "sel", - width: _options.width, - resizable: false, - sortable: false, - cssClass: _options.cssClass, - formatter: checkboxSelectionFormatter - }; - } - - function checkboxSelectionFormatter(row, cell, value, columnDef, dataContext) { - if (dataContext) { - return _selectedRowsLookup[row] - ? "" - : ""; - } - return null; - } - - $.extend(this, { - "init": init, - "destroy": destroy, - - "getColumnDefinition": getColumnDefinition - }); - } -})(jQuery); \ No newline at end of file diff --git a/frappe/public/js/lib/slickgrid/plugins/slick.headerbuttons.css b/frappe/public/js/lib/slickgrid/plugins/slick.headerbuttons.css deleted file mode 100644 index 0ba79ea0df..0000000000 --- a/frappe/public/js/lib/slickgrid/plugins/slick.headerbuttons.css +++ /dev/null @@ -1,39 +0,0 @@ -.slick-column-name, -.slick-sort-indicator { - /** - * This makes all "float:right" elements after it that spill over to the next line - * display way below the lower boundary of the column thus hiding them. - */ - display: inline-block; - float: left; - margin-bottom: 100px; -} - -.slick-header-button { - display: inline-block; - float: right; - vertical-align: top; - margin: 1px; - /** - * This makes all "float:right" elements after it that spill over to the next line - * display way below the lower boundary of the column thus hiding them. - */ - margin-bottom: 100px; - height: 15px; - width: 15px; - background-repeat: no-repeat; - background-position: center center; - cursor: pointer; -} - -.slick-header-button-hidden { - width: 0; - - -webkit-transition: 0.2s width; - -ms-transition: 0.2s width; - transition: 0.2s width; -} - -.slick-header-column:hover > .slick-header-button { - width: 15px; -} \ No newline at end of file diff --git a/frappe/public/js/lib/slickgrid/plugins/slick.headerbuttons.js b/frappe/public/js/lib/slickgrid/plugins/slick.headerbuttons.js deleted file mode 100644 index 8e612735e6..0000000000 --- a/frappe/public/js/lib/slickgrid/plugins/slick.headerbuttons.js +++ /dev/null @@ -1,177 +0,0 @@ -(function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "Plugins": { - "HeaderButtons": HeaderButtons - } - } - }); - - - /*** - * A plugin to add custom buttons to column headers. - * - * USAGE: - * - * Add the plugin .js & .css files and register it with the grid. - * - * To specify a custom button in a column header, extend the column definition like so: - * - * var columns = [ - * { - * id: 'myColumn', - * name: 'My column', - * - * // This is the relevant part - * header: { - * buttons: [ - * { - * // button options - * }, - * { - * // button options - * } - * ] - * } - * } - * ]; - * - * Available button options: - * cssClass: CSS class to add to the button. - * image: Relative button image path. - * tooltip: Button tooltip. - * showOnHover: Only show the button on hover. - * handler: Button click handler. - * command: A command identifier to be passed to the onCommand event handlers. - * - * The plugin exposes the following events: - * onCommand: Fired on button click for buttons with 'command' specified. - * Event args: - * grid: Reference to the grid. - * column: Column definition. - * command: Button command identified. - * button: Button options. Note that you can change the button options in your - * event handler, and the column header will be automatically updated to - * reflect them. This is useful if you want to implement something like a - * toggle button. - * - * - * @param options {Object} Options: - * buttonCssClass: a CSS class to use for buttons (default 'slick-header-button') - * @class Slick.Plugins.HeaderButtons - * @constructor - */ - function HeaderButtons(options) { - var _grid; - var _self = this; - var _handler = new Slick.EventHandler(); - var _defaults = { - buttonCssClass: "slick-header-button" - }; - - - function init(grid) { - options = $.extend(true, {}, _defaults, options); - _grid = grid; - _handler - .subscribe(_grid.onHeaderCellRendered, handleHeaderCellRendered) - .subscribe(_grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy); - - // Force the grid to re-render the header now that the events are hooked up. - _grid.setColumns(_grid.getColumns()); - } - - - function destroy() { - _handler.unsubscribeAll(); - } - - - function handleHeaderCellRendered(e, args) { - var column = args.column; - - if (column.header && column.header.buttons) { - // Append buttons in reverse order since they are floated to the right. - var i = column.header.buttons.length; - while (i--) { - var button = column.header.buttons[i]; - var btn = $("
") - .addClass(options.buttonCssClass) - .data("column", column) - .data("button", button); - - if (button.showOnHover) { - btn.addClass("slick-header-button-hidden"); - } - - if (button.image) { - btn.css("backgroundImage", "url(" + button.image + ")"); - } - - if (button.cssClass) { - btn.addClass(button.cssClass); - } - - if (button.tooltip) { - btn.attr("title", button.tooltip); - } - - if (button.command) { - btn.data("command", button.command); - } - - if (button.handler) { - btn.bind("click", button.handler); - } - - btn - .bind("click", handleButtonClick) - .appendTo(args.node); - } - } - } - - - function handleBeforeHeaderCellDestroy(e, args) { - var column = args.column; - - if (column.header && column.header.buttons) { - // Removing buttons via jQuery will also clean up any event handlers and data. - // NOTE: If you attach event handlers directly or using a different framework, - // you must also clean them up here to avoid memory leaks. - $(args.node).find("." + options.buttonCssClass).remove(); - } - } - - - function handleButtonClick(e) { - var command = $(this).data("command"); - var columnDef = $(this).data("column"); - var button = $(this).data("button"); - - if (command != null) { - _self.onCommand.notify({ - "grid": _grid, - "column": columnDef, - "command": command, - "button": button - }, e, _self); - - // Update the header in case the user updated the button definition in the handler. - _grid.updateColumnHeader(columnDef.id); - } - - // Stop propagation so that it doesn't register as a header click event. - e.preventDefault(); - e.stopPropagation(); - } - - $.extend(this, { - "init": init, - "destroy": destroy, - - "onCommand": new Slick.Event() - }); - } -})(jQuery); \ No newline at end of file diff --git a/frappe/public/js/lib/slickgrid/plugins/slick.headermenu.css b/frappe/public/js/lib/slickgrid/plugins/slick.headermenu.css deleted file mode 100644 index 8b0b6a9f7b..0000000000 --- a/frappe/public/js/lib/slickgrid/plugins/slick.headermenu.css +++ /dev/null @@ -1,59 +0,0 @@ -/* Menu button */ -.slick-header-menubutton { - position: absolute; - right: 0; - top: 0; - bottom: 0; - width: 14px; - background-repeat: no-repeat; - background-position: left center; - background-image: url(../images/down.gif); - cursor: pointer; - - display: none; - border-left: thin ridge silver; -} - -.slick-header-column:hover > .slick-header-menubutton, -.slick-header-column-active .slick-header-menubutton { - display: inline-block; -} - -/* Menu */ -.slick-header-menu { - position: absolute; - display: inline-block; - margin: 0; - padding: 2px; - cursor: default; -} - - -/* Menu items */ -.slick-header-menuitem { - list-style: none; - margin: 0; - padding: 0; - cursor: pointer; -} - -.slick-header-menuicon { - display: inline-block; - width: 16px; - height: 16px; - vertical-align: middle; - margin-right: 4px; - background-repeat: no-repeat; - background-position: center center; -} - -.slick-header-menucontent { - display: inline-block; - vertical-align: middle; -} - - -/* Disabled */ -.slick-header-menuitem-disabled { - color: silver; -} diff --git a/frappe/public/js/lib/slickgrid/plugins/slick.headermenu.js b/frappe/public/js/lib/slickgrid/plugins/slick.headermenu.js deleted file mode 100644 index ec8244daa6..0000000000 --- a/frappe/public/js/lib/slickgrid/plugins/slick.headermenu.js +++ /dev/null @@ -1,275 +0,0 @@ -(function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "Plugins": { - "HeaderMenu": HeaderMenu - } - } - }); - - - /*** - * A plugin to add drop-down menus to column headers. - * - * USAGE: - * - * Add the plugin .js & .css files and register it with the grid. - * - * To specify a menu in a column header, extend the column definition like so: - * - * var columns = [ - * { - * id: 'myColumn', - * name: 'My column', - * - * // This is the relevant part - * header: { - * menu: { - * items: [ - * { - * // menu item options - * }, - * { - * // menu item options - * } - * ] - * } - * } - * } - * ]; - * - * - * Available menu options: - * tooltip: Menu button tooltip. - * - * - * Available menu item options: - * title: Menu item text. - * disabled: Whether the item is disabled. - * tooltip: Item tooltip. - * command: A command identifier to be passed to the onCommand event handlers. - * iconCssClass: A CSS class to be added to the menu item icon. - * iconImage: A url to the icon image. - * - * - * The plugin exposes the following events: - * onBeforeMenuShow: Fired before the menu is shown. You can customize the menu or dismiss it by returning false. - * Event args: - * grid: Reference to the grid. - * column: Column definition. - * menu: Menu options. Note that you can change the menu items here. - * - * onCommand: Fired on menu item click for buttons with 'command' specified. - * Event args: - * grid: Reference to the grid. - * column: Column definition. - * command: Button command identified. - * button: Button options. Note that you can change the button options in your - * event handler, and the column header will be automatically updated to - * reflect them. This is useful if you want to implement something like a - * toggle button. - * - * - * @param options {Object} Options: - * buttonCssClass: an extra CSS class to add to the menu button - * buttonImage: a url to the menu button image (default '../images/down.gif') - * @class Slick.Plugins.HeaderButtons - * @constructor - */ - function HeaderMenu(options) { - var _grid; - var _self = this; - var _handler = new Slick.EventHandler(); - var _defaults = { - buttonCssClass: null, - buttonImage: null - }; - var $menu; - var $activeHeaderColumn; - - - function init(grid) { - options = $.extend(true, {}, _defaults, options); - _grid = grid; - _handler - .subscribe(_grid.onHeaderCellRendered, handleHeaderCellRendered) - .subscribe(_grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy); - - // Force the grid to re-render the header now that the events are hooked up. - _grid.setColumns(_grid.getColumns()); - - // Hide the menu on outside click. - $(document.body).bind("mousedown", handleBodyMouseDown); - } - - - function destroy() { - _handler.unsubscribeAll(); - $(document.body).unbind("mousedown", handleBodyMouseDown); - } - - - function handleBodyMouseDown(e) { - if ($menu && $menu[0] != e.target && !$.contains($menu[0], e.target)) { - hideMenu(); - } - } - - - function hideMenu() { - if ($menu) { - $menu.remove(); - $menu = null; - $activeHeaderColumn - .removeClass("slick-header-column-active"); - } - } - - function handleHeaderCellRendered(e, args) { - var column = args.column; - var menu = column.header && column.header.menu; - - if (menu) { - var $el = $("
") - .addClass("slick-header-menubutton") - .data("column", column) - .data("menu", menu); - - if (options.buttonCssClass) { - $el.addClass(options.buttonCssClass); - } - - if (options.buttonImage) { - $el.css("background-image", "url(" + options.buttonImage + ")"); - } - - if (menu.tooltip) { - $el.attr("title", menu.tooltip); - } - - $el - .bind("click", showMenu) - .appendTo(args.node); - } - } - - - function handleBeforeHeaderCellDestroy(e, args) { - var column = args.column; - - if (column.header && column.header.menu) { - $(args.node).find(".slick-header-menubutton").remove(); - } - } - - - function showMenu(e) { - var $menuButton = $(this); - var menu = $menuButton.data("menu"); - var columnDef = $menuButton.data("column"); - - // Let the user modify the menu or cancel altogether, - // or provide alternative menu implementation. - if (_self.onBeforeMenuShow.notify({ - "grid": _grid, - "column": columnDef, - "menu": menu - }, e, _self) == false) { - return; - } - - - if (!$menu) { - $menu = $("
") - .appendTo(_grid.getContainerNode()); - } - $menu.empty(); - - - // Construct the menu items. - for (var i = 0; i < menu.items.length; i++) { - var item = menu.items[i]; - - var $li = $("
") - .data("command", item.command || '') - .data("column", columnDef) - .data("item", item) - .bind("click", handleMenuItemClick) - .appendTo($menu); - - if (item.disabled) { - $li.addClass("slick-header-menuitem-disabled"); - } - - if (item.tooltip) { - $li.attr("title", item.tooltip); - } - - var $icon = $("
") - .appendTo($li); - - if (item.iconCssClass) { - $icon.addClass(item.iconCssClass); - } - - if (item.iconImage) { - $icon.css("background-image", "url(" + item.iconImage + ")"); - } - - $("") - .text(item.title) - .appendTo($li); - } - - - // Position the menu. - $menu - .offset({ top: $(this).offset().top + $(this).height(), left: $(this).offset().left }); - - - // Mark the header as active to keep the highlighting. - $activeHeaderColumn = $menuButton.closest(".slick-header-column"); - $activeHeaderColumn - .addClass("slick-header-column-active"); - - // Stop propagation so that it doesn't register as a header click event. - e.preventDefault(); - e.stopPropagation(); - } - - - function handleMenuItemClick(e) { - var command = $(this).data("command"); - var columnDef = $(this).data("column"); - var item = $(this).data("item"); - - if (item.disabled) { - return; - } - - hideMenu(); - - if (command != null && command != '') { - _self.onCommand.notify({ - "grid": _grid, - "column": columnDef, - "command": command, - "item": item - }, e, _self); - } - - // Stop propagation so that it doesn't register as a header click event. - e.preventDefault(); - e.stopPropagation(); - } - - $.extend(this, { - "init": init, - "destroy": destroy, - - "onBeforeMenuShow": new Slick.Event(), - "onCommand": new Slick.Event() - }); - } -})(jQuery); diff --git a/frappe/public/js/lib/slickgrid/plugins/slick.rowmovemanager.js b/frappe/public/js/lib/slickgrid/plugins/slick.rowmovemanager.js deleted file mode 100644 index 5f87a1eddf..0000000000 --- a/frappe/public/js/lib/slickgrid/plugins/slick.rowmovemanager.js +++ /dev/null @@ -1,138 +0,0 @@ -(function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "RowMoveManager": RowMoveManager - } - }); - - function RowMoveManager(options) { - var _grid; - var _canvas; - var _dragging; - var _self = this; - var _handler = new Slick.EventHandler(); - var _defaults = { - cancelEditOnDrag: false - }; - - function init(grid) { - options = $.extend(true, {}, _defaults, options); - _grid = grid; - _canvas = _grid.getCanvasNode(); - _handler - .subscribe(_grid.onDragInit, handleDragInit) - .subscribe(_grid.onDragStart, handleDragStart) - .subscribe(_grid.onDrag, handleDrag) - .subscribe(_grid.onDragEnd, handleDragEnd); - } - - function destroy() { - _handler.unsubscribeAll(); - } - - function handleDragInit(e, dd) { - // prevent the grid from cancelling drag'n'drop by default - e.stopImmediatePropagation(); - } - - function handleDragStart(e, dd) { - var cell = _grid.getCellFromEvent(e); - - if (options.cancelEditOnDrag && _grid.getEditorLock().isActive()) { - _grid.getEditorLock().cancelCurrentEdit(); - } - - if (_grid.getEditorLock().isActive() || !/move|selectAndMove/.test(_grid.getColumns()[cell.cell].behavior)) { - return false; - } - - _dragging = true; - e.stopImmediatePropagation(); - - var selectedRows = _grid.getSelectedRows(); - - if (selectedRows.length == 0 || $.inArray(cell.row, selectedRows) == -1) { - selectedRows = [cell.row]; - _grid.setSelectedRows(selectedRows); - } - - var rowHeight = _grid.getOptions().rowHeight; - - dd.selectedRows = selectedRows; - - dd.selectionProxy = $("
") - .css("position", "absolute") - .css("zIndex", "99999") - .css("width", $(_canvas).innerWidth()) - .css("height", rowHeight * selectedRows.length) - .appendTo(_canvas); - - dd.guide = $("
") - .css("position", "absolute") - .css("zIndex", "99998") - .css("width", $(_canvas).innerWidth()) - .css("top", -1000) - .appendTo(_canvas); - - dd.insertBefore = -1; - } - - function handleDrag(e, dd) { - if (!_dragging) { - return; - } - - e.stopImmediatePropagation(); - - var top = e.pageY - $(_canvas).offset().top; - dd.selectionProxy.css("top", top - 5); - - var insertBefore = Math.max(0, Math.min(Math.round(top / _grid.getOptions().rowHeight), _grid.getDataLength())); - if (insertBefore !== dd.insertBefore) { - var eventData = { - "rows": dd.selectedRows, - "insertBefore": insertBefore - }; - - if (_self.onBeforeMoveRows.notify(eventData) === false) { - dd.guide.css("top", -1000); - dd.canMove = false; - } else { - dd.guide.css("top", insertBefore * _grid.getOptions().rowHeight); - dd.canMove = true; - } - - dd.insertBefore = insertBefore; - } - } - - function handleDragEnd(e, dd) { - if (!_dragging) { - return; - } - _dragging = false; - e.stopImmediatePropagation(); - - dd.guide.remove(); - dd.selectionProxy.remove(); - - if (dd.canMove) { - var eventData = { - "rows": dd.selectedRows, - "insertBefore": dd.insertBefore - }; - // TODO: _grid.remapCellCssClasses ? - _self.onMoveRows.notify(eventData); - } - } - - $.extend(this, { - "onBeforeMoveRows": new Slick.Event(), - "onMoveRows": new Slick.Event(), - - "init": init, - "destroy": destroy - }); - } -})(jQuery); \ No newline at end of file diff --git a/frappe/public/js/lib/slickgrid/plugins/slick.rowselectionmodel.js b/frappe/public/js/lib/slickgrid/plugins/slick.rowselectionmodel.js deleted file mode 100644 index 0de8dd3a40..0000000000 --- a/frappe/public/js/lib/slickgrid/plugins/slick.rowselectionmodel.js +++ /dev/null @@ -1,187 +0,0 @@ -(function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "RowSelectionModel": RowSelectionModel - } - }); - - function RowSelectionModel(options) { - var _grid; - var _ranges = []; - var _self = this; - var _handler = new Slick.EventHandler(); - var _inHandler; - var _options; - var _defaults = { - selectActiveRow: true - }; - - function init(grid) { - _options = $.extend(true, {}, _defaults, options); - _grid = grid; - _handler.subscribe(_grid.onActiveCellChanged, - wrapHandler(handleActiveCellChange)); - _handler.subscribe(_grid.onKeyDown, - wrapHandler(handleKeyDown)); - _handler.subscribe(_grid.onClick, - wrapHandler(handleClick)); - } - - function destroy() { - _handler.unsubscribeAll(); - } - - function wrapHandler(handler) { - return function () { - if (!_inHandler) { - _inHandler = true; - handler.apply(this, arguments); - _inHandler = false; - } - }; - } - - function rangesToRows(ranges) { - var rows = []; - for (var i = 0; i < ranges.length; i++) { - for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { - rows.push(j); - } - } - return rows; - } - - function rowsToRanges(rows) { - var ranges = []; - var lastCell = _grid.getColumns().length - 1; - for (var i = 0; i < rows.length; i++) { - ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell)); - } - return ranges; - } - - function getRowsRange(from, to) { - var i, rows = []; - for (i = from; i <= to; i++) { - rows.push(i); - } - for (i = to; i < from; i++) { - rows.push(i); - } - return rows; - } - - function getSelectedRows() { - return rangesToRows(_ranges); - } - - function setSelectedRows(rows) { - setSelectedRanges(rowsToRanges(rows)); - } - - function setSelectedRanges(ranges) { - _ranges = ranges; - _self.onSelectedRangesChanged.notify(_ranges); - } - - function getSelectedRanges() { - return _ranges; - } - - function handleActiveCellChange(e, data) { - if (_options.selectActiveRow && data.row != null) { - setSelectedRanges([new Slick.Range(data.row, 0, data.row, _grid.getColumns().length - 1)]); - } - } - - function handleKeyDown(e) { - var activeRow = _grid.getActiveCell(); - if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which == 38 || e.which == 40)) { - var selectedRows = getSelectedRows(); - selectedRows.sort(function (x, y) { - return x - y - }); - - if (!selectedRows.length) { - selectedRows = [activeRow.row]; - } - - var top = selectedRows[0]; - var bottom = selectedRows[selectedRows.length - 1]; - var active; - - if (e.which == 40) { - active = activeRow.row < bottom || top == bottom ? ++bottom : ++top; - } else { - active = activeRow.row < bottom ? --bottom : --top; - } - - if (active >= 0 && active < _grid.getDataLength()) { - _grid.scrollRowIntoView(active); - _ranges = rowsToRanges(getRowsRange(top, bottom)); - setSelectedRanges(_ranges); - } - - e.preventDefault(); - e.stopPropagation(); - } - } - - function handleClick(e) { - var cell = _grid.getCellFromEvent(e); - if (!cell || !_grid.canCellBeActive(cell.row, cell.cell)) { - return false; - } - - if (!_grid.getOptions().multiSelect || ( - !e.ctrlKey && !e.shiftKey && !e.metaKey)) { - return false; - } - - var selection = rangesToRows(_ranges); - var idx = $.inArray(cell.row, selection); - - if (idx === -1 && (e.ctrlKey || e.metaKey)) { - selection.push(cell.row); - _grid.setActiveCell(cell.row, cell.cell); - } else if (idx !== -1 && (e.ctrlKey || e.metaKey)) { - selection = $.grep(selection, function (o, i) { - return (o !== cell.row); - }); - _grid.setActiveCell(cell.row, cell.cell); - } else if (selection.length && e.shiftKey) { - var last = selection.pop(); - var from = Math.min(cell.row, last); - var to = Math.max(cell.row, last); - selection = []; - for (var i = from; i <= to; i++) { - if (i !== last) { - selection.push(i); - } - } - selection.push(last); - _grid.setActiveCell(cell.row, cell.cell); - } - - _ranges = rowsToRanges(selection); - setSelectedRanges(_ranges); - e.stopImmediatePropagation(); - - return true; - } - - $.extend(this, { - "getSelectedRows": getSelectedRows, - "setSelectedRows": setSelectedRows, - - "getSelectedRanges": getSelectedRanges, - "setSelectedRanges": setSelectedRanges, - - "init": init, - "destroy": destroy, - - "onSelectedRangesChanged": new Slick.Event() - }); - } -})(jQuery); \ No newline at end of file diff --git a/frappe/public/js/lib/slickgrid/slick-default-theme.css b/frappe/public/js/lib/slickgrid/slick-default-theme.css deleted file mode 100644 index 572f184406..0000000000 --- a/frappe/public/js/lib/slickgrid/slick-default-theme.css +++ /dev/null @@ -1,118 +0,0 @@ -/* -IMPORTANT: -In order to preserve the uniform grid appearance, all cell styles need to have padding, margin and border sizes. -No built-in (selected, editable, highlight, flashing, invalid, loading, :focus) or user-specified CSS -classes should alter those! -*/ - -.slick-header-columns { - /*background: url('images/header-columns-bg.gif') repeat-x center bottom;*/ - border-bottom: 1px solid silver; -} - -.slick-header-column { - /*background: url('images/header-columns-bg.gif') repeat-x center bottom;*/ - border-right: 1px solid silver; -} - -.slick-header-column:hover, .slick-header-column-active { - /*background: white url('images/header-columns-over-bg.gif') repeat-x center bottom;*/ -} - -.slick-headerrow { - background: #fafafa; -} - -.slick-headerrow-column { - background: #fafafa; - border-bottom: 0; - height: 100%; -} - -.slick-row.ui-state-active { - background: #F5F7D7; -} - -.slick-row { - position: absolute; - background: white; - border: 0px; - line-height: 20px; -} - -.slick-row.selected { - z-index: 10; - background: #DFE8F6; -} - -.slick-cell { - padding-left: 4px; - padding-right: 4px; -} - -.slick-group { - border-bottom: 2px solid silver; -} - -.slick-group-toggle { - width: 9px; - height: 9px; - margin-right: 5px; -} - -.slick-group-toggle.expanded { - background: url(images/collapse.gif) no-repeat center center; -} - -.slick-group-toggle.collapsed { - background: url(images/expand.gif) no-repeat center center; -} - -.slick-group-totals { - color: gray; - background: white; -} - -.slick-cell.selected { - background-color: #fffce7 !important; -} - -.slick-cell.active { - border-color: gray; - border-style: solid; -} - -.slick-sortable-placeholder { - background: silver !important; -} - -.slick-row.odd { - background: #fafafa; -} - -.slick-row.ui-state-active { - background: #F5F7D7; -} - -.slick-row.loading { - opacity: 0.5; - /* filter: alpha(opacity = 50); */ -} - -.slick-cell.invalid { - border-color: red; - -moz-animation-duration: 0.2s; - -webkit-animation-duration: 0.2s; - -moz-animation-name: slickgrid-invalid-hilite; - -webkit-animation-name: slickgrid-invalid-hilite; -} - -@-moz-keyframes slickgrid-invalid-hilite { - from { box-shadow: 0 0 6px red; } - to { box-shadow: none; } -} - -@-webkit-keyframes slickgrid-invalid-hilite { - from { box-shadow: 0 0 6px red; } - to { box-shadow: none; } -} diff --git a/frappe/public/js/lib/slickgrid/slick.core.js b/frappe/public/js/lib/slickgrid/slick.core.js deleted file mode 100644 index 2f097b1db6..0000000000 --- a/frappe/public/js/lib/slickgrid/slick.core.js +++ /dev/null @@ -1,467 +0,0 @@ -/*** - * Contains core SlickGrid classes. - * @module Core - * @namespace Slick - */ - -(function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "Event": Event, - "EventData": EventData, - "EventHandler": EventHandler, - "Range": Range, - "NonDataRow": NonDataItem, - "Group": Group, - "GroupTotals": GroupTotals, - "EditorLock": EditorLock, - - /*** - * A global singleton editor lock. - * @class GlobalEditorLock - * @static - * @constructor - */ - "GlobalEditorLock": new EditorLock() - } - }); - - /*** - * An event object for passing data to event handlers and letting them control propagation. - *

This is pretty much identical to how W3C and jQuery implement events.

- * @class EventData - * @constructor - */ - function EventData() { - var isPropagationStopped = false; - var isImmediatePropagationStopped = false; - - /*** - * Stops event from propagating up the DOM tree. - * @method stopPropagation - */ - this.stopPropagation = function () { - isPropagationStopped = true; - }; - - /*** - * Returns whether stopPropagation was called on this event object. - * @method isPropagationStopped - * @return {Boolean} - */ - this.isPropagationStopped = function () { - return isPropagationStopped; - }; - - /*** - * Prevents the rest of the handlers from being executed. - * @method stopImmediatePropagation - */ - this.stopImmediatePropagation = function () { - isImmediatePropagationStopped = true; - }; - - /*** - * Returns whether stopImmediatePropagation was called on this event object.\ - * @method isImmediatePropagationStopped - * @return {Boolean} - */ - this.isImmediatePropagationStopped = function () { - return isImmediatePropagationStopped; - } - } - - /*** - * A simple publisher-subscriber implementation. - * @class Event - * @constructor - */ - function Event() { - var handlers = []; - - /*** - * Adds an event handler to be called when the event is fired. - *

Event handler will receive two arguments - an EventData and the data - * object the event was fired with.

- * @method subscribe - * @param fn {Function} Event handler. - */ - this.subscribe = function (fn) { - handlers.push(fn); - }; - - /*** - * Removes an event handler added with subscribe(fn). - * @method unsubscribe - * @param fn {Function} Event handler to be removed. - */ - this.unsubscribe = function (fn) { - for (var i = handlers.length - 1; i >= 0; i--) { - if (handlers[i] === fn) { - handlers.splice(i, 1); - } - } - }; - - /*** - * Fires an event notifying all subscribers. - * @method notify - * @param args {Object} Additional data object to be passed to all handlers. - * @param e {EventData} - * Optional. - * An EventData object to be passed to all handlers. - * For DOM events, an existing W3C/jQuery event object can be passed in. - * @param scope {Object} - * Optional. - * The scope ("this") within which the handler will be executed. - * If not specified, the scope will be set to the Event instance. - */ - this.notify = function (args, e, scope) { - e = e || new EventData(); - scope = scope || this; - - var returnValue; - for (var i = 0; i < handlers.length && !(e.isPropagationStopped() || e.isImmediatePropagationStopped()); i++) { - returnValue = handlers[i].call(scope, e, args); - } - - return returnValue; - }; - } - - function EventHandler() { - var handlers = []; - - this.subscribe = function (event, handler) { - handlers.push({ - event: event, - handler: handler - }); - event.subscribe(handler); - - return this; // allow chaining - }; - - this.unsubscribe = function (event, handler) { - var i = handlers.length; - while (i--) { - if (handlers[i].event === event && - handlers[i].handler === handler) { - handlers.splice(i, 1); - event.unsubscribe(handler); - return; - } - } - - return this; // allow chaining - }; - - this.unsubscribeAll = function () { - var i = handlers.length; - while (i--) { - handlers[i].event.unsubscribe(handlers[i].handler); - } - handlers = []; - - return this; // allow chaining - } - } - - /*** - * A structure containing a range of cells. - * @class Range - * @constructor - * @param fromRow {Integer} Starting row. - * @param fromCell {Integer} Starting cell. - * @param toRow {Integer} Optional. Ending row. Defaults to fromRow. - * @param toCell {Integer} Optional. Ending cell. Defaults to fromCell. - */ - function Range(fromRow, fromCell, toRow, toCell) { - if (toRow === undefined && toCell === undefined) { - toRow = fromRow; - toCell = fromCell; - } - - /*** - * @property fromRow - * @type {Integer} - */ - this.fromRow = Math.min(fromRow, toRow); - - /*** - * @property fromCell - * @type {Integer} - */ - this.fromCell = Math.min(fromCell, toCell); - - /*** - * @property toRow - * @type {Integer} - */ - this.toRow = Math.max(fromRow, toRow); - - /*** - * @property toCell - * @type {Integer} - */ - this.toCell = Math.max(fromCell, toCell); - - /*** - * Returns whether a range represents a single row. - * @method isSingleRow - * @return {Boolean} - */ - this.isSingleRow = function () { - return this.fromRow == this.toRow; - }; - - /*** - * Returns whether a range represents a single cell. - * @method isSingleCell - * @return {Boolean} - */ - this.isSingleCell = function () { - return this.fromRow == this.toRow && this.fromCell == this.toCell; - }; - - /*** - * Returns whether a range contains a given cell. - * @method contains - * @param row {Integer} - * @param cell {Integer} - * @return {Boolean} - */ - this.contains = function (row, cell) { - return row >= this.fromRow && row <= this.toRow && - cell >= this.fromCell && cell <= this.toCell; - }; - - /*** - * Returns a readable representation of a range. - * @method toString - * @return {String} - */ - this.toString = function () { - if (this.isSingleCell()) { - return "(" + this.fromRow + ":" + this.fromCell + ")"; - } - else { - return "(" + this.fromRow + ":" + this.fromCell + " - " + this.toRow + ":" + this.toCell + ")"; - } - } - } - - - /*** - * A base class that all special / non-data rows (like Group and GroupTotals) derive from. - * @class NonDataItem - * @constructor - */ - function NonDataItem() { - this.__nonDataRow = true; - } - - - /*** - * Information about a group of rows. - * @class Group - * @extends Slick.NonDataItem - * @constructor - */ - function Group() { - this.__group = true; - - /** - * Grouping level, starting with 0. - * @property level - * @type {Number} - */ - this.level = 0; - - /*** - * Number of rows in the group. - * @property count - * @type {Integer} - */ - this.count = 0; - - /*** - * Grouping value. - * @property value - * @type {Object} - */ - this.value = null; - - /*** - * Formatted display value of the group. - * @property title - * @type {String} - */ - this.title = null; - - /*** - * Whether a group is collapsed. - * @property collapsed - * @type {Boolean} - */ - this.collapsed = false; - - /*** - * GroupTotals, if any. - * @property totals - * @type {GroupTotals} - */ - this.totals = null; - - /** - * Rows that are part of the group. - * @property rows - * @type {Array} - */ - this.rows = []; - - /** - * Sub-groups that are part of the group. - * @property groups - * @type {Array} - */ - this.groups = null; - - /** - * A unique key used to identify the group. This key can be used in calls to DataView - * collapseGroup() or expandGroup(). - * @property groupingKey - * @type {Object} - */ - this.groupingKey = null; - } - - Group.prototype = new NonDataItem(); - - /*** - * Compares two Group instances. - * @method equals - * @return {Boolean} - * @param group {Group} Group instance to compare to. - */ - Group.prototype.equals = function (group) { - return this.value === group.value && - this.count === group.count && - this.collapsed === group.collapsed && - this.title === group.title; - }; - - /*** - * Information about group totals. - * An instance of GroupTotals will be created for each totals row and passed to the aggregators - * so that they can store arbitrary data in it. That data can later be accessed by group totals - * formatters during the display. - * @class GroupTotals - * @extends Slick.NonDataItem - * @constructor - */ - function GroupTotals() { - this.__groupTotals = true; - - /*** - * Parent Group. - * @param group - * @type {Group} - */ - this.group = null; - - /*** - * Whether the totals have been fully initialized / calculated. - * Will be set to false for lazy-calculated group totals. - * @param initialized - * @type {Boolean} - */ - this.initialized = false; - } - - GroupTotals.prototype = new NonDataItem(); - - /*** - * A locking helper to track the active edit controller and ensure that only a single controller - * can be active at a time. This prevents a whole class of state and validation synchronization - * issues. An edit controller (such as SlickGrid) can query if an active edit is in progress - * and attempt a commit or cancel before proceeding. - * @class EditorLock - * @constructor - */ - function EditorLock() { - var activeEditController = null; - - /*** - * Returns true if a specified edit controller is active (has the edit lock). - * If the parameter is not specified, returns true if any edit controller is active. - * @method isActive - * @param editController {EditController} - * @return {Boolean} - */ - this.isActive = function (editController) { - return (editController ? activeEditController === editController : activeEditController !== null); - }; - - /*** - * Sets the specified edit controller as the active edit controller (acquire edit lock). - * If another edit controller is already active, and exception will be thrown. - * @method activate - * @param editController {EditController} edit controller acquiring the lock - */ - this.activate = function (editController) { - if (editController === activeEditController) { // already activated? - return; - } - if (activeEditController !== null) { - throw "SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController"; - } - if (!editController.commitCurrentEdit) { - throw "SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()"; - } - if (!editController.cancelCurrentEdit) { - throw "SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()"; - } - activeEditController = editController; - }; - - /*** - * Unsets the specified edit controller as the active edit controller (release edit lock). - * If the specified edit controller is not the active one, an exception will be thrown. - * @method deactivate - * @param editController {EditController} edit controller releasing the lock - */ - this.deactivate = function (editController) { - if (activeEditController !== editController) { - throw "SlickGrid.EditorLock.deactivate: specified editController is not the currently active one"; - } - activeEditController = null; - }; - - /*** - * Attempts to commit the current edit by calling "commitCurrentEdit" method on the active edit - * controller and returns whether the commit attempt was successful (commit may fail due to validation - * errors, etc.). Edit controller's "commitCurrentEdit" must return true if the commit has succeeded - * and false otherwise. If no edit controller is active, returns true. - * @method commitCurrentEdit - * @return {Boolean} - */ - this.commitCurrentEdit = function () { - return (activeEditController ? activeEditController.commitCurrentEdit() : true); - }; - - /*** - * Attempts to cancel the current edit by calling "cancelCurrentEdit" method on the active edit - * controller and returns whether the edit was successfully cancelled. If no edit controller is - * active, returns true. - * @method cancelCurrentEdit - * @return {Boolean} - */ - this.cancelCurrentEdit = function cancelCurrentEdit() { - return (activeEditController ? activeEditController.cancelCurrentEdit() : true); - }; - } -})(jQuery); - - diff --git a/frappe/public/js/lib/slickgrid/slick.dataview.js b/frappe/public/js/lib/slickgrid/slick.dataview.js deleted file mode 100644 index 989250c401..0000000000 --- a/frappe/public/js/lib/slickgrid/slick.dataview.js +++ /dev/null @@ -1,1126 +0,0 @@ -(function ($) { - $.extend(true, window, { - Slick: { - Data: { - DataView: DataView, - Aggregators: { - Avg: AvgAggregator, - Min: MinAggregator, - Max: MaxAggregator, - Sum: SumAggregator - } - } - } - }); - - - /*** - * A sample Model implementation. - * Provides a filtered view of the underlying data. - * - * Relies on the data item having an "id" property uniquely identifying it. - */ - function DataView(options) { - var self = this; - - var defaults = { - groupItemMetadataProvider: null, - inlineFilters: false - }; - - - // private - var idProperty = "id"; // property holding a unique row id - var items = []; // data by index - var rows = []; // data by row - var idxById = {}; // indexes by id - var rowsById = null; // rows by id; lazy-calculated - var filter = null; // filter function - var updated = null; // updated item ids - var suspend = false; // suspends the recalculation - var sortAsc = true; - var fastSortField; - var sortComparer; - var refreshHints = {}; - var prevRefreshHints = {}; - var filterArgs; - var filteredItems = []; - var compiledFilter; - var compiledFilterWithCaching; - var filterCache = []; - - // grouping - var groupingInfoDefaults = { - getter: null, - formatter: null, - comparer: function(a, b) { return a.value - b.value; }, - predefinedValues: [], - aggregators: [], - aggregateEmpty: false, - aggregateCollapsed: false, - aggregateChildGroups: false, - collapsed: false, - displayTotalsRow: true, - lazyTotalsCalculation: false - }; - var groupingInfos = []; - var groups = []; - var toggledGroupsByLevel = []; - var groupingDelimiter = ':|:'; - - var pagesize = 0; - var pagenum = 0; - var totalRows = 0; - - // events - var onRowCountChanged = new Slick.Event(); - var onRowsChanged = new Slick.Event(); - var onPagingInfoChanged = new Slick.Event(); - - options = $.extend(true, {}, defaults, options); - - - function beginUpdate() { - suspend = true; - } - - function endUpdate() { - suspend = false; - refresh(); - } - - function setRefreshHints(hints) { - refreshHints = hints; - } - - function setFilterArgs(args) { - filterArgs = args; - } - - function updateIdxById(startingIndex) { - startingIndex = startingIndex || 0; - var id; - for (var i = startingIndex, l = items.length; i < l; i++) { - id = items[i][idProperty]; - if (id === undefined) { - throw "Each data element must implement a unique 'id' property"; - } - idxById[id] = i; - } - } - - function ensureIdUniqueness() { - var id; - for (var i = 0, l = items.length; i < l; i++) { - id = items[i][idProperty]; - if (id === undefined || idxById[id] !== i) { - throw "Each data element must implement a unique 'id' property"; - } - } - } - - function getItems() { - return items; - } - - function setItems(data, objectIdProperty) { - if (objectIdProperty !== undefined) { - idProperty = objectIdProperty; - } - items = filteredItems = data; - idxById = {}; - updateIdxById(); - ensureIdUniqueness(); - refresh(); - } - - function setPagingOptions(args) { - if (args.pageSize != undefined) { - pagesize = args.pageSize; - pagenum = pagesize ? Math.min(pagenum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)) : 0; - } - - if (args.pageNum != undefined) { - pagenum = Math.min(args.pageNum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)); - } - - onPagingInfoChanged.notify(getPagingInfo(), null, self); - - refresh(); - } - - function getPagingInfo() { - var totalPages = pagesize ? Math.max(1, Math.ceil(totalRows / pagesize)) : 1; - return {pageSize: pagesize, pageNum: pagenum, totalRows: totalRows, totalPages: totalPages}; - } - - function sort(comparer, ascending) { - sortAsc = ascending; - sortComparer = comparer; - fastSortField = null; - if (ascending === false) { - items.reverse(); - } - items.sort(comparer); - if (ascending === false) { - items.reverse(); - } - idxById = {}; - updateIdxById(); - refresh(); - } - - /*** - * Provides a workaround for the extremely slow sorting in IE. - * Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString - * to return the value of that field and then doing a native Array.sort(). - */ - function fastSort(field, ascending) { - sortAsc = ascending; - fastSortField = field; - sortComparer = null; - var oldToString = Object.prototype.toString; - Object.prototype.toString = (typeof field == "function") ? field : function () { - return this[field] - }; - // an extra reversal for descending sort keeps the sort stable - // (assuming a stable native sort implementation, which isn't true in some cases) - if (ascending === false) { - items.reverse(); - } - items.sort(); - Object.prototype.toString = oldToString; - if (ascending === false) { - items.reverse(); - } - idxById = {}; - updateIdxById(); - refresh(); - } - - function reSort() { - if (sortComparer) { - sort(sortComparer, sortAsc); - } else if (fastSortField) { - fastSort(fastSortField, sortAsc); - } - } - - function setFilter(filterFn) { - filter = filterFn; - if (options.inlineFilters) { - compiledFilter = compileFilter(); - compiledFilterWithCaching = compileFilterWithCaching(); - } - refresh(); - } - - function getGrouping() { - return groupingInfos; - } - - function setGrouping(groupingInfo) { - if (!options.groupItemMetadataProvider) { - options.groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider(); - } - - groups = []; - toggledGroupsByLevel = []; - groupingInfo = groupingInfo || []; - groupingInfos = (groupingInfo instanceof Array) ? groupingInfo : [groupingInfo]; - - for (var i = 0; i < groupingInfos.length; i++) { - var gi = groupingInfos[i] = $.extend(true, {}, groupingInfoDefaults, groupingInfos[i]); - gi.getterIsAFn = typeof gi.getter === "function"; - - // pre-compile accumulator loops - gi.compiledAccumulators = []; - var idx = gi.aggregators.length; - while (idx--) { - gi.compiledAccumulators[idx] = compileAccumulatorLoop(gi.aggregators[idx]); - } - - toggledGroupsByLevel[i] = {}; - } - - refresh(); - } - - /** - * @deprecated Please use {@link setGrouping}. - */ - function groupBy(valueGetter, valueFormatter, sortComparer) { - if (valueGetter == null) { - setGrouping([]); - return; - } - - setGrouping({ - getter: valueGetter, - formatter: valueFormatter, - comparer: sortComparer - }); - } - - /** - * @deprecated Please use {@link setGrouping}. - */ - function setAggregators(groupAggregators, includeCollapsed) { - if (!groupingInfos.length) { - throw new Error("At least one grouping must be specified before calling setAggregators()."); - } - - groupingInfos[0].aggregators = groupAggregators; - groupingInfos[0].aggregateCollapsed = includeCollapsed; - - setGrouping(groupingInfos); - } - - function getItemByIdx(i) { - return items[i]; - } - - function getIdxById(id) { - return idxById[id]; - } - - function ensureRowsByIdCache() { - if (!rowsById) { - rowsById = {}; - for (var i = 0, l = rows.length; i < l; i++) { - rowsById[rows[i][idProperty]] = i; - } - } - } - - function getRowById(id) { - ensureRowsByIdCache(); - return rowsById[id]; - } - - function getItemById(id) { - return items[idxById[id]]; - } - - function mapIdsToRows(idArray) { - var rows = []; - ensureRowsByIdCache(); - for (var i = 0, l = idArray.length; i < l; i++) { - var row = rowsById[idArray[i]]; - if (row != null) { - rows[rows.length] = row; - } - } - return rows; - } - - function mapRowsToIds(rowArray) { - var ids = []; - for (var i = 0, l = rowArray.length; i < l; i++) { - if (rowArray[i] < rows.length) { - ids[ids.length] = rows[rowArray[i]][idProperty]; - } - } - return ids; - } - - function updateItem(id, item) { - if (idxById[id] === undefined || id !== item[idProperty]) { - throw "Invalid or non-matching id"; - } - items[idxById[id]] = item; - if (!updated) { - updated = {}; - } - updated[id] = true; - refresh(); - } - - function insertItem(insertBefore, item) { - items.splice(insertBefore, 0, item); - updateIdxById(insertBefore); - refresh(); - } - - function addItem(item) { - items.push(item); - updateIdxById(items.length - 1); - refresh(); - } - - function deleteItem(id) { - var idx = idxById[id]; - if (idx === undefined) { - throw "Invalid id"; - } - delete idxById[id]; - items.splice(idx, 1); - updateIdxById(idx); - refresh(); - } - - function getLength() { - return rows.length; - } - - function getItem(i) { - var item = rows[i]; - - // if this is a group row, make sure totals are calculated and update the title - if (item && item.__group && item.totals && !item.totals.initialized) { - var gi = groupingInfos[item.level]; - if (!gi.displayTotalsRow) { - calculateTotals(item.totals); - item.title = gi.formatter ? gi.formatter(item) : item.value; - } - } - // if this is a totals row, make sure it's calculated - else if (item && item.__groupTotals && !item.initialized) { - calculateTotals(item); - } - - return item; - } - - function getItemMetadata(i) { - var item = rows[i]; - if (item === undefined) { - return null; - } - - // overrides for grouping rows - if (item.__group) { - return options.groupItemMetadataProvider.getGroupRowMetadata(item); - } - - // overrides for totals rows - if (item.__groupTotals) { - return options.groupItemMetadataProvider.getTotalsRowMetadata(item); - } - - return null; - } - - function expandCollapseAllGroups(level, collapse) { - if (level == null) { - for (var i = 0; i < groupingInfos.length; i++) { - toggledGroupsByLevel[i] = {}; - groupingInfos[i].collapsed = collapse; - } - } else { - toggledGroupsByLevel[level] = {}; - groupingInfos[level].collapsed = collapse; - } - refresh(); - } - - /** - * @param level {Number} Optional level to collapse. If not specified, applies to all levels. - */ - function collapseAllGroups(level) { - expandCollapseAllGroups(level, true); - } - - /** - * @param level {Number} Optional level to expand. If not specified, applies to all levels. - */ - function expandAllGroups(level) { - expandCollapseAllGroups(level, false); - } - - function expandCollapseGroup(level, groupingKey, collapse) { - toggledGroupsByLevel[level][groupingKey] = groupingInfos[level].collapsed ^ collapse; - refresh(); - } - - /** - * @param varArgs Either a Slick.Group's "groupingKey" property, or a - * variable argument list of grouping values denoting a unique path to the row. For - * example, calling collapseGroup('high', '10%') will collapse the '10%' subgroup of - * the 'high' group. - */ - function collapseGroup(varArgs) { - var args = Array.prototype.slice.call(arguments); - var arg0 = args[0]; - if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) { - expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, true); - } else { - expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), true); - } - } - - /** - * @param varArgs Either a Slick.Group's "groupingKey" property, or a - * variable argument list of grouping values denoting a unique path to the row. For - * example, calling expandGroup('high', '10%') will expand the '10%' subgroup of - * the 'high' group. - */ - function expandGroup(varArgs) { - var args = Array.prototype.slice.call(arguments); - var arg0 = args[0]; - if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) { - expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, false); - } else { - expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), false); - } - } - - function getGroups() { - return groups; - } - - function extractGroups(rows, parentGroup) { - var group; - var val; - var groups = []; - var groupsByVal = {}; - var r; - var level = parentGroup ? parentGroup.level + 1 : 0; - var gi = groupingInfos[level]; - - for (var i = 0, l = gi.predefinedValues.length; i < l; i++) { - val = gi.predefinedValues[i]; - group = groupsByVal[val]; - if (!group) { - group = new Slick.Group(); - group.value = val; - group.level = level; - group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val; - groups[groups.length] = group; - groupsByVal[val] = group; - } - } - - for (var i = 0, l = rows.length; i < l; i++) { - r = rows[i]; - val = gi.getterIsAFn ? gi.getter(r) : r[gi.getter]; - group = groupsByVal[val]; - if (!group) { - group = new Slick.Group(); - group.value = val; - group.level = level; - group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val; - groups[groups.length] = group; - groupsByVal[val] = group; - } - - group.rows[group.count++] = r; - } - - if (level < groupingInfos.length - 1) { - for (var i = 0; i < groups.length; i++) { - group = groups[i]; - group.groups = extractGroups(group.rows, group); - } - } - - groups.sort(groupingInfos[level].comparer); - - return groups; - } - - function calculateTotals(totals) { - var group = totals.group; - var gi = groupingInfos[group.level]; - var isLeafLevel = (group.level == groupingInfos.length); - var agg, idx = gi.aggregators.length; - - if (!isLeafLevel && gi.aggregateChildGroups) { - // make sure all the subgroups are calculated - var i = group.groups.length; - while (i--) { - if (!group.groups[i].initialized) { - calculateTotals(group.groups[i]); - } - } - } - - while (idx--) { - agg = gi.aggregators[idx]; - agg.init(); - if (!isLeafLevel && gi.aggregateChildGroups) { - gi.compiledAccumulators[idx].call(agg, group.groups); - } else { - gi.compiledAccumulators[idx].call(agg, group.rows); - } - agg.storeResult(totals); - } - totals.initialized = true; - } - - function addGroupTotals(group) { - var gi = groupingInfos[group.level]; - var totals = new Slick.GroupTotals(); - totals.group = group; - group.totals = totals; - if (!gi.lazyTotalsCalculation) { - calculateTotals(totals); - } - } - - function addTotals(groups, level) { - level = level || 0; - var gi = groupingInfos[level]; - var groupCollapsed = gi.collapsed; - var toggledGroups = toggledGroupsByLevel[level]; - var idx = groups.length, g; - while (idx--) { - g = groups[idx]; - - if (g.collapsed && !gi.aggregateCollapsed) { - continue; - } - - // Do a depth-first aggregation so that parent group aggregators can access subgroup totals. - if (g.groups) { - addTotals(g.groups, level + 1); - } - - if (gi.aggregators.length && ( - gi.aggregateEmpty || g.rows.length || (g.groups && g.groups.length))) { - addGroupTotals(g); - } - - g.collapsed = groupCollapsed ^ toggledGroups[g.groupingKey]; - g.title = gi.formatter ? gi.formatter(g) : g.value; - } - } - - function flattenGroupedRows(groups, level) { - level = level || 0; - var gi = groupingInfos[level]; - var groupedRows = [], rows, gl = 0, g; - for (var i = 0, l = groups.length; i < l; i++) { - g = groups[i]; - groupedRows[gl++] = g; - - if (!g.collapsed) { - rows = g.groups ? flattenGroupedRows(g.groups, level + 1) : g.rows; - for (var j = 0, jj = rows.length; j < jj; j++) { - groupedRows[gl++] = rows[j]; - } - } - - if (g.totals && gi.displayTotalsRow && (!g.collapsed || gi.aggregateCollapsed)) { - groupedRows[gl++] = g.totals; - } - } - return groupedRows; - } - - function getFunctionInfo(fn) { - var fnRegex = /^function[^(]*\(([^)]*)\)\s*{([\s\S]*)}$/; - var matches = fn.toString().match(fnRegex); - return { - params: matches[1].split(","), - body: matches[2] - }; - } - - function compileAccumulatorLoop(aggregator) { - var accumulatorInfo = getFunctionInfo(aggregator.accumulate); - var fn = new Function( - "_items", - "for (var " + accumulatorInfo.params[0] + ", _i=0, _il=_items.length; _i<_il; _i++) {" + - accumulatorInfo.params[0] + " = _items[_i]; " + - accumulatorInfo.body + - "}" - ); - fn.displayName = "compiledAccumulatorLoop"; - return fn; - } - - function compileFilter() { - var filterInfo = getFunctionInfo(filter); - - var filterBody = filterInfo.body - .replace(/return false\s*([;}]|$)/gi, "{ continue _coreloop; }$1") - .replace(/return true\s*([;}]|$)/gi, "{ _retval[_idx++] = $item$; continue _coreloop; }$1") - .replace(/return ([^;}]+?)\s*([;}]|$)/gi, - "{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }$2"); - - // This preserves the function template code after JS compression, - // so that replace() commands still work as expected. - var tpl = [ - //"function(_items, _args) { ", - "var _retval = [], _idx = 0; ", - "var $item$, $args$ = _args; ", - "_coreloop: ", - "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ", - "$item$ = _items[_i]; ", - "$filter$; ", - "} ", - "return _retval; " - //"}" - ].join(""); - tpl = tpl.replace(/\$filter\$/gi, filterBody); - tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]); - tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]); - - var fn = new Function("_items,_args", tpl); - fn.displayName = "compiledFilter"; - return fn; - } - - function compileFilterWithCaching() { - var filterInfo = getFunctionInfo(filter); - - var filterBody = filterInfo.body - .replace(/return false\s*([;}]|$)/gi, "{ continue _coreloop; }$1") - .replace(/return true\s*([;}]|$)/gi, "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }$1") - .replace(/return ([^;}]+?)\s*([;}]|$)/gi, - "{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }$2"); - - // This preserves the function template code after JS compression, - // so that replace() commands still work as expected. - var tpl = [ - //"function(_items, _args, _cache) { ", - "var _retval = [], _idx = 0; ", - "var $item$, $args$ = _args; ", - "_coreloop: ", - "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ", - "$item$ = _items[_i]; ", - "if (_cache[_i]) { ", - "_retval[_idx++] = $item$; ", - "continue _coreloop; ", - "} ", - "$filter$; ", - "} ", - "return _retval; " - //"}" - ].join(""); - tpl = tpl.replace(/\$filter\$/gi, filterBody); - tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]); - tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]); - - var fn = new Function("_items,_args,_cache", tpl); - fn.displayName = "compiledFilterWithCaching"; - return fn; - } - - function uncompiledFilter(items, args) { - var retval = [], idx = 0; - - for (var i = 0, ii = items.length; i < ii; i++) { - if (filter(items[i], args)) { - retval[idx++] = items[i]; - } - } - - return retval; - } - - function uncompiledFilterWithCaching(items, args, cache) { - var retval = [], idx = 0, item; - - for (var i = 0, ii = items.length; i < ii; i++) { - item = items[i]; - if (cache[i]) { - retval[idx++] = item; - } else if (filter(item, args)) { - retval[idx++] = item; - cache[i] = true; - } - } - - return retval; - } - - function getFilteredAndPagedItems(items) { - if (filter) { - var batchFilter = options.inlineFilters ? compiledFilter : uncompiledFilter; - var batchFilterWithCaching = options.inlineFilters ? compiledFilterWithCaching : uncompiledFilterWithCaching; - - if (refreshHints.isFilterNarrowing) { - filteredItems = batchFilter(filteredItems, filterArgs); - } else if (refreshHints.isFilterExpanding) { - filteredItems = batchFilterWithCaching(items, filterArgs, filterCache); - } else if (!refreshHints.isFilterUnchanged) { - filteredItems = batchFilter(items, filterArgs); - } - } else { - // special case: if not filtering and not paging, the resulting - // rows collection needs to be a copy so that changes due to sort - // can be caught - filteredItems = pagesize ? items : items.concat(); - } - - // get the current page - var paged; - if (pagesize) { - if (filteredItems.length < pagenum * pagesize) { - pagenum = Math.floor(filteredItems.length / pagesize); - } - paged = filteredItems.slice(pagesize * pagenum, pagesize * pagenum + pagesize); - } else { - paged = filteredItems; - } - - return {totalRows: filteredItems.length, rows: paged}; - } - - function getRowDiffs(rows, newRows) { - var item, r, eitherIsNonData, diff = []; - var from = 0, to = newRows.length; - - if (refreshHints && refreshHints.ignoreDiffsBefore) { - from = Math.max(0, - Math.min(newRows.length, refreshHints.ignoreDiffsBefore)); - } - - if (refreshHints && refreshHints.ignoreDiffsAfter) { - to = Math.min(newRows.length, - Math.max(0, refreshHints.ignoreDiffsAfter)); - } - - for (var i = from, rl = rows.length; i < to; i++) { - if (i >= rl) { - diff[diff.length] = i; - } else { - item = newRows[i]; - r = rows[i]; - - if ((groupingInfos.length && (eitherIsNonData = (item.__nonDataRow) || (r.__nonDataRow)) && - item.__group !== r.__group || - item.__group && !item.equals(r)) - || (eitherIsNonData && - // no good way to compare totals since they are arbitrary DTOs - // deep object comparison is pretty expensive - // always considering them 'dirty' seems easier for the time being - (item.__groupTotals || r.__groupTotals)) - || item[idProperty] != r[idProperty] - || (updated && updated[item[idProperty]]) - ) { - diff[diff.length] = i; - } - } - } - return diff; - } - - function recalc(_items) { - rowsById = null; - - if (refreshHints.isFilterNarrowing != prevRefreshHints.isFilterNarrowing || - refreshHints.isFilterExpanding != prevRefreshHints.isFilterExpanding) { - filterCache = []; - } - - var filteredItems = getFilteredAndPagedItems(_items); - totalRows = filteredItems.totalRows; - var newRows = filteredItems.rows; - - groups = []; - if (groupingInfos.length) { - groups = extractGroups(newRows); - if (groups.length) { - addTotals(groups); - newRows = flattenGroupedRows(groups); - } - } - - var diff = getRowDiffs(rows, newRows); - - rows = newRows; - - return diff; - } - - function refresh() { - if (suspend) { - return; - } - - var countBefore = rows.length; - var totalRowsBefore = totalRows; - - var diff = recalc(items, filter); // pass as direct refs to avoid closure perf hit - - // if the current page is no longer valid, go to last page and recalc - // we suffer a performance penalty here, but the main loop (recalc) remains highly optimized - if (pagesize && totalRows < pagenum * pagesize) { - pagenum = Math.max(0, Math.ceil(totalRows / pagesize) - 1); - diff = recalc(items, filter); - } - - updated = null; - prevRefreshHints = refreshHints; - refreshHints = {}; - - if (totalRowsBefore != totalRows) { - onPagingInfoChanged.notify(getPagingInfo(), null, self); - } - if (countBefore != rows.length) { - onRowCountChanged.notify({previous: countBefore, current: rows.length}, null, self); - } - if (diff.length > 0) { - onRowsChanged.notify({rows: diff}, null, self); - } - } - - /*** - * Wires the grid and the DataView together to keep row selection tied to item ids. - * This is useful since, without it, the grid only knows about rows, so if the items - * move around, the same rows stay selected instead of the selection moving along - * with the items. - * - * NOTE: This doesn't work with cell selection model. - * - * @param grid {Slick.Grid} The grid to sync selection with. - * @param preserveHidden {Boolean} Whether to keep selected items that go out of the - * view due to them getting filtered out. - * @param preserveHiddenOnSelectionChange {Boolean} Whether to keep selected items - * that are currently out of the view (see preserveHidden) as selected when selection - * changes. - * @return {Slick.Event} An event that notifies when an internal list of selected row ids - * changes. This is useful since, in combination with the above two options, it allows - * access to the full list selected row ids, and not just the ones visible to the grid. - * @method syncGridSelection - */ - function syncGridSelection(grid, preserveHidden, preserveHiddenOnSelectionChange) { - var self = this; - var inHandler; - var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows()); - var onSelectedRowIdsChanged = new Slick.Event(); - - function setSelectedRowIds(rowIds) { - if (selectedRowIds.join(",") == rowIds.join(",")) { - return; - } - - selectedRowIds = rowIds; - - onSelectedRowIdsChanged.notify({ - "grid": grid, - "ids": selectedRowIds - }, new Slick.EventData(), self); - } - - function update() { - if (selectedRowIds.length > 0) { - inHandler = true; - var selectedRows = self.mapIdsToRows(selectedRowIds); - if (!preserveHidden) { - setSelectedRowIds(self.mapRowsToIds(selectedRows)); - } - grid.setSelectedRows(selectedRows); - inHandler = false; - } - } - - grid.onSelectedRowsChanged.subscribe(function(e, args) { - if (inHandler) { return; } - var newSelectedRowIds = self.mapRowsToIds(grid.getSelectedRows()); - if (!preserveHiddenOnSelectionChange || !grid.getOptions().multiSelect) { - setSelectedRowIds(newSelectedRowIds); - } else { - // keep the ones that are hidden - var existing = $.grep(selectedRowIds, function(id) { return self.getRowById(id) === undefined; }); - // add the newly selected ones - setSelectedRowIds(existing.concat(newSelectedRowIds)); - } - }); - - this.onRowsChanged.subscribe(update); - - this.onRowCountChanged.subscribe(update); - - return onSelectedRowIdsChanged; - } - - function syncGridCellCssStyles(grid, key) { - var hashById; - var inHandler; - - // since this method can be called after the cell styles have been set, - // get the existing ones right away - storeCellCssStyles(grid.getCellCssStyles(key)); - - function storeCellCssStyles(hash) { - hashById = {}; - for (var row in hash) { - var id = rows[row][idProperty]; - hashById[id] = hash[row]; - } - } - - function update() { - if (hashById) { - inHandler = true; - ensureRowsByIdCache(); - var newHash = {}; - for (var id in hashById) { - var row = rowsById[id]; - if (row != undefined) { - newHash[row] = hashById[id]; - } - } - grid.setCellCssStyles(key, newHash); - inHandler = false; - } - } - - grid.onCellCssStylesChanged.subscribe(function(e, args) { - if (inHandler) { return; } - if (key != args.key) { return; } - if (args.hash) { - storeCellCssStyles(args.hash); - } - }); - - this.onRowsChanged.subscribe(update); - - this.onRowCountChanged.subscribe(update); - } - - $.extend(this, { - // methods - "beginUpdate": beginUpdate, - "endUpdate": endUpdate, - "setPagingOptions": setPagingOptions, - "getPagingInfo": getPagingInfo, - "getItems": getItems, - "setItems": setItems, - "setFilter": setFilter, - "sort": sort, - "fastSort": fastSort, - "reSort": reSort, - "setGrouping": setGrouping, - "getGrouping": getGrouping, - "groupBy": groupBy, - "setAggregators": setAggregators, - "collapseAllGroups": collapseAllGroups, - "expandAllGroups": expandAllGroups, - "collapseGroup": collapseGroup, - "expandGroup": expandGroup, - "getGroups": getGroups, - "getIdxById": getIdxById, - "getRowById": getRowById, - "getItemById": getItemById, - "getItemByIdx": getItemByIdx, - "mapRowsToIds": mapRowsToIds, - "mapIdsToRows": mapIdsToRows, - "setRefreshHints": setRefreshHints, - "setFilterArgs": setFilterArgs, - "refresh": refresh, - "updateItem": updateItem, - "insertItem": insertItem, - "addItem": addItem, - "deleteItem": deleteItem, - "syncGridSelection": syncGridSelection, - "syncGridCellCssStyles": syncGridCellCssStyles, - - // data provider methods - "getLength": getLength, - "getItem": getItem, - "getItemMetadata": getItemMetadata, - - // events - "onRowCountChanged": onRowCountChanged, - "onRowsChanged": onRowsChanged, - "onPagingInfoChanged": onPagingInfoChanged - }); - } - - function AvgAggregator(field) { - this.field_ = field; - - this.init = function () { - this.count_ = 0; - this.nonNullCount_ = 0; - this.sum_ = 0; - }; - - this.accumulate = function (item) { - var val = item[this.field_]; - this.count_++; - if (val != null && val !== "" && val !== NaN) { - this.nonNullCount_++; - this.sum_ += parseFloat(val); - } - }; - - this.storeResult = function (groupTotals) { - if (!groupTotals.avg) { - groupTotals.avg = {}; - } - if (this.nonNullCount_ != 0) { - groupTotals.avg[this.field_] = this.sum_ / this.nonNullCount_; - } - }; - } - - function MinAggregator(field) { - this.field_ = field; - - this.init = function () { - this.min_ = null; - }; - - this.accumulate = function (item) { - var val = item[this.field_]; - if (val != null && val !== "" && val !== NaN) { - if (this.min_ == null || val < this.min_) { - this.min_ = val; - } - } - }; - - this.storeResult = function (groupTotals) { - if (!groupTotals.min) { - groupTotals.min = {}; - } - groupTotals.min[this.field_] = this.min_; - } - } - - function MaxAggregator(field) { - this.field_ = field; - - this.init = function () { - this.max_ = null; - }; - - this.accumulate = function (item) { - var val = item[this.field_]; - if (val != null && val !== "" && val !== NaN) { - if (this.max_ == null || val > this.max_) { - this.max_ = val; - } - } - }; - - this.storeResult = function (groupTotals) { - if (!groupTotals.max) { - groupTotals.max = {}; - } - groupTotals.max[this.field_] = this.max_; - } - } - - function SumAggregator(field) { - this.field_ = field; - - this.init = function () { - this.sum_ = null; - }; - - this.accumulate = function (item) { - var val = item[this.field_]; - if (val != null && val !== "" && val !== NaN) { - this.sum_ += parseFloat(val); - } - }; - - this.storeResult = function (groupTotals) { - if (!groupTotals.sum) { - groupTotals.sum = {}; - } - groupTotals.sum[this.field_] = this.sum_; - } - } - - // TODO: add more built-in aggregators - // TODO: merge common aggregators in one to prevent needles iterating - -})(jQuery); diff --git a/frappe/public/js/lib/slickgrid/slick.editors.js b/frappe/public/js/lib/slickgrid/slick.editors.js deleted file mode 100644 index 4ef21e6e54..0000000000 --- a/frappe/public/js/lib/slickgrid/slick.editors.js +++ /dev/null @@ -1,512 +0,0 @@ -/*** - * Contains basic SlickGrid editors. - * @module Editors - * @namespace Slick - */ - -(function ($) { - // register namespace - $.extend(true, window, { - "Slick": { - "Editors": { - "Text": TextEditor, - "Integer": IntegerEditor, - "Date": DateEditor, - "YesNoSelect": YesNoSelectEditor, - "Checkbox": CheckboxEditor, - "PercentComplete": PercentCompleteEditor, - "LongText": LongTextEditor - } - } - }); - - function TextEditor(args) { - var $input; - var defaultValue; - var scope = this; - - this.init = function () { - $input = $("") - .appendTo(args.container) - .bind("keydown.nav", function (e) { - if (e.keyCode === frappe.ui.keyCode.LEFT || e.keyCode === frappe.ui.keyCode.RIGHT) { - e.stopImmediatePropagation(); - } - }) - .focus() - .select(); - }; - - this.destroy = function () { - $input.remove(); - }; - - this.focus = function () { - $input.focus(); - }; - - this.getValue = function () { - return $input.val(); - }; - - this.setValue = function (val) { - $input.val(val); - }; - - this.loadValue = function (item) { - defaultValue = item[args.column.field] || ""; - $input.val(defaultValue); - $input[0].defaultValue = defaultValue; - $input.select(); - }; - - this.serializeValue = function () { - return $input.val(); - }; - - this.applyValue = function (item, state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function () { - return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); - }; - - this.validate = function () { - if (args.column.validator) { - var validationResults = args.column.validator($input.val()); - if (!validationResults.valid) { - return validationResults; - } - } - - return { - valid: true, - msg: null - }; - }; - - this.init(); - } - - function IntegerEditor(args) { - var $input; - var defaultValue; - var scope = this; - - this.init = function () { - $input = $(""); - - $input.bind("keydown.nav", function (e) { - if (e.keyCode === frappe.ui.keyCode.LEFT || e.keyCode === frappe.ui.keyCode.RIGHT) { - e.stopImmediatePropagation(); - } - }); - - $input.appendTo(args.container); - $input.focus().select(); - }; - - this.destroy = function () { - $input.remove(); - }; - - this.focus = function () { - $input.focus(); - }; - - this.loadValue = function (item) { - defaultValue = item[args.column.field]; - $input.val(defaultValue); - $input[0].defaultValue = defaultValue; - $input.select(); - }; - - this.serializeValue = function () { - return parseInt($input.val(), 10) || 0; - }; - - this.applyValue = function (item, state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function () { - return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); - }; - - this.validate = function () { - if (isNaN($input.val())) { - return { - valid: false, - msg: "Please enter a valid integer" - }; - } - - return { - valid: true, - msg: null - }; - }; - - this.init(); - } - - function DateEditor(args) { - var $input; - var defaultValue; - var scope = this; - var calendarOpen = false; - - this.init = function () { - $input = $(""); - $input.appendTo(args.container); - $input.focus().select(); - $input.datepicker({ - showOn: "button", - buttonImageOnly: true, - buttonImage: "../images/calendar.gif", - beforeShow: function () { - calendarOpen = true - }, - onClose: function () { - calendarOpen = false - } - }); - $input.width($input.width() - 18); - }; - - this.destroy = function () { - $.datepicker.dpDiv.stop(true, true); - $input.datepicker("hide"); - $input.datepicker("destroy"); - $input.remove(); - }; - - this.show = function () { - if (calendarOpen) { - $.datepicker.dpDiv.stop(true, true).show(); - } - }; - - this.hide = function () { - if (calendarOpen) { - $.datepicker.dpDiv.stop(true, true).hide(); - } - }; - - this.position = function (position) { - if (!calendarOpen) { - return; - } - $.datepicker.dpDiv - .css("top", position.top + 30) - .css("left", position.left); - }; - - this.focus = function () { - $input.focus(); - }; - - this.loadValue = function (item) { - defaultValue = item[args.column.field]; - $input.val(defaultValue); - $input[0].defaultValue = defaultValue; - $input.select(); - }; - - this.serializeValue = function () { - return $input.val(); - }; - - this.applyValue = function (item, state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function () { - return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue); - }; - - this.validate = function () { - return { - valid: true, - msg: null - }; - }; - - this.init(); - } - - function YesNoSelectEditor(args) { - var $select; - var defaultValue; - var scope = this; - - this.init = function () { - $select = $(""); - $select.appendTo(args.container); - $select.focus(); - }; - - this.destroy = function () { - $select.remove(); - }; - - this.focus = function () { - $select.focus(); - }; - - this.loadValue = function (item) { - $select.val((defaultValue = item[args.column.field]) ? "yes" : "no"); - $select.select(); - }; - - this.serializeValue = function () { - return ($select.val() == "yes"); - }; - - this.applyValue = function (item, state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function () { - return ($select.val() != defaultValue); - }; - - this.validate = function () { - return { - valid: true, - msg: null - }; - }; - - this.init(); - } - - function CheckboxEditor(args) { - var $select; - var defaultValue; - var scope = this; - - this.init = function () { - $select = $(""); - $select.appendTo(args.container); - $select.focus(); - }; - - this.destroy = function () { - $select.remove(); - }; - - this.focus = function () { - $select.focus(); - }; - - this.loadValue = function (item) { - defaultValue = !!item[args.column.field]; - if (defaultValue) { - $select.prop('checked', true); - } else { - $select.prop('checked', false); - } - }; - - this.serializeValue = function () { - return $select.prop('checked'); - }; - - this.applyValue = function (item, state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function () { - return (this.serializeValue() !== defaultValue); - }; - - this.validate = function () { - return { - valid: true, - msg: null - }; - }; - - this.init(); - } - - function PercentCompleteEditor(args) { - var $input, $picker; - var defaultValue; - var scope = this; - - this.init = function () { - $input = $(""); - $input.width($(args.container).innerWidth() - 25); - $input.appendTo(args.container); - - $picker = $("

").appendTo(args.container); - $picker.append("
"); - - $picker.find(".editor-percentcomplete-buttons").append("

"); - - $input.focus().select(); - - $picker.find(".editor-percentcomplete-slider").slider({ - orientation: "vertical", - range: "min", - value: defaultValue, - slide: function (event, ui) { - $input.val(ui.value) - } - }); - - $picker.find(".editor-percentcomplete-buttons button").bind("click", function (e) { - $input.val($(this).attr("val")); - $picker.find(".editor-percentcomplete-slider").slider("value", $(this).attr("val")); - }) - }; - - this.destroy = function () { - $input.remove(); - $picker.remove(); - }; - - this.focus = function () { - $input.focus(); - }; - - this.loadValue = function (item) { - $input.val(defaultValue = item[args.column.field]); - $input.select(); - }; - - this.serializeValue = function () { - return parseInt($input.val(), 10) || 0; - }; - - this.applyValue = function (item, state) { - item[args.column.field] = state; - }; - - this.isValueChanged = function () { - return (!($input.val() == "" && defaultValue == null)) && ((parseInt($input.val(), 10) || 0) != defaultValue); - }; - - this.validate = function () { - if (isNaN(parseInt($input.val(), 10))) { - return { - valid: false, - msg: "Please enter a valid positive number" - }; - } - - return { - valid: true, - msg: null - }; - }; - - this.init(); - } - - /* - * An example of a "detached" editor. - * The UI is added onto document BODY and .position(), .show() and .hide() are implemented. - * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter. - */ - function LongTextEditor(args) { - var $input, $wrapper; - var defaultValue; - var scope = this; - - this.init = function () { - var $container = $("body"); - - $wrapper = $("
") - .appendTo($container); - - $input = $("