diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000000..4faece896a
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,12 @@
+# Since version 2.23 (released in August 2019), git-blame has a feature
+# to ignore or bypass certain commits.
+#
+# This file contains a list of commits that are not likely what you
+# are looking for in a blame, such as mass reformatting or renaming.
+# You can set this file as a default ignore file for blame by running
+# the following command.
+#
+# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
+
+# Replace use of Class.extend with native JS class
+fe20515c23a3ac41f1092bf0eaf0a0a452ec2e85
diff --git a/.github/workflows/publish-assets-develop.yml b/.github/workflows/publish-assets-develop.yml
index 2a934a6795..a23885b508 100644
--- a/.github/workflows/publish-assets-develop.yml
+++ b/.github/workflows/publish-assets-develop.yml
@@ -15,11 +15,11 @@ jobs:
path: 'frappe'
- uses: actions/setup-node@v1
with:
- python-version: '12.x'
+ node-version: 14
- uses: actions/setup-python@v2
with:
python-version: '3.6'
- - name: Set up bench for current push
+ - name: Set up bench and build assets
run: |
npm install -g yarn
pip3 install -U frappe-bench
@@ -29,7 +29,7 @@ jobs:
- name: Package assets
run: |
mkdir -p $GITHUB_WORKSPACE/build
- tar -cvpzf $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz ./frappe-bench/sites/assets/js ./frappe-bench/sites/assets/css
+ tar -cvpzf $GITHUB_WORKSPACE/build/$GITHUB_SHA.tar.gz ./frappe-bench/sites/assets/frappe/dist
- name: Publish assets to S3
uses: jakejarvis/s3-sync-action@master
diff --git a/.github/workflows/publish-assets-releases.yml b/.github/workflows/publish-assets-releases.yml
index e86f884f35..a697517c23 100644
--- a/.github/workflows/publish-assets-releases.yml
+++ b/.github/workflows/publish-assets-releases.yml
@@ -22,7 +22,7 @@ jobs:
- uses: actions/setup-python@v2
with:
python-version: '3.6'
- - name: Set up bench for current push
+ - name: Set up bench and build assets
run: |
npm install -g yarn
pip3 install -U frappe-bench
@@ -32,7 +32,7 @@ jobs:
- name: Package assets
run: |
mkdir -p $GITHUB_WORKSPACE/build
- tar -cvpzf $GITHUB_WORKSPACE/build/assets.tar.gz ./frappe-bench/sites/assets/js ./frappe-bench/sites/assets/css
+ tar -cvpzf $GITHUB_WORKSPACE/build/assets.tar.gz ./frappe-bench/sites/assets/frappe/dist
- name: Get release
id: get_release
diff --git a/.github/workflows/server-mariadb-tests.yml b/.github/workflows/server-mariadb-tests.yml
index 075b76e8a1..1742e813c6 100644
--- a/.github/workflows/server-mariadb-tests.yml
+++ b/.github/workflows/server-mariadb-tests.yml
@@ -35,7 +35,7 @@ jobs:
- uses: actions/setup-node@v2
with:
- node-version: '14'
+ node-version: 14
check-latest: true
- name: Add to Hosts
diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml
index 9eea128cd1..d9ccb07da0 100644
--- a/.github/workflows/ui-tests.yml
+++ b/.github/workflows/ui-tests.yml
@@ -35,7 +35,7 @@ jobs:
- uses: actions/setup-node@v2
with:
- node-version: '12'
+ node-version: 14
check-latest: true
- name: Add to Hosts
diff --git a/.gitignore b/.gitignore
index 766288fe2e..1ff3122d70 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@ locale
dist/
# build/
frappe/docs/current
+frappe/public/dist
.vscode
node_modules
.kdev4/
diff --git a/cypress/integration/recorder.js b/cypress/integration/recorder.js
index d30cc3568c..5b7692d8ff 100644
--- a/cypress/integration/recorder.js
+++ b/cypress/integration/recorder.js
@@ -50,7 +50,7 @@ context('Recorder', () => {
cy.get('.result-list').should('contain', '/api/method/frappe.desk.reportview.get');
});
- it.only('Recorder View Request', () => {
+ it('Recorder View Request', () => {
cy.get('.primary-action').should('contain', 'Start').click();
cy.visit('/app/List/DocType/List');
diff --git a/esbuild/esbuild.js b/esbuild/esbuild.js
new file mode 100644
index 0000000000..ecf0d49511
--- /dev/null
+++ b/esbuild/esbuild.js
@@ -0,0 +1,486 @@
+/* eslint-disable no-console */
+let path = require("path");
+let fs = require("fs");
+let glob = require("fast-glob");
+let esbuild = require("esbuild");
+let vue = require("esbuild-vue");
+let yargs = require("yargs");
+let cliui = require("cliui")();
+let chalk = require("chalk");
+let html_plugin = require("./frappe-html");
+let postCssPlugin = require("esbuild-plugin-postcss2").default;
+let ignore_assets = require("./ignore-assets");
+let sass_options = require("./sass_options");
+let {
+ app_list,
+ assets_path,
+ apps_path,
+ sites_path,
+ get_app_path,
+ get_public_path,
+ log,
+ log_warn,
+ log_error,
+ bench_path,
+ get_redis_subscriber
+} = require("./utils");
+
+let argv = yargs
+ .usage("Usage: node esbuild [options]")
+ .option("apps", {
+ type: "string",
+ description: "Run build for specific apps"
+ })
+ .option("skip_frappe", {
+ type: "boolean",
+ description: "Skip building frappe assets"
+ })
+ .option("files", {
+ type: "string",
+ description: "Run build for specified bundles"
+ })
+ .option("watch", {
+ type: "boolean",
+ description: "Run in watch mode and rebuild on file changes"
+ })
+ .option("production", {
+ type: "boolean",
+ description: "Run build in production mode"
+ })
+ .option("run-build-command", {
+ type: "boolean",
+ description: "Run build command for apps"
+ })
+ .example(
+ "node esbuild --apps frappe,erpnext",
+ "Run build only for frappe and erpnext"
+ )
+ .example(
+ "node esbuild --files frappe/website.bundle.js,frappe/desk.bundle.js",
+ "Run build only for specified bundles"
+ )
+ .version(false).argv;
+
+const APPS = (!argv.apps ? app_list : argv.apps.split(",")).filter(
+ app => !(argv.skip_frappe && app == "frappe")
+);
+const FILES_TO_BUILD = argv.files ? argv.files.split(",") : [];
+const WATCH_MODE = Boolean(argv.watch);
+const PRODUCTION = Boolean(argv.production);
+const RUN_BUILD_COMMAND = !WATCH_MODE && Boolean(argv["run-build-command"]);
+
+const TOTAL_BUILD_TIME = `${chalk.black.bgGreen(" DONE ")} Total Build Time`;
+const NODE_PATHS = [].concat(
+ // node_modules of apps directly importable
+ app_list
+ .map(app => path.resolve(get_app_path(app), "../node_modules"))
+ .filter(fs.existsSync),
+ // import js file of any app if you provide the full path
+ app_list
+ .map(app => path.resolve(get_app_path(app), ".."))
+ .filter(fs.existsSync)
+);
+
+execute()
+ .then(() => RUN_BUILD_COMMAND && run_build_command_for_apps(APPS))
+ .catch(e => console.error(e));
+
+if (WATCH_MODE) {
+ // listen for open files in editor event
+ open_in_editor();
+}
+
+async function execute() {
+ console.time(TOTAL_BUILD_TIME);
+ if (!FILES_TO_BUILD.length) {
+ await clean_dist_folders(APPS);
+ }
+
+ let result;
+ try {
+ result = await build_assets_for_apps(APPS, FILES_TO_BUILD);
+ } catch (e) {
+ log_error("There were some problems during build");
+ log();
+ log(chalk.dim(e.stack));
+ return;
+ }
+
+ if (!WATCH_MODE) {
+ log_built_assets(result.metafile);
+ console.timeEnd(TOTAL_BUILD_TIME);
+ log();
+ } else {
+ log("Watching for changes...");
+ }
+ return await write_assets_json(result.metafile);
+}
+
+function build_assets_for_apps(apps, files) {
+ let { include_patterns, ignore_patterns } = files.length
+ ? get_files_to_build(files)
+ : get_all_files_to_build(apps);
+
+ return glob(include_patterns, { ignore: ignore_patterns }).then(files => {
+ let output_path = assets_path;
+
+ let file_map = {};
+ for (let file of files) {
+ let relative_app_path = path.relative(apps_path, file);
+ let app = relative_app_path.split(path.sep)[0];
+
+ let extension = path.extname(file);
+ let output_name = path.basename(file, extension);
+ if (
+ [".css", ".scss", ".less", ".sass", ".styl"].includes(extension)
+ ) {
+ output_name = path.join("css", output_name);
+ } else if ([".js", ".ts"].includes(extension)) {
+ output_name = path.join("js", output_name);
+ }
+ output_name = path.join(app, "dist", output_name);
+
+ if (Object.keys(file_map).includes(output_name)) {
+ log_warn(
+ `Duplicate output file ${output_name} generated from ${file}`
+ );
+ }
+
+ file_map[output_name] = file;
+ }
+
+ return build_files({
+ files: file_map,
+ outdir: output_path
+ });
+ });
+}
+
+function get_all_files_to_build(apps) {
+ let include_patterns = [];
+ let ignore_patterns = [];
+
+ for (let app of apps) {
+ let public_path = get_public_path(app);
+ include_patterns.push(
+ path.resolve(
+ public_path,
+ "**",
+ "*.bundle.{js,ts,css,sass,scss,less,styl}"
+ )
+ );
+ ignore_patterns.push(
+ path.resolve(public_path, "node_modules"),
+ path.resolve(public_path, "dist")
+ );
+ }
+
+ return {
+ include_patterns,
+ ignore_patterns
+ };
+}
+
+function get_files_to_build(files) {
+ // files: ['frappe/website.bundle.js', 'erpnext/main.bundle.js']
+ let include_patterns = [];
+ let ignore_patterns = [];
+
+ for (let file of files) {
+ let [app, bundle] = file.split("/");
+ let public_path = get_public_path(app);
+ include_patterns.push(path.resolve(public_path, "**", bundle));
+ ignore_patterns.push(
+ path.resolve(public_path, "node_modules"),
+ path.resolve(public_path, "dist")
+ );
+ }
+
+ return {
+ include_patterns,
+ ignore_patterns
+ };
+}
+
+function build_files({ files, outdir }) {
+ return esbuild.build({
+ entryPoints: files,
+ entryNames: "[dir]/[name].[hash]",
+ outdir,
+ sourcemap: true,
+ bundle: true,
+ metafile: true,
+ minify: PRODUCTION,
+ nodePaths: NODE_PATHS,
+ define: {
+ "process.env.NODE_ENV": JSON.stringify(
+ PRODUCTION ? "production" : "development"
+ )
+ },
+ plugins: [
+ html_plugin,
+ ignore_assets,
+ vue(),
+ postCssPlugin({
+ plugins: [require("autoprefixer")],
+ sassOptions: sass_options
+ })
+ ],
+ watch: get_watch_config()
+ });
+}
+
+function get_watch_config() {
+ if (WATCH_MODE) {
+ return {
+ async onRebuild(error, result) {
+ if (error) {
+ log_error("There was an error during rebuilding changes.");
+ log();
+ log(chalk.dim(error.stack));
+ notify_redis({ error });
+ } else {
+ let {
+ assets_json,
+ prev_assets_json
+ } = await write_assets_json(result.metafile);
+ if (prev_assets_json) {
+ log_rebuilt_assets(prev_assets_json, assets_json);
+ }
+ notify_redis({ success: true });
+ }
+ }
+ };
+ }
+ return null;
+}
+
+async function clean_dist_folders(apps) {
+ for (let app of apps) {
+ let public_path = get_public_path(app);
+ await fs.promises.rmdir(path.resolve(public_path, "dist", "js"), {
+ recursive: true
+ });
+ await fs.promises.rmdir(path.resolve(public_path, "dist", "css"), {
+ recursive: true
+ });
+ }
+}
+
+function log_built_assets(metafile) {
+ let column_widths = [60, 20];
+ cliui.div(
+ {
+ text: chalk.cyan.bold("File"),
+ width: column_widths[0]
+ },
+ {
+ text: chalk.cyan.bold("Size"),
+ width: column_widths[1]
+ }
+ );
+ cliui.div("");
+
+ let output_by_dist_path = {};
+ for (let outfile in metafile.outputs) {
+ if (outfile.endsWith(".map")) continue;
+ let data = metafile.outputs[outfile];
+ outfile = path.resolve(outfile);
+ outfile = path.relative(assets_path, outfile);
+ let filename = path.basename(outfile);
+ let dist_path = outfile.replace(filename, "");
+ output_by_dist_path[dist_path] = output_by_dist_path[dist_path] || [];
+ output_by_dist_path[dist_path].push({
+ name: filename,
+ size: (data.bytes / 1000).toFixed(2) + " Kb"
+ });
+ }
+
+ for (let dist_path in output_by_dist_path) {
+ let files = output_by_dist_path[dist_path];
+ cliui.div({
+ text: dist_path,
+ width: column_widths[0]
+ });
+
+ for (let i in files) {
+ let file = files[i];
+ let branch = "";
+ if (i < files.length - 1) {
+ branch = "├─ ";
+ } else {
+ branch = "└─ ";
+ }
+ let color = file.name.endsWith(".js") ? "green" : "blue";
+ cliui.div(
+ {
+ text: branch + chalk[color]("" + file.name),
+ width: column_widths[0]
+ },
+ {
+ text: file.size,
+ width: column_widths[1]
+ }
+ );
+ }
+ cliui.div("");
+ }
+ log(cliui.toString());
+}
+
+// to store previous build's assets.json for comparison
+let prev_assets_json;
+let curr_assets_json;
+
+async function write_assets_json(metafile) {
+ prev_assets_json = curr_assets_json;
+ let out = {};
+ for (let output in metafile.outputs) {
+ let info = metafile.outputs[output];
+ let asset_path = "/" + path.relative(sites_path, output);
+ if (info.entryPoint) {
+ out[path.basename(info.entryPoint)] = asset_path;
+ }
+ }
+
+ let assets_json_path = path.resolve(
+ assets_path,
+ "frappe",
+ "dist",
+ "assets.json"
+ );
+ let assets_json;
+ try {
+ assets_json = await fs.promises.readFile(assets_json_path, "utf-8");
+ } catch (error) {
+ assets_json = "{}";
+ }
+ assets_json = JSON.parse(assets_json);
+ // update with new values
+ assets_json = Object.assign({}, assets_json, out);
+ curr_assets_json = assets_json;
+
+ await fs.promises.writeFile(
+ assets_json_path,
+ JSON.stringify(assets_json, null, 4)
+ );
+ await update_assets_json_in_cache(assets_json);
+ return {
+ assets_json,
+ prev_assets_json
+ };
+}
+
+function update_assets_json_in_cache(assets_json) {
+ // update assets_json cache in redis, so that it can be read directly by python
+ return new Promise(resolve => {
+ let client = get_redis_subscriber("redis_cache");
+ // handle error event to avoid printing stack traces
+ client.on("error", _ => {
+ log_warn("Cannot connect to redis_cache to update assets_json");
+ });
+ client.set("assets_json", JSON.stringify(assets_json), err => {
+ client.unref();
+ resolve();
+ });
+ });
+}
+
+function run_build_command_for_apps(apps) {
+ let cwd = process.cwd();
+ let { execSync } = require("child_process");
+
+ for (let app of apps) {
+ if (app === "frappe") continue;
+
+ let root_app_path = path.resolve(get_app_path(app), "..");
+ let package_json = path.resolve(root_app_path, "package.json");
+ if (fs.existsSync(package_json)) {
+ let { scripts } = require(package_json);
+ if (scripts && scripts.build) {
+ log("\nRunning build command for", chalk.bold(app));
+ process.chdir(root_app_path);
+ execSync("yarn build", { encoding: "utf8", stdio: "inherit" });
+ }
+ }
+ }
+
+ process.chdir(cwd);
+}
+
+async function notify_redis({ error, success }) {
+ // notify redis which in turns tells socketio to publish this to browser
+ let subscriber = get_redis_subscriber("redis_socketio");
+ subscriber.on("error", _ => {
+ log_warn("Cannot connect to redis_socketio for browser events");
+ });
+
+ let payload = null;
+ if (error) {
+ let formatted = await esbuild.formatMessages(error.errors, {
+ kind: "error",
+ terminalWidth: 100
+ });
+ let stack = error.stack.replace(new RegExp(bench_path, "g"), "");
+ payload = {
+ error,
+ formatted,
+ stack
+ };
+ }
+ if (success) {
+ payload = {
+ success: true
+ };
+ }
+
+ subscriber.publish(
+ "events",
+ JSON.stringify({
+ event: "build_event",
+ message: payload
+ })
+ );
+}
+
+function open_in_editor() {
+ let subscriber = get_redis_subscriber("redis_socketio");
+ subscriber.on("error", _ => {
+ log_warn("Cannot connect to redis_socketio for open_in_editor events");
+ });
+ subscriber.on("message", (event, file) => {
+ if (event === "open_in_editor") {
+ file = JSON.parse(file);
+ let file_path = path.resolve(file.file);
+ log("Opening file in editor:", file_path);
+ let launch = require("launch-editor");
+ launch(`${file_path}:${file.line}:${file.column}`);
+ }
+ });
+ subscriber.subscribe("open_in_editor");
+}
+
+function log_rebuilt_assets(prev_assets, new_assets) {
+ let added_files = [];
+ let old_files = Object.values(prev_assets);
+ let new_files = Object.values(new_assets);
+
+ for (let filepath of new_files) {
+ if (!old_files.includes(filepath)) {
+ added_files.push(filepath);
+ }
+ }
+
+ log(
+ chalk.yellow(
+ `${new Date().toLocaleTimeString()}: Compiled ${
+ added_files.length
+ } files...`
+ )
+ );
+ for (let filepath of added_files) {
+ let filename = path.basename(filepath);
+ log(" " + filename);
+ }
+ log();
+}
diff --git a/esbuild/frappe-html.js b/esbuild/frappe-html.js
new file mode 100644
index 0000000000..8c4b7ca3d7
--- /dev/null
+++ b/esbuild/frappe-html.js
@@ -0,0 +1,43 @@
+module.exports = {
+ name: "frappe-html",
+ setup(build) {
+ let path = require("path");
+ let fs = require("fs/promises");
+
+ build.onResolve({ filter: /\.html$/ }, args => {
+ return {
+ path: path.join(args.resolveDir, args.path),
+ namespace: "frappe-html"
+ };
+ });
+
+ build.onLoad({ filter: /.*/, namespace: "frappe-html" }, args => {
+ let filepath = args.path;
+ let filename = path.basename(filepath).split(".")[0];
+
+ return fs
+ .readFile(filepath, "utf-8")
+ .then(content => {
+ content = scrub_html_template(content);
+ return {
+ contents: `\n\tfrappe.templates['${filename}'] = \`${content}\`;\n`
+ };
+ })
+ .catch(() => {
+ return {
+ contents: "",
+ warnings: [
+ {
+ text: `There was an error importing ${filepath}`
+ }
+ ]
+ };
+ });
+ });
+ }
+};
+
+function scrub_html_template(content) {
+ content = content.replace(/`/g, "\\`");
+ return content;
+}
diff --git a/esbuild/ignore-assets.js b/esbuild/ignore-assets.js
new file mode 100644
index 0000000000..5edfef2110
--- /dev/null
+++ b/esbuild/ignore-assets.js
@@ -0,0 +1,11 @@
+module.exports = {
+ name: "frappe-ignore-asset",
+ setup(build) {
+ build.onResolve({ filter: /^\/assets\// }, args => {
+ return {
+ path: args.path,
+ external: true
+ };
+ });
+ }
+};
diff --git a/esbuild/index.js b/esbuild/index.js
new file mode 100644
index 0000000000..2721673702
--- /dev/null
+++ b/esbuild/index.js
@@ -0,0 +1 @@
+require("./esbuild");
diff --git a/esbuild/sass_options.js b/esbuild/sass_options.js
new file mode 100644
index 0000000000..fcc7e04ccd
--- /dev/null
+++ b/esbuild/sass_options.js
@@ -0,0 +1,29 @@
+let path = require("path");
+let { get_app_path, app_list } = require("./utils");
+
+let node_modules_path = path.resolve(
+ get_app_path("frappe"),
+ "..",
+ "node_modules"
+);
+let app_paths = app_list
+ .map(get_app_path)
+ .map(app_path => path.resolve(app_path, ".."));
+
+module.exports = {
+ includePaths: [node_modules_path, ...app_paths],
+ importer: function(url) {
+ if (url.startsWith("~")) {
+ // strip ~ so that it can resolve from node_modules
+ url = url.slice(1);
+ }
+ if (url.endsWith(".css")) {
+ // strip .css from end of path
+ url = url.slice(0, -4);
+ }
+ // normal file, let it go
+ return {
+ file: url
+ };
+ }
+};
diff --git a/esbuild/utils.js b/esbuild/utils.js
new file mode 100644
index 0000000000..82490adb36
--- /dev/null
+++ b/esbuild/utils.js
@@ -0,0 +1,145 @@
+const path = require("path");
+const fs = require("fs");
+const chalk = require("chalk");
+
+const frappe_path = path.resolve(__dirname, "..");
+const bench_path = path.resolve(frappe_path, "..", "..");
+const sites_path = path.resolve(bench_path, "sites");
+const apps_path = path.resolve(bench_path, "apps");
+const assets_path = path.resolve(sites_path, "assets");
+const app_list = get_apps_list();
+
+const app_paths = app_list.reduce((out, app) => {
+ out[app] = path.resolve(apps_path, app, app);
+ return out;
+}, {});
+const public_paths = app_list.reduce((out, app) => {
+ out[app] = path.resolve(app_paths[app], "public");
+ return out;
+}, {});
+const public_js_paths = app_list.reduce((out, app) => {
+ out[app] = path.resolve(app_paths[app], "public/js");
+ return out;
+}, {});
+
+const bundle_map = app_list.reduce((out, app) => {
+ const public_js_path = public_js_paths[app];
+ if (fs.existsSync(public_js_path)) {
+ const all_files = fs.readdirSync(public_js_path);
+ const js_files = all_files.filter(file => file.endsWith(".js"));
+
+ for (let js_file of js_files) {
+ const filename = path.basename(js_file).split(".")[0];
+ out[path.join(app, "js", filename)] = path.resolve(
+ public_js_path,
+ js_file
+ );
+ }
+ }
+
+ return out;
+}, {});
+
+const get_public_path = app => public_paths[app];
+
+const get_build_json_path = app =>
+ path.resolve(get_public_path(app), "build.json");
+
+function get_build_json(app) {
+ try {
+ return require(get_build_json_path(app));
+ } catch (e) {
+ // build.json does not exist
+ return null;
+ }
+}
+
+function delete_file(path) {
+ if (fs.existsSync(path)) {
+ fs.unlinkSync(path);
+ }
+}
+
+function run_serially(tasks) {
+ let result = Promise.resolve();
+ tasks.forEach(task => {
+ if (task) {
+ result = result.then ? result.then(task) : Promise.resolve();
+ }
+ });
+ return result;
+}
+
+const get_app_path = app => app_paths[app];
+
+function get_apps_list() {
+ return fs
+ .readFileSync(path.resolve(sites_path, "apps.txt"), {
+ encoding: "utf-8"
+ })
+ .split("\n")
+ .filter(Boolean);
+}
+
+function get_cli_arg(name) {
+ let args = process.argv.slice(2);
+ let arg = `--${name}`;
+ let index = args.indexOf(arg);
+
+ let value = null;
+ if (index != -1) {
+ value = true;
+ }
+ if (value && args[index + 1]) {
+ value = args[index + 1];
+ }
+ return value;
+}
+
+function log_error(message, badge = "ERROR") {
+ badge = chalk.white.bgRed(` ${badge} `);
+ console.error(`${badge} ${message}`); // eslint-disable-line no-console
+}
+
+function log_warn(message, badge = "WARN") {
+ badge = chalk.black.bgYellowBright(` ${badge} `);
+ console.warn(`${badge} ${message}`); // eslint-disable-line no-console
+}
+
+function log(...args) {
+ console.log(...args); // eslint-disable-line no-console
+}
+
+function get_redis_subscriber(kind) {
+ // get redis subscriber that aborts after 10 connection attempts
+ let { get_redis_subscriber: get_redis } = require("../node_utils");
+ return get_redis(kind, {
+ retry_strategy: function(options) {
+ // abort after 10 connection attempts
+ if (options.attempt > 10) {
+ return undefined;
+ }
+ return Math.min(options.attempt * 100, 2000);
+ }
+ });
+}
+
+module.exports = {
+ app_list,
+ bench_path,
+ assets_path,
+ sites_path,
+ apps_path,
+ bundle_map,
+ get_public_path,
+ get_build_json_path,
+ get_build_json,
+ get_app_path,
+ delete_file,
+ run_serially,
+ get_cli_arg,
+ log,
+ log_warn,
+ log_error,
+ get_redis_subscriber
+};
diff --git a/frappe/__init__.py b/frappe/__init__.py
index 02b8d71e40..9b208f7c2d 100644
--- a/frappe/__init__.py
+++ b/frappe/__init__.py
@@ -10,9 +10,16 @@ be used to build database driven apps.
Read the documentation: https://frappeframework.com/docs
"""
+import os, warnings
+
+_dev_server = os.environ.get('DEV_SERVER', False)
+
+if _dev_server:
+ warnings.simplefilter('always', DeprecationWarning)
+ warnings.simplefilter('always', PendingDeprecationWarning)
from werkzeug.local import Local, release_local
-import os, sys, importlib, inspect, json, warnings
+import sys, importlib, inspect, json
import typing
from past.builtins import cmp
import click
@@ -31,8 +38,6 @@ __title__ = "Frappe Framework"
local = Local()
controllers = {}
-warnings.simplefilter('always', DeprecationWarning)
-warnings.simplefilter('always', PendingDeprecationWarning)
class _dict(dict):
"""dict like object that exposes keys as attributes"""
@@ -197,7 +202,7 @@ def init(site, sites_path=None, new_site=False):
local.meta_cache = {}
local.form_dict = _dict()
local.session = _dict()
- local.dev_server = os.environ.get('DEV_SERVER', False)
+ local.dev_server = _dev_server
setup_module_map()
diff --git a/frappe/build.py b/frappe/build.py
index 321a9bf734..c970ae3a28 100644
--- a/frappe/build.py
+++ b/frappe/build.py
@@ -5,6 +5,7 @@ import os
import re
import json
import shutil
+import subprocess
from tempfile import mkdtemp, mktemp
from distutils.spawn import find_executable
@@ -15,6 +16,7 @@ import click
import psutil
from urllib.parse import urlparse
from simple_chalk import green
+from semantic_version import Version
timestamps = {}
@@ -36,35 +38,36 @@ def download_file(url, prefix):
def build_missing_files():
- # check which files dont exist yet from the build.json and tell build.js to build only those!
+ '''Check which files dont exist yet from the assets.json and run build for those files'''
+
missing_assets = []
current_asset_files = []
- frappe_build = os.path.join("..", "apps", "frappe", "frappe", "public", "build.json")
for type in ["css", "js"]:
- current_asset_files.extend(
- [
- "{0}/{1}".format(type, name)
- for name in os.listdir(os.path.join(sites_path, "assets", type))
- ]
- )
+ folder = os.path.join(sites_path, "assets", "frappe", "dist", type)
+ current_asset_files.extend(os.listdir(folder))
- with open(frappe_build) as f:
- all_asset_files = json.load(f).keys()
+ development = frappe.local.conf.developer_mode or frappe.local.dev_server
+ build_mode = "development" if development else "production"
- for asset in all_asset_files:
- if asset.replace("concat:", "") not in current_asset_files:
- missing_assets.append(asset)
+ assets_json = frappe.read_file(frappe.get_app_path('frappe', 'public', 'dist', 'assets.json'))
+ if assets_json:
+ assets_json = frappe.parse_json(assets_json)
- if missing_assets:
- from subprocess import check_call
- from shlex import split
+ for bundle_file, output_file in assets_json.items():
+ if not output_file.startswith('/assets/frappe'):
+ continue
- click.secho("\nBuilding missing assets...\n", fg="yellow")
- command = split(
- "node rollup/build.js --files {0} --no-concat".format(",".join(missing_assets))
- )
- check_call(command, cwd=os.path.join("..", "apps", "frappe"))
+ if os.path.basename(output_file) not in current_asset_files:
+ missing_assets.append(bundle_file)
+
+ if missing_assets:
+ click.secho("\nBuilding missing assets...\n", fg="yellow")
+ files_to_build = ["frappe/" + name for name in missing_assets]
+ bundle(build_mode, files=files_to_build)
+ else:
+ # no assets.json, run full build
+ bundle(build_mode, apps="frappe")
def get_assets_link(frappe_head):
@@ -200,49 +203,51 @@ def setup():
assets_path = os.path.join(frappe.local.sites_path, "assets")
-def get_node_pacman():
- exec_ = find_executable("yarn")
- if exec_:
- return exec_
- raise ValueError("Yarn not found")
-
-
-def bundle(no_compress, app=None, hard_link=False, verbose=False, skip_frappe=False):
+def bundle(mode, apps=None, hard_link=False, make_copy=False, restore=False, verbose=False, skip_frappe=False, files=None):
"""concat / minify js files"""
setup()
make_asset_dirs(hard_link=hard_link)
- pacman = get_node_pacman()
- mode = "build" if no_compress else "production"
- command = "{pacman} run {mode}".format(pacman=pacman, mode=mode)
+ mode = "production" if mode == "production" else "build"
+ command = "yarn run {mode}".format(mode=mode)
- if app:
- command += " --app {app}".format(app=app)
+ if apps:
+ command += " --apps {apps}".format(apps=apps)
if skip_frappe:
command += " --skip_frappe"
- frappe_app_path = os.path.abspath(os.path.join(app_paths[0], ".."))
- check_yarn()
+ if files:
+ command += " --files {files}".format(files=','.join(files))
+
+ command += " --run-build-command"
+
+ check_node_executable()
+ frappe_app_path = frappe.get_app_path("frappe", "..")
frappe.commands.popen(command, cwd=frappe_app_path, env=get_node_env())
-def watch(no_compress):
+def watch(apps=None):
"""watch and rebuild if necessary"""
setup()
- pacman = get_node_pacman()
+ command = "yarn run watch"
+ if apps:
+ command += " --apps {apps}".format(apps=apps)
- frappe_app_path = os.path.abspath(os.path.join(app_paths[0], ".."))
- check_yarn()
+ check_node_executable()
frappe_app_path = frappe.get_app_path("frappe", "..")
- frappe.commands.popen("{pacman} run watch".format(pacman=pacman),
- cwd=frappe_app_path, env=get_node_env())
+ frappe.commands.popen(command, cwd=frappe_app_path, env=get_node_env())
-def check_yarn():
+def check_node_executable():
+ node_version = Version(subprocess.getoutput('node -v')[1:])
+ warn = '⚠️ '
+ if node_version.major < 14:
+ click.echo(f"{warn} Please update your node version to 14")
if not find_executable("yarn"):
- print("Please install yarn using below command and try again.\nnpm install -g yarn")
+ click.echo(f"{warn} Please install yarn using below command and try again.\nnpm install -g yarn")
+ click.echo()
def get_node_env():
node_env = {
@@ -312,13 +317,20 @@ def clear_broken_symlinks():
-def unstrip(message):
+def unstrip(message: str) -> str:
+ """Pads input string on the right side until the last available column in the terminal
+ """
+ _len = len(message)
try:
max_str = os.get_terminal_size().columns
except Exception:
max_str = 80
- _len = len(message)
- _rem = max_str - _len
+
+ if _len < max_str:
+ _rem = max_str - _len
+ else:
+ _rem = max_str % _len
+
return f"{message}{' ' * _rem}"
@@ -331,6 +343,7 @@ def make_asset_dirs(hard_link=False):
start_message = unstrip(f"{'Copying assets from' if hard_link else 'Linking'} {source} to {target}")
fail_message = unstrip(f"Cannot {'copy' if hard_link else 'link'} {source} to {target}")
+ # Used '\r' instead of '\x1b[1K\r' to print entire lines in smaller terminal sizes
try:
print(start_message, end="\r")
link_assets_dir(source, target, hard_link=hard_link)
diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py
index 4e0fe0cf44..7330c83102 100644
--- a/frappe/cache_manager.py
+++ b/frappe/cache_manager.py
@@ -13,6 +13,8 @@ common_default_keys = ["__default", "__global"]
doctype_map_keys = ('energy_point_rule_map', 'assignment_rule_map',
'milestone_tracker_map', 'event_consumer_document_type_map')
+bench_cache_keys = ('assets_json',)
+
global_cache_keys = ("app_hooks", "installed_apps", 'all_apps',
"app_modules", "module_app", "system_settings",
'scheduler_events', 'time_zone', 'webhooks', 'active_domains',
@@ -58,6 +60,7 @@ def clear_global_cache():
clear_doctype_cache()
clear_website_cache()
frappe.cache().delete_value(global_cache_keys)
+ frappe.cache().delete_value(bench_cache_keys)
frappe.setup_module_map()
def clear_defaults_cache(user=None):
diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py
index 1ee2a7ec00..4da0f6bb78 100644
--- a/frappe/commands/utils.py
+++ b/frappe/commands/utils.py
@@ -16,24 +16,34 @@ from frappe.utils import get_bench_path, update_progress_bar, cint
@click.command('build')
@click.option('--app', help='Build assets for app')
+@click.option('--apps', help='Build assets for specific apps')
@click.option('--hard-link', is_flag=True, default=False, help='Copy the files instead of symlinking')
@click.option('--make-copy', is_flag=True, default=False, help='[DEPRECATED] Copy the files instead of symlinking')
@click.option('--restore', is_flag=True, default=False, help='[DEPRECATED] Copy the files instead of symlinking with force')
+@click.option('--production', is_flag=True, default=False, help='Build assets in production mode')
@click.option('--verbose', is_flag=True, default=False, help='Verbose')
@click.option('--force', is_flag=True, default=False, help='Force build assets instead of downloading available')
-def build(app=None, hard_link=False, make_copy=False, restore=False, verbose=False, force=False):
- "Minify + concatenate JS and CSS files, build translations"
+def build(app=None, apps=None, hard_link=False, make_copy=False, restore=False, production=False, verbose=False, force=False):
+ "Compile JS and CSS source files"
+ from frappe.build import bundle, download_frappe_assets
frappe.init('')
- # don't minify in developer_mode for faster builds
- no_compress = frappe.local.conf.developer_mode or False
+
+ if not apps and app:
+ apps = app
# dont try downloading assets if force used, app specified or running via CI
- if not (force or app or os.environ.get('CI')):
+ if not (force or apps or os.environ.get('CI')):
# skip building frappe if assets exist remotely
- skip_frappe = frappe.build.download_frappe_assets(verbose=verbose)
+ skip_frappe = download_frappe_assets(verbose=verbose)
else:
skip_frappe = False
+ # don't minify in developer_mode for faster builds
+ development = frappe.local.conf.developer_mode or frappe.local.dev_server
+ mode = "development" if development else "production"
+ if production:
+ mode = "production"
+
if make_copy or restore:
hard_link = make_copy or restore
click.secho(
@@ -41,21 +51,17 @@ def build(app=None, hard_link=False, make_copy=False, restore=False, verbose=Fal
fg="yellow",
)
- frappe.build.bundle(
- skip_frappe=skip_frappe,
- no_compress=no_compress,
- hard_link=hard_link,
- verbose=verbose,
- app=app,
- )
+ bundle(mode, apps=apps, hard_link=hard_link, verbose=verbose, skip_frappe=skip_frappe)
+
@click.command('watch')
-def watch():
- "Watch and concatenate JS and CSS files as and when they change"
- import frappe.build
+@click.option('--apps', help='Watch assets for specific apps')
+def watch(apps=None):
+ "Watch and compile JS and CSS files as and when they change"
+ from frappe.build import watch
frappe.init('')
- frappe.build.watch(True)
+ watch(apps)
@click.command('clear-cache')
@@ -501,8 +507,6 @@ frappe.db.connect()
@pass_context
def console(context):
"Start ipython console for a site"
- import warnings
-
site = get_site(context)
frappe.init(site=site)
frappe.connect()
@@ -523,7 +527,6 @@ def console(context):
if failed_to_import:
print("\nFailed to import:\n{}".format(", ".join(failed_to_import)))
- warnings.simplefilter('ignore')
IPython.embed(display_banner="", header="", colors="neutral")
diff --git a/frappe/core/doctype/data_import/data_import.js b/frappe/core/doctype/data_import/data_import.js
index e03c22a898..079bdaa09c 100644
--- a/frappe/core/doctype/data_import/data_import.js
+++ b/frappe/core/doctype/data_import/data_import.js
@@ -203,7 +203,7 @@ frappe.ui.form.on('Data Import', {
},
download_template(frm) {
- frappe.require('/assets/js/data_import_tools.min.js', () => {
+ frappe.require('data_import_tools.bundle.js', () => {
frm.data_exporter = new frappe.data_import.DataExporter(
frm.doc.reference_doctype,
frm.doc.import_type
@@ -287,7 +287,7 @@ frappe.ui.form.on('Data Import', {
return;
}
- frappe.require('/assets/js/data_import_tools.min.js', () => {
+ frappe.require('data_import_tools.bundle.js', () => {
frm.import_preview = new frappe.data_import.ImportPreview({
wrapper: frm.get_field('import_preview').$wrapper,
doctype: frm.doc.reference_doctype,
diff --git a/frappe/core/page/recorder/recorder.js b/frappe/core/page/recorder/recorder.js
index fdca93e8b9..f1f74daf71 100644
--- a/frappe/core/page/recorder/recorder.js
+++ b/frappe/core/page/recorder/recorder.js
@@ -11,7 +11,7 @@ frappe.pages['recorder'].on_page_load = function(wrapper) {
frappe.recorder.show();
});
- frappe.require('/assets/js/frappe-recorder.min.js');
+ frappe.require('recorder.bundle.js');
};
class Recorder {
diff --git a/frappe/desk/page/activity/activity.js b/frappe/desk/page/activity/activity.js
index 39de414122..7b4e8ddc1a 100644
--- a/frappe/desk/page/activity/activity.js
+++ b/frappe/desk/page/activity/activity.js
@@ -67,8 +67,8 @@ frappe.pages['activity'].on_page_show = function () {
}
frappe.activity.last_feed_date = false;
-frappe.activity.Feed = Class.extend({
- init: function (row, data) {
+frappe.activity.Feed = class Feed {
+ constructor(row, data) {
this.scrub_data(data);
this.add_date_separator(row, data);
if (!data.add_class)
@@ -97,8 +97,9 @@ frappe.activity.Feed = Class.extend({
$(row)
.append(frappe.render_template("activity_row", data))
.find("a").addClass("grey");
- },
- scrub_data: function (data) {
+ }
+
+ scrub_data(data) {
data.by = frappe.user.full_name(data.owner);
data.avatar = frappe.avatar(data.owner);
@@ -113,9 +114,9 @@ frappe.activity.Feed = Class.extend({
data.when = comment_when(data.creation);
data.feed_type = data.comment_type || data.communication_medium;
- },
+ }
- add_date_separator: function (row, data) {
+ add_date_separator(row, data) {
var date = frappe.datetime.str_to_obj(data.creation);
var last = frappe.activity.last_feed_date;
@@ -137,7 +138,7 @@ frappe.activity.Feed = Class.extend({
}
frappe.activity.last_feed_date = date;
}
-});
+};
frappe.activity.render_heatmap = function (page) {
$('
\
diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py
index 45888119ea..3b03c42b95 100755
--- a/frappe/email/email_body.py
+++ b/frappe/email/email_body.py
@@ -292,18 +292,12 @@ def inline_style_in_html(html):
''' Convert email.css and html to inline-styled html
'''
from premailer import Premailer
+ from frappe.utils.jinja_globals import bundled_asset
- apps = frappe.get_installed_apps()
-
- # add frappe email css file
- css_files = ['assets/css/email.css']
- if 'frappe' in apps:
- apps.remove('frappe')
-
- for app in apps:
- path = 'assets/{0}/css/email.css'.format(app)
- css_files.append(path)
-
+ # get email css files from hooks
+ css_files = frappe.get_hooks('email_css')
+ css_files = [bundled_asset(path) for path in css_files]
+ css_files = [path.lstrip('/') for path in css_files]
css_files = [css_file for css_file in css_files if os.path.exists(os.path.abspath(css_file))]
p = Premailer(html=html, external_styles=css_files, strip_important=False)
diff --git a/frappe/hooks.py b/frappe/hooks.py
index b999d2cd95..d0968ce051 100644
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -29,16 +29,16 @@ page_js = {
# website
app_include_js = [
- "/assets/js/libs.min.js",
- "/assets/js/desk.min.js",
- "/assets/js/list.min.js",
- "/assets/js/form.min.js",
- "/assets/js/control.min.js",
- "/assets/js/report.min.js",
+ "libs.bundle.js",
+ "desk.bundle.js",
+ "list.bundle.js",
+ "form.bundle.js",
+ "controls.bundle.js",
+ "report.bundle.js",
]
app_include_css = [
- "/assets/css/desk.min.css",
- "/assets/css/report.min.css",
+ "desk.bundle.css",
+ "report.bundle.css",
]
doctype_js = {
@@ -52,6 +52,8 @@ web_include_js = [
web_include_css = []
+email_css = ['email.bundle.css']
+
website_route_rules = [
{"from_route": "/blog/
", "to_route": "Blog Post"},
{"from_route": "/kb/", "to_route": "Help Article"},
diff --git a/frappe/printing/page/print/print.js b/frappe/printing/page/print/print.js
index dfd93c4efa..233bbe0ce7 100644
--- a/frappe/printing/page/print/print.js
+++ b/frappe/printing/page/print/print.js
@@ -408,14 +408,17 @@ frappe.ui.form.PrintView = class {
setup_print_format_dom(out, $print_format) {
this.print_wrapper.find('.print-format-skeleton').remove();
+ let base_url = frappe.urllib.get_base_url();
+ let print_css = frappe.assets.bundled_asset('print.bundle.css');
this.$print_format_body.find('head').html(
`
- `
+ `
);
if (frappe.utils.is_rtl(this.lang_code)) {
+ let rtl_css = frappe.assets.bundled_asset('frappe-rtl.bundle.css');
this.$print_format_body.find('head').append(
- ``
+ ``
);
}
diff --git a/frappe/printing/page/print_format_builder/print_format_builder.js b/frappe/printing/page/print_format_builder/print_format_builder.js
index 7e58e295b5..ca2a8bc378 100644
--- a/frappe/printing/page/print_format_builder/print_format_builder.js
+++ b/frappe/printing/page/print_format_builder/print_format_builder.js
@@ -23,13 +23,13 @@ frappe.pages['print-format-builder'].on_page_show = function(wrapper) {
}
}
-frappe.PrintFormatBuilder = Class.extend({
- init: function(parent) {
+frappe.PrintFormatBuilder = class PrintFormatBuilder {
+ constructor(parent) {
this.parent = parent;
this.make();
this.refresh();
- },
- refresh: function() {
+ }
+ refresh() {
this.custom_html_count = 0;
if(!this.print_format) {
this.show_start();
@@ -37,8 +37,8 @@ frappe.PrintFormatBuilder = Class.extend({
this.page.set_title(this.print_format.name);
this.setup_print_format();
}
- },
- make: function() {
+ }
+ make() {
this.page = frappe.ui.make_app_page({
parent: this.parent,
title: __("Print Format Builder"),
@@ -56,15 +56,15 @@ frappe.PrintFormatBuilder = Class.extend({
this.setup_edit_custom_html();
// $(this.page.sidebar).css({"position": 'fixed'});
// $(this.page.main).parent().css({"margin-left": '16.67%'});
- },
- show_start: function() {
+ }
+ show_start() {
this.page.main.html(frappe.render_template("print_format_builder_start", {}));
this.page.clear_actions();
this.page.set_title(__("Print Format Builder"));
this.start_edit_print_format();
this.start_new_print_format();
- },
- start_edit_print_format: function() {
+ }
+ start_edit_print_format() {
// print format control
var me = this;
this.print_format_input = frappe.ui.form.make_control({
@@ -89,8 +89,8 @@ frappe.PrintFormatBuilder = Class.extend({
frappe.set_route('print-format-builder', name);
});
});
- },
- start_new_print_format: function() {
+ }
+ start_new_print_format() {
var me = this;
this.doctype_input = frappe.ui.form.make_control({
parent: this.page.main.find(".doctype-selector"),
@@ -125,8 +125,8 @@ frappe.PrintFormatBuilder = Class.extend({
me.setup_new_print_format(doctype, name);
});
- },
- setup_new_print_format: function(doctype, name, based_on) {
+ }
+ setup_new_print_format(doctype, name, based_on) {
frappe.call({
method: 'frappe.printing.page.print_format_builder.print_format_builder.create_custom_format',
args: {
@@ -143,8 +143,8 @@ frappe.PrintFormatBuilder = Class.extend({
}
},
});
- },
- setup_print_format: function() {
+ }
+ setup_print_format() {
var me = this;
frappe.model.with_doctype(this.print_format.doc_type, function(doctype) {
me.meta = frappe.get_meta(me.print_format.doc_type);
@@ -163,23 +163,23 @@ frappe.PrintFormatBuilder = Class.extend({
frappe.set_route("Form", "Print Format", me.print_format.name);
});
});
- },
- setup_sidebar: function() {
+ }
+ setup_sidebar() {
// prepend custom HTML field
var fields = [this.get_custom_html_field()].concat(this.meta.fields);
this.page.sidebar.html(
$(frappe.render_template("print_format_builder_sidebar", {fields: fields}))
);
this.setup_field_filter();
- },
- get_custom_html_field: function() {
+ }
+ get_custom_html_field() {
return {
fieldtype: "Custom HTML",
fieldname: "_custom_html",
label: __("Custom HTML")
- }
- },
- render_layout: function() {
+ };
+ }
+ render_layout() {
this.page.main.empty();
this.prepare_data();
$(frappe.render_template("print_format_builder_layout", {
@@ -190,8 +190,8 @@ frappe.PrintFormatBuilder = Class.extend({
this.setup_edit_heading();
this.setup_field_settings();
this.setup_html_data();
- },
- prepare_data: function() {
+ }
+ prepare_data() {
this.print_heading_template = null;
this.data = JSON.parse(this.print_format.format_data || "[]");
if(!this.data.length) {
@@ -280,22 +280,22 @@ frappe.PrintFormatBuilder = Class.extend({
this.layout_data = $.map(this.layout_data, function(s) {
return s.has_fields ? s : null
});
- },
- get_new_section: function() {
+ }
+ get_new_section() {
return {columns: [], no_of_columns: 0, label:''};
- },
- get_new_column: function() {
+ }
+ get_new_column() {
return {fields: []}
- },
- add_table_properties: function(f) {
+ }
+ add_table_properties(f) {
// build table columns and widths in a dict
// visible_columns
var me = this;
if(!f.visible_columns) {
me.init_visible_columns(f);
}
- },
- init_visible_columns: function(f) {
+ }
+ init_visible_columns(f) {
f.visible_columns = []
$.each(frappe.get_meta(f.options).fields, function(i, _f) {
if(!in_list(["Section Break", "Column Break"], _f.fieldtype) &&
@@ -306,8 +306,8 @@ frappe.PrintFormatBuilder = Class.extend({
print_width: (_f.width || ""), print_hide:0});
}
});
- },
- setup_sortable: function() {
+ }
+ setup_sortable() {
var me = this;
// drag from fields library
@@ -332,8 +332,8 @@ frappe.PrintFormatBuilder = Class.extend({
Sortable.create(this.page.main.find(".print-format-builder-layout").get(0),
{ handle: ".print-format-builder-section-head" }
);
- },
- setup_sortable_for_column: function(col) {
+ }
+ setup_sortable_for_column(col) {
var me = this;
Sortable.create(col, {
group: {
@@ -363,8 +363,8 @@ frappe.PrintFormatBuilder = Class.extend({
}
});
- },
- setup_field_filter: function() {
+ }
+ setup_field_filter() {
var me = this;
this.page.sidebar.find(".filter-fields").on("keyup", function() {
var text = $(this).val();
@@ -373,8 +373,8 @@ frappe.PrintFormatBuilder = Class.extend({
$(this).parent().toggle(show);
})
});
- },
- setup_section_settings: function() {
+ }
+ setup_section_settings() {
var me = this;
this.page.main.on("click", ".section-settings", function() {
var section = $(this).parent().parent();
@@ -431,8 +431,8 @@ frappe.PrintFormatBuilder = Class.extend({
return false;
});
- },
- setup_field_settings: function() {
+ }
+ setup_field_settings() {
this.page.main.find(".field-settings").on("click", e => {
const field = $(e.currentTarget).parent();
// new dialog
@@ -482,8 +482,8 @@ frappe.PrintFormatBuilder = Class.extend({
return false;
});
- },
- setup_html_data: function() {
+ }
+ setup_html_data() {
// set JQuery `data` for Custom HTML fields, since editing the HTML
// directly causes problem becuase of HTML reformatting
//
@@ -496,8 +496,8 @@ frappe.PrintFormatBuilder = Class.extend({
var html = me.custom_html_dict[parseInt(content.attr('data-custom-html-id'))].options;
content.data('content', html);
})
- },
- update_columns_in_section: function(section, no_of_columns, new_no_of_columns) {
+ }
+ update_columns_in_section(section, no_of_columns, new_no_of_columns) {
var col_size = 12 / new_no_of_columns,
me = this,
resize = function() {
@@ -539,8 +539,8 @@ frappe.PrintFormatBuilder = Class.extend({
resize();
}
- },
- setup_add_section: function() {
+ }
+ setup_add_section() {
var me = this;
this.page.main.find(".print-format-builder-add-section").on("click", function() {
// boostrap new section info
@@ -554,8 +554,8 @@ frappe.PrintFormatBuilder = Class.extend({
me.setup_sortable_for_column($section.find(".print-format-builder-column").get(0));
});
- },
- setup_edit_heading: function() {
+ }
+ setup_edit_heading() {
var me = this;
var $heading = this.page.main.find(".print-format-builder-print-heading");
@@ -565,8 +565,8 @@ frappe.PrintFormatBuilder = Class.extend({
this.page.main.find(".edit-heading").on("click", function() {
var d = me.get_edit_html_dialog(__("Edit Heading"), __("Heading"), $heading);
})
- },
- setup_column_selector: function() {
+ }
+ setup_column_selector() {
var me = this;
this.page.main.on("click", ".select-columns", function() {
var parent = $(this).parents(".print-format-builder-field:first"),
@@ -657,24 +657,24 @@ frappe.PrintFormatBuilder = Class.extend({
return false;
});
- },
- get_visible_columns_string: function(f) {
+ }
+ get_visible_columns_string(f) {
if(!f.visible_columns) {
this.init_visible_columns(f);
}
return $.map(f.visible_columns, function(v) { return v.fieldname + "|" + (v.print_width || "") }).join(",");
- },
- get_no_content: function() {
+ }
+ get_no_content() {
return __("Edit to add content")
- },
- setup_edit_custom_html: function() {
+ }
+ setup_edit_custom_html() {
var me = this;
this.page.main.on("click", ".edit-html", function() {
me.get_edit_html_dialog(__("Edit Custom HTML"), __("Custom HTML"),
$(this).parents(".print-format-builder-field:first").find(".html-content"));
});
- },
- get_edit_html_dialog: function(title, label, $content) {
+ }
+ get_edit_html_dialog(title, label, $content) {
var me = this;
var d = new frappe.ui.Dialog({
title: title,
@@ -710,8 +710,8 @@ frappe.PrintFormatBuilder = Class.extend({
d.show();
return d;
- },
- save_print_format: function() {
+ }
+ save_print_format() {
var data = [],
me = this;
@@ -789,4 +789,4 @@ frappe.PrintFormatBuilder = Class.extend({
}
});
}
-});
+};
diff --git a/frappe/public/html/print_template.html b/frappe/public/html/print_template.html
index bdb09541c9..721bec7fa7 100644
--- a/frappe/public/html/print_template.html
+++ b/frappe/public/html/print_template.html
@@ -7,7 +7,7 @@
{{ title }}
-
+
diff --git a/frappe/public/js/barcode_scanner.bundle.js b/frappe/public/js/barcode_scanner.bundle.js
new file mode 100644
index 0000000000..294f20c08f
--- /dev/null
+++ b/frappe/public/js/barcode_scanner.bundle.js
@@ -0,0 +1 @@
+import "./frappe/barcode_scanner/quagga";
diff --git a/frappe/public/js/bootstrap-4-web.bundle.js b/frappe/public/js/bootstrap-4-web.bundle.js
new file mode 100644
index 0000000000..2e3c4d7145
--- /dev/null
+++ b/frappe/public/js/bootstrap-4-web.bundle.js
@@ -0,0 +1,64 @@
+
+// multilevel dropdown
+$('.dropdown-menu a.dropdown-toggle').on('click', function (e) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ if (!$(this).next().hasClass('show')) {
+ $(this).parents('.dropdown-menu').first().find('.show').removeClass("show");
+ }
+ var $subMenu = $(this).next(".dropdown-menu");
+ $subMenu.toggleClass('show');
+
+
+ $(this).parents('li.nav-item.dropdown.show').on('hidden.bs.dropdown', function () {
+ $('.dropdown-submenu .show').removeClass("show");
+ });
+
+ return false;
+});
+
+frappe.get_modal = function (title, content) {
+ return $(
+ ``
+ );
+};
+
+frappe.ui.Dialog = class Dialog extends frappe.ui.Dialog {
+ get_primary_btn() {
+ return this.$wrapper.find(".modal-footer .btn-primary");
+ }
+
+ set_primary_action(label, click) {
+ this.$wrapper.find('.modal-footer').removeClass('hidden');
+ return super.set_primary_action(label, click)
+ .removeClass('hidden');
+ }
+
+ make() {
+ super.make();
+ if (this.fields) {
+ this.$wrapper.find('.section-body').addClass('w-100');
+ }
+ }
+};
diff --git a/frappe/public/js/chat.bundle.js b/frappe/public/js/chat.bundle.js
new file mode 100644
index 0000000000..5f9a91ebb7
--- /dev/null
+++ b/frappe/public/js/chat.bundle.js
@@ -0,0 +1 @@
+import "./frappe/chat";
diff --git a/frappe/public/js/checkout.bundle.js b/frappe/public/js/checkout.bundle.js
new file mode 100644
index 0000000000..954e838fa8
--- /dev/null
+++ b/frappe/public/js/checkout.bundle.js
@@ -0,0 +1 @@
+import "./integrations/razorpay";
diff --git a/frappe/public/js/controls.bundle.js b/frappe/public/js/controls.bundle.js
new file mode 100644
index 0000000000..30b5d43905
--- /dev/null
+++ b/frappe/public/js/controls.bundle.js
@@ -0,0 +1,18 @@
+import "air-datepicker/dist/js/datepicker.min.js";
+import "air-datepicker/dist/js/i18n/datepicker.cs.js";
+import "air-datepicker/dist/js/i18n/datepicker.da.js";
+import "air-datepicker/dist/js/i18n/datepicker.de.js";
+import "air-datepicker/dist/js/i18n/datepicker.en.js";
+import "air-datepicker/dist/js/i18n/datepicker.es.js";
+import "air-datepicker/dist/js/i18n/datepicker.fi.js";
+import "air-datepicker/dist/js/i18n/datepicker.fr.js";
+import "air-datepicker/dist/js/i18n/datepicker.hu.js";
+import "air-datepicker/dist/js/i18n/datepicker.nl.js";
+import "air-datepicker/dist/js/i18n/datepicker.pl.js";
+import "air-datepicker/dist/js/i18n/datepicker.pt-BR.js";
+import "air-datepicker/dist/js/i18n/datepicker.pt.js";
+import "air-datepicker/dist/js/i18n/datepicker.ro.js";
+import "air-datepicker/dist/js/i18n/datepicker.sk.js";
+import "air-datepicker/dist/js/i18n/datepicker.zh.js";
+import "./frappe/ui/capture.js";
+import "./frappe/form/controls/control.js";
diff --git a/frappe/public/js/data_import_tools.bundle.js b/frappe/public/js/data_import_tools.bundle.js
new file mode 100644
index 0000000000..b6e4c11968
--- /dev/null
+++ b/frappe/public/js/data_import_tools.bundle.js
@@ -0,0 +1 @@
+import "./frappe/data_import";
diff --git a/frappe/public/js/desk.bundle.js b/frappe/public/js/desk.bundle.js
new file mode 100644
index 0000000000..66eb72cda0
--- /dev/null
+++ b/frappe/public/js/desk.bundle.js
@@ -0,0 +1,105 @@
+import "./frappe/translate.js";
+import "./frappe/class.js";
+import "./frappe/polyfill.js";
+import "./frappe/provide.js";
+import "./frappe/assets.js";
+import "./frappe/format.js";
+import "./frappe/form/formatters.js";
+import "./frappe/dom.js";
+import "./frappe/ui/messages.js";
+import "./frappe/ui/keyboard.js";
+import "./frappe/ui/colors.js";
+import "./frappe/ui/sidebar.js";
+import "./frappe/ui/link_preview.js";
+
+import "./frappe/request.js";
+import "./frappe/socketio_client.js";
+import "./frappe/utils/utils.js";
+import "./frappe/event_emitter.js";
+import "./frappe/router.js";
+import "./frappe/router_history.js";
+import "./frappe/defaults.js";
+import "./frappe/roles_editor.js";
+import "./frappe/module_editor.js";
+import "./frappe/microtemplate.js";
+
+import "./frappe/ui/page.html";
+import "./frappe/ui/page.js";
+import "./frappe/ui/slides.js";
+// import "./frappe/ui/onboarding_dialog.js";
+import "./frappe/ui/find.js";
+import "./frappe/ui/iconbar.js";
+import "./frappe/form/layout.js";
+import "./frappe/ui/field_group.js";
+import "./frappe/form/link_selector.js";
+import "./frappe/form/multi_select_dialog.js";
+import "./frappe/ui/dialog.js";
+import "./frappe/ui/capture.js";
+import "./frappe/ui/app_icon.js";
+import "./frappe/ui/theme_switcher.js";
+
+import "./frappe/model/model.js";
+import "./frappe/db.js";
+import "./frappe/model/meta.js";
+import "./frappe/model/sync.js";
+import "./frappe/model/create_new.js";
+import "./frappe/model/perm.js";
+import "./frappe/model/workflow.js";
+import "./frappe/model/user_settings.js";
+
+import "./frappe/utils/user.js";
+import "./frappe/utils/common.js";
+import "./frappe/utils/urllib.js";
+import "./frappe/utils/pretty_date.js";
+import "./frappe/utils/tools.js";
+import "./frappe/utils/datetime.js";
+import "./frappe/utils/number_format.js";
+import "./frappe/utils/help.js";
+import "./frappe/utils/help_links.js";
+import "./frappe/utils/address_and_contact.js";
+import "./frappe/utils/preview_email.js";
+import "./frappe/utils/file_manager.js";
+
+import "./frappe/upload.js";
+import "./frappe/ui/tree.js";
+
+import "./frappe/views/container.js";
+import "./frappe/views/breadcrumbs.js";
+import "./frappe/views/factory.js";
+import "./frappe/views/pageview.js";
+
+import "./frappe/ui/toolbar/awesome_bar.js";
+// import "./frappe/ui/toolbar/energy_points_notifications.js";
+import "./frappe/ui/notifications/notifications.js";
+import "./frappe/ui/toolbar/search.js";
+import "./frappe/ui/toolbar/tag_utils.js";
+import "./frappe/ui/toolbar/search.html";
+import "./frappe/ui/toolbar/search_utils.js";
+import "./frappe/ui/toolbar/about.js";
+import "./frappe/ui/toolbar/navbar.html";
+import "./frappe/ui/toolbar/toolbar.js";
+// import "./frappe/ui/toolbar/notifications.js";
+import "./frappe/views/communication.js";
+import "./frappe/views/translation_manager.js";
+import "./frappe/views/workspace/workspace.js";
+
+import "./frappe/widgets/widget_group.js";
+
+import "./frappe/ui/sort_selector.html";
+import "./frappe/ui/sort_selector.js";
+
+import "./frappe/change_log.html";
+import "./frappe/ui/workspace_loading_skeleton.html";
+import "./frappe/desk.js";
+import "./frappe/query_string.js";
+
+// import "./frappe/ui/comment.js";
+
+import "./frappe/chat.js";
+import "./frappe/utils/energy_point_utils.js";
+import "./frappe/utils/dashboard_utils.js";
+import "./frappe/ui/chart.js";
+import "./frappe/ui/datatable.js";
+import "./frappe/ui/driver.js";
+import "./frappe/ui/plyr.js";
+import "./frappe/barcode_scanner/index.js";
diff --git a/frappe/public/js/dialog.bundle.js b/frappe/public/js/dialog.bundle.js
new file mode 100644
index 0000000000..3100b42ca7
--- /dev/null
+++ b/frappe/public/js/dialog.bundle.js
@@ -0,0 +1,7 @@
+import "./frappe/dom.js";
+import "./frappe/form/formatters.js";
+import "./frappe/form/layout.js";
+import "./frappe/ui/field_group.js";
+import "./frappe/form/link_selector.js";
+import "./frappe/form/multi_select_dialog.js";
+import "./frappe/ui/dialog.js";
diff --git a/frappe/public/js/form.bundle.js b/frappe/public/js/form.bundle.js
new file mode 100644
index 0000000000..5bed5c2cb8
--- /dev/null
+++ b/frappe/public/js/form.bundle.js
@@ -0,0 +1,17 @@
+import "./frappe/form/templates/address_list.html";
+import "./frappe/form/templates/contact_list.html";
+import "./frappe/form/templates/form_dashboard.html";
+import "./frappe/form/templates/form_footer.html";
+import "./frappe/form/templates/form_links.html";
+import "./frappe/form/templates/form_sidebar.html";
+import "./frappe/form/templates/print_layout.html";
+import "./frappe/form/templates/report_links.html";
+import "./frappe/form/templates/set_sharing.html";
+import "./frappe/form/templates/timeline_message_box.html";
+import "./frappe/form/templates/users_in_sidebar.html";
+
+import "./frappe/form/controls/control.js";
+import "./frappe/views/formview.js";
+import "./frappe/form/form.js";
+import "./frappe/meta_tag.js";
+
diff --git a/frappe/public/js/frappe-web.bundle.js b/frappe/public/js/frappe-web.bundle.js
new file mode 100644
index 0000000000..9f7875f96b
--- /dev/null
+++ b/frappe/public/js/frappe-web.bundle.js
@@ -0,0 +1,26 @@
+import "./jquery-bootstrap";
+import "./frappe/class.js";
+import "./frappe/polyfill.js";
+import "./lib/md5.min.js";
+import "./frappe/provide.js";
+import "./frappe/format.js";
+import "./frappe/utils/number_format.js";
+import "./frappe/utils/utils.js";
+import "./frappe/utils/common.js";
+import "./frappe/ui/messages.js";
+import "./frappe/translate.js";
+import "./frappe/utils/pretty_date.js";
+import "./frappe/microtemplate.js";
+import "./frappe/query_string.js";
+
+import "./frappe/upload.js";
+
+import "./frappe/model/meta.js";
+import "./frappe/model/model.js";
+import "./frappe/model/perm.js";
+
+import "./bootstrap-4-web.bundle";
+
+
+import "../../website/js/website.js";
+import "./frappe/socketio_client.js";
diff --git a/frappe/public/js/frappe/assets.js b/frappe/public/js/frappe/assets.js
index 76441af235..3fca8640f3 100644
--- a/frappe/public/js/frappe/assets.js
+++ b/frappe/public/js/frappe/assets.js
@@ -9,7 +9,14 @@ frappe.require = function(items, callback) {
if(typeof items === "string") {
items = [items];
}
- frappe.assets.execute(items, callback);
+ items = items.map(item => frappe.assets.bundled_asset(item));
+
+ return new Promise(resolve => {
+ frappe.assets.execute(items, () => {
+ resolve();
+ callback && callback();
+ });
+ });
};
frappe.assets = {
@@ -160,4 +167,11 @@ frappe.assets = {
frappe.dom.set_style(txt);
}
},
+
+ bundled_asset(path) {
+ if (!path.startsWith('/assets') && path.includes('.bundle.')) {
+ return frappe.boot.assets_json[path] || path;
+ }
+ return path;
+ }
};
diff --git a/frappe/public/js/frappe/barcode_scanner/index.js b/frappe/public/js/frappe/barcode_scanner/index.js
index c5e7a7600f..fa3975b578 100644
--- a/frappe/public/js/frappe/barcode_scanner/index.js
+++ b/frappe/public/js/frappe/barcode_scanner/index.js
@@ -13,7 +13,7 @@ frappe.barcode.scan_barcode = function() {
}
}, reject);
} else {
- frappe.require('/assets/js/barcode_scanner.min.js', () => {
+ frappe.require('barcode_scanner.bundle.js', () => {
frappe.barcode.get_barcode().then(barcode => {
resolve(barcode);
});
diff --git a/frappe/public/js/frappe/build_events/BuildError.vue b/frappe/public/js/frappe/build_events/BuildError.vue
new file mode 100644
index 0000000000..6e10852719
--- /dev/null
+++ b/frappe/public/js/frappe/build_events/BuildError.vue
@@ -0,0 +1,111 @@
+
+
+
+
+
diff --git a/frappe/public/js/frappe/build_events/BuildSuccess.vue b/frappe/public/js/frappe/build_events/BuildSuccess.vue
new file mode 100644
index 0000000000..75a365fdc2
--- /dev/null
+++ b/frappe/public/js/frappe/build_events/BuildSuccess.vue
@@ -0,0 +1,52 @@
+
+
+
+
+
diff --git a/frappe/public/js/frappe/build_events/build_events.bundle.js b/frappe/public/js/frappe/build_events/build_events.bundle.js
new file mode 100644
index 0000000000..6c8986af3f
--- /dev/null
+++ b/frappe/public/js/frappe/build_events/build_events.bundle.js
@@ -0,0 +1,48 @@
+import BuildError from "./BuildError.vue";
+import BuildSuccess from "./BuildSuccess.vue";
+
+let $container = $("#build-events-overlay");
+let success = null;
+let error = null;
+
+frappe.realtime.on("build_event", data => {
+ if (data.success) {
+ show_build_success(data);
+ } else if (data.error) {
+ show_build_error(data);
+ }
+});
+
+function show_build_success() {
+ if (error) {
+ error.hide();
+ }
+ if (!success) {
+ let target = $('')
+ .appendTo($container)
+ .get(0);
+ let vm = new Vue({
+ el: target,
+ render: h => h(BuildSuccess)
+ });
+ success = vm.$children[0];
+ }
+ success.show();
+}
+
+function show_build_error(data) {
+ if (success) {
+ success.hide();
+ }
+ if (!error) {
+ let target = $('
')
+ .appendTo($container)
+ .get(0);
+ let vm = new Vue({
+ el: target,
+ render: h => h(BuildError)
+ });
+ error = vm.$children[0];
+ }
+ error.show(data);
+}
diff --git a/frappe/public/js/frappe/class.js b/frappe/public/js/frappe/class.js
index 4f6dd0dc97..79ef2792ae 100644
--- a/frappe/public/js/frappe/class.js
+++ b/frappe/public/js/frappe/class.js
@@ -80,4 +80,4 @@ To subclass, use:
// export
global.Class = Class;
- })(this);
\ No newline at end of file
+ })(window);
diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js
index d6cb7f5507..46812f5fb6 100644
--- a/frappe/public/js/frappe/desk.js
+++ b/frappe/public/js/frappe/desk.js
@@ -24,12 +24,12 @@ $(document).ready(function() {
frappe.start_app();
});
-frappe.Application = Class.extend({
- init: function() {
+frappe.Application = class Application {
+ constructor() {
this.startup();
- },
+ }
- startup: function() {
+ startup() {
frappe.socketio.init();
frappe.model.init();
@@ -115,7 +115,7 @@ frappe.Application = Class.extend({
});
// listen to build errors
- this.setup_build_error_listener();
+ this.setup_build_events();
if (frappe.sys_defaults.email_user_password) {
var email_list = frappe.sys_defaults.email_user_password.split(',');
@@ -160,7 +160,7 @@ frappe.Application = Class.extend({
}, 600000); // check every 10 minutes
}
}
- },
+ }
set_route() {
frappe.flags.setting_original_route = true;
@@ -175,14 +175,14 @@ frappe.Application = Class.extend({
frappe.router.on('change', () => {
$(".tooltip").hide();
});
- },
+ }
setup_frappe_vue() {
Vue.prototype.__ = window.__;
Vue.prototype.frappe = window.frappe;
- },
+ }
- set_password: function(user) {
+ set_password(user) {
var me=this;
frappe.call({
method: 'frappe.core.doctype.user.user.get_email_awaiting',
@@ -199,9 +199,9 @@ frappe.Application = Class.extend({
}
}
});
- },
+ }
- email_password_prompt: function(email_account,user,i) {
+ email_password_prompt(email_account,user,i) {
var me = this;
let d = new frappe.ui.Dialog({
title: __('Password missing in Email Account'),
@@ -255,8 +255,8 @@ frappe.Application = Class.extend({
});
});
d.show();
- },
- load_bootinfo: function() {
+ }
+ load_bootinfo() {
if(frappe.boot) {
this.setup_workspaces();
frappe.model.sync(frappe.boot.docs);
@@ -278,7 +278,7 @@ frappe.Application = Class.extend({
} else {
this.set_as_guest();
}
- },
+ }
setup_workspaces() {
frappe.modules = {};
@@ -289,26 +289,26 @@ frappe.Application = Class.extend({
}
if (!frappe.workspaces['home']) {
// default workspace is settings for Frappe
- frappe.workspaces['home'] = frappe.workspaces['build'];
+ frappe.workspaces['home'] = frappe.workspaces[Object.keys(frappe.workspaces)[0]];
}
- },
+ }
- load_user_permissions: function() {
+ load_user_permissions() {
frappe.defaults.update_user_permissions();
frappe.realtime.on('update_user_permissions', frappe.utils.debounce(() => {
frappe.defaults.update_user_permissions();
}, 500));
- },
+ }
- check_metadata_cache_status: function() {
+ check_metadata_cache_status() {
if(frappe.boot.metadata_version != localStorage.metadata_version) {
frappe.assets.clear_local_storage();
frappe.assets.init_local_storage();
}
- },
+ }
- set_globals: function() {
+ set_globals() {
frappe.session.user = frappe.boot.user.name;
frappe.session.logged_in_user = frappe.boot.user.name;
frappe.session.user_email = frappe.boot.user.email;
@@ -360,8 +360,8 @@ frappe.Application = Class.extend({
}
}
});
- },
- sync_pages: function() {
+ }
+ sync_pages() {
// clear cached pages if timestamp is not found
if(localStorage["page_info"]) {
frappe.boot.allowed_pages = [];
@@ -376,8 +376,8 @@ frappe.Application = Class.extend({
frappe.boot.allowed_pages = Object.keys(frappe.boot.page_info);
}
localStorage["page_info"] = JSON.stringify(frappe.boot.page_info);
- },
- set_as_guest: function() {
+ }
+ set_as_guest() {
frappe.session.user = 'Guest';
frappe.session.user_email = '';
frappe.session.user_fullname = 'Guest';
@@ -385,23 +385,23 @@ frappe.Application = Class.extend({
frappe.user_defaults = {};
frappe.user_roles = ['Guest'];
frappe.sys_defaults = {};
- },
- make_page_container: function() {
+ }
+ make_page_container() {
if ($("#body").length) {
$(".splash").remove();
frappe.temp_container = $("
")
.appendTo("body");
frappe.container = new frappe.views.Container();
}
- },
- make_nav_bar: function() {
+ }
+ make_nav_bar() {
// toolbar
if(frappe.boot && frappe.boot.home_page!=='setup-wizard') {
frappe.frappe_toolbar = new frappe.ui.toolbar.Toolbar();
}
- },
- logout: function() {
+ }
+ logout() {
var me = this;
me.logged_out = true;
return frappe.call({
@@ -413,8 +413,8 @@ frappe.Application = Class.extend({
me.redirect_to_login();
}
});
- },
- handle_session_expired: function() {
+ }
+ handle_session_expired() {
if(!frappe.app.session_expired_dialog) {
var dialog = new frappe.ui.Dialog({
title: __('Session Expired'),
@@ -464,16 +464,16 @@ frappe.Application = Class.extend({
'background-color': '#4B4C9D'
});
}
- },
- redirect_to_login: function() {
+ }
+ redirect_to_login() {
window.location.href = '/';
- },
- set_favicon: function() {
+ }
+ set_favicon() {
var link = $('link[type="image/x-icon"]').remove().attr("href");
$('
').appendTo("head");
$('
').appendTo("head");
- },
- trigger_primary_action: function() {
+ }
+ trigger_primary_action() {
// to trigger change event on active input before triggering primary action
$(document.activeElement).blur();
// wait for possible JS validations triggered after blur (it might change primary button)
@@ -487,20 +487,20 @@ frappe.Application = Class.extend({
frappe.container.page.save_action();
}
}, 100);
- },
+ }
- set_rtl: function() {
+ set_rtl() {
if (frappe.utils.is_rtl()) {
var ls = document.createElement('link');
ls.rel="stylesheet";
ls.type = "text/css";
- ls.href= "/assets/css/frappe-rtl.css";
+ ls.href= frappe.assets.bundled_asset("frappe-rtl.bundle.css");
document.getElementsByTagName('head')[0].appendChild(ls);
$('body').addClass('frappe-rtl');
}
- },
+ }
- show_change_log: function() {
+ show_change_log() {
var me = this;
let change_log = frappe.boot.change_log;
@@ -531,15 +531,15 @@ frappe.Application = Class.extend({
});
me.show_notes();
};
- },
+ }
- show_update_available: () => {
+ show_update_available() {
frappe.call({
"method": "frappe.utils.change_log.show_update_popup"
});
- },
+ }
- setup_analytics: function() {
+ setup_analytics() {
if(window.mixpanel) {
window.mixpanel.identify(frappe.session.user);
window.mixpanel.people.set({
@@ -549,17 +549,17 @@ frappe.Application = Class.extend({
"$email": frappe.session.user
});
}
- },
+ }
add_browser_class() {
$('html').addClass(frappe.utils.get_browser().name.toLowerCase());
- },
+ }
set_fullwidth_if_enabled() {
frappe.ui.toolbar.set_fullwidth_if_enabled();
- },
+ }
- show_notes: function() {
+ show_notes() {
var me = this;
if(frappe.boot.notes.length) {
frappe.boot.notes.forEach(function(note) {
@@ -586,21 +586,19 @@ frappe.Application = Class.extend({
}
});
}
- },
+ }
- setup_build_error_listener() {
+ setup_build_events() {
if (frappe.boot.developer_mode) {
- frappe.realtime.on('build_error', (data) => {
- console.log(data);
- });
+ frappe.require("build_events.bundle.js");
}
- },
+ }
setup_energy_point_listeners() {
frappe.realtime.on('energy_point_alert', (message) => {
frappe.show_alert(message);
});
- },
+ }
setup_copy_doc_listener() {
$('body').on('paste', (e) => {
@@ -634,7 +632,7 @@ frappe.Application = Class.extend({
}
});
}
-});
+}
frappe.get_module = function(m, default_module) {
var module = frappe.modules[m] || default_module;
diff --git a/frappe/public/js/frappe/form/controls/attach.js b/frappe/public/js/frappe/form/controls/attach.js
index 604510bb52..672087ddc2 100644
--- a/frappe/public/js/frappe/form/controls/attach.js
+++ b/frappe/public/js/frappe/form/controls/attach.js
@@ -1,5 +1,5 @@
-frappe.ui.form.ControlAttach = frappe.ui.form.ControlData.extend({
- make_input: function() {
+frappe.ui.form.ControlAttach = class ControlAttach extends frappe.ui.form.ControlData {
+ make_input() {
let me = this;
this.$input = $('
').appendTo(this.parent);
}
- },
- toggle_label: function(show) {
+ }
+ toggle_label(show) {
this.$wrapper.find(".control-label").toggleClass("hide", !show);
- },
- toggle_description: function(show) {
+ }
+ toggle_description(show) {
this.$wrapper.find(".help-box").toggleClass("hide", !show);
- },
- set_input_areas: function() {
+ }
+ set_input_areas() {
if(this.only_input) {
this.input_area = this.wrapper;
} else {
@@ -43,17 +43,17 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
// like links, currencies, HTMLs etc.
this.disp_area = this.$wrapper.find(".control-value").get(0);
}
- },
- set_max_width: function() {
- if(this.horizontal) {
+ }
+ set_max_width() {
+ if(this.constructor.horizontal) {
this.$wrapper.addClass("input-max-width");
}
- },
+ }
// update input value, label, description
// display (show/hide/read-only),
// mandatory style on refresh
- refresh_input: function() {
+ refresh_input() {
var me = this;
var make_input = function() {
if (!me.has_input) {
@@ -106,13 +106,13 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
me.set_bold();
me.set_required();
}
- },
+ }
can_write() {
return this.disp_status == "Write";
- },
+ }
- set_disp_area: function(value) {
+ set_disp_area(value) {
if(in_list(["Currency", "Int", "Float"], this.df.fieldtype)
&& (this.value === 0 || value === 0)) {
// to set the 0 value in readonly for currency, int, float field
@@ -126,8 +126,8 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
let doc = this.doc || (this.frm && this.frm.doc);
let display_value = frappe.format(value, this.df, { no_icon: true, inline: true }, doc);
this.disp_area && $(this.disp_area).html(display_value);
- },
- set_label: function(label) {
+ }
+ set_label(label) {
if(label) this.df.label = label;
if(this.only_input || this.df.label==this._label)
@@ -137,8 +137,8 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
this.label_span.innerHTML = (icon ? '
' : "") +
__(this.df.label) || " ";
this._label = this.df.label;
- },
- set_description: function(description) {
+ }
+ set_description(description) {
if (description !== undefined) {
this.df.description = description;
}
@@ -151,17 +151,17 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
this.set_empty_description();
}
this._description = this.df.description;
- },
- set_new_description: function(description) {
+ }
+ set_new_description(description) {
this.$wrapper.find(".help-box").html(description);
- },
- set_empty_description: function() {
+ }
+ set_empty_description() {
this.$wrapper.find(".help-box").html("");
- },
- set_mandatory: function(value) {
+ }
+ set_mandatory(value) {
this.$wrapper.toggleClass("has-error", Boolean(this.df.reqd && is_null(value)));
- },
- set_invalid: function () {
+ }
+ set_invalid () {
let invalid = !!this.df.invalid;
if (this.grid) {
this.$wrapper.parents('.grid-static-col').toggleClass('invalid', invalid);
@@ -170,11 +170,11 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
} else {
this.$wrapper.toggleClass('has-error', invalid);
}
- },
+ }
set_required() {
this.label_area && $(this.label_area).toggleClass('reqd', Boolean(this.df.reqd));
- },
- set_bold: function() {
+ }
+ set_bold() {
if(this.$input) {
this.$input.toggleClass("bold", !!(this.df.bold || this.df.reqd));
}
@@ -182,4 +182,4 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
$(this.disp_area).toggleClass("bold", !!(this.df.bold || this.df.reqd));
}
}
-});
+};
diff --git a/frappe/public/js/frappe/form/controls/button.js b/frappe/public/js/frappe/form/controls/button.js
index d09e9c3a95..5e80ebfa0e 100644
--- a/frappe/public/js/frappe/form/controls/button.js
+++ b/frappe/public/js/frappe/form/controls/button.js
@@ -1,9 +1,9 @@
-frappe.ui.form.ControlButton = frappe.ui.form.ControlData.extend({
+frappe.ui.form.ControlButton = class ControlButton extends frappe.ui.form.ControlData {
can_write() {
// should be always true in case of button
return true;
- },
- make_input: function() {
+ }
+ make_input() {
var me = this;
const btn_type = this.df.primary ? 'btn-primary': 'btn-default';
const btn_size = this.df.btn_size
@@ -18,8 +18,8 @@ frappe.ui.form.ControlButton = frappe.ui.form.ControlData.extend({
this.set_input_attributes();
this.has_input = true;
this.toggle_label(false);
- },
- onclick: function() {
+ }
+ onclick() {
if (this.frm && this.frm.doc) {
if (this.frm.script_manager.has_handlers(this.df.fieldname, this.doctype)) {
this.frm.script_manager.trigger(this.df.fieldname, this.doctype, this.docname);
@@ -31,8 +31,8 @@ frappe.ui.form.ControlButton = frappe.ui.form.ControlData.extend({
} else if (this.df.click) {
this.df.click();
}
- },
- run_server_script: function() {
+ }
+ run_server_script() {
// DEPRECATE
var me = this;
if(this.frm && this.frm.docname) {
@@ -47,18 +47,18 @@ frappe.ui.form.ControlButton = frappe.ui.form.ControlData.extend({
}
});
}
- },
+ }
hide() {
this.$input.hide();
- },
- set_input_areas: function() {
- this._super();
+ }
+ set_input_areas() {
+ super.set_input_areas();
$(this.disp_area).removeClass().addClass("hide");
- },
- set_empty_description: function() {
+ }
+ set_empty_description() {
this.$wrapper.find(".help-box").empty().toggle(false);
- },
- set_label: function(label) {
+ }
+ set_label(label) {
if (label) {
this.df.label = label;
}
@@ -66,4 +66,4 @@ frappe.ui.form.ControlButton = frappe.ui.form.ControlData.extend({
$(this.label_span).html(" ");
this.$input && this.$input.html(label);
}
-});
+};
diff --git a/frappe/public/js/frappe/form/controls/check.js b/frappe/public/js/frappe/form/controls/check.js
index c8dc0df962..658ef640a9 100644
--- a/frappe/public/js/frappe/form/controls/check.js
+++ b/frappe/public/js/frappe/form/controls/check.js
@@ -1,6 +1,7 @@
-frappe.ui.form.ControlCheck = frappe.ui.form.ControlData.extend({
- input_type: "checkbox",
- make_wrapper: function() {
+frappe.ui.form.ControlCheck = class ControlCheck extends frappe.ui.form.ControlData {
+ static html_element = "input"
+ static input_type = "checkbox"
+ make_wrapper() {
this.$wrapper = $(`
`).appendTo(this.parent);
- },
- set_input_areas: function() {
+ }
+ set_input_areas() {
this.label_area = this.label_span = this.$wrapper.find(".label-area").get(0);
this.input_area = this.$wrapper.find(".input-area").get(0);
this.disp_area = this.$wrapper.find(".disp-area").get(0);
- },
- make_input: function() {
- this._super();
+ }
+ make_input() {
+ super.make_input();
this.$input.removeClass("form-control");
- },
- get_input_value: function() {
+ }
+ get_input_value() {
return this.input && this.input.checked ? 1 : 0;
- },
- validate: function(value) {
+ }
+ validate(value) {
return cint(value);
- },
- set_input: function(value) {
+ }
+ set_input(value) {
value = cint(value);
if(this.input) {
this.input.checked = (value ? 1 : 0);
@@ -36,4 +37,4 @@ frappe.ui.form.ControlCheck = frappe.ui.form.ControlData.extend({
this.set_mandatory(value);
this.set_disp_area(value);
}
-});
+};
diff --git a/frappe/public/js/frappe/form/controls/code.js b/frappe/public/js/frappe/form/controls/code.js
index 9600763588..9155333ee3 100644
--- a/frappe/public/js/frappe/form/controls/code.js
+++ b/frappe/public/js/frappe/form/controls/code.js
@@ -1,8 +1,8 @@
-frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
+frappe.ui.form.ControlCode = class ControlCode extends frappe.ui.form.ControlText {
make_input() {
if (this.editor) return;
this.load_lib().then(() => this.make_ace_editor());
- },
+ }
make_ace_editor() {
if (this.editor) return;
@@ -34,6 +34,7 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
// setup autocompletion when it is set the first time
Object.defineProperty(this.df, 'autocompletions', {
+ configurable: true,
get() {
return this._autocompletions || [];
},
@@ -42,7 +43,7 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
this.df._autocompletions = value;
}
});
- },
+ }
setup_autocompletion() {
if (this._autocompletion_setup) return;
@@ -82,20 +83,20 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
});
});
this._autocompletion_setup = true;
- },
+ }
refresh_height() {
this.ace_editor_target.css('height', this.expanded ? 600 : 300);
this.editor.resize();
- },
+ }
toggle_label() {
this.$expand_button && this.$expand_button.text(this.get_button_label());
- },
+ }
get_button_label() {
return this.expanded ? __('Collapse', null, 'Shrink code field.') : __('Expand', null, 'Enlarge code field.');
- },
+ }
set_language() {
const language_map = {
@@ -122,14 +123,14 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
const ace_language_mode = language_map[language] || '';
this.editor.session.setMode(ace_language_mode);
this.editor.setKeyboardHandler('ace/keyboard/vscode');
- },
+ }
parse(value) {
if (value == null) {
value = "";
}
return value;
- },
+ }
set_formatted_input(value) {
return this.load_lib().then(() => {
@@ -138,11 +139,11 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
if (value === this.get_input_value()) return;
this.editor.session.setValue(value);
});
- },
+ }
get_input_value() {
return this.editor ? this.editor.session.getValue() : '';
- },
+ }
load_lib() {
if (this.library_loaded) return this.library_loaded;
@@ -162,4 +163,4 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
return this.library_loaded;
}
-});
+};
diff --git a/frappe/public/js/frappe/form/controls/color.js b/frappe/public/js/frappe/form/controls/color.js
index 8a60f3e3da..7e8e25fac9 100644
--- a/frappe/public/js/frappe/form/controls/color.js
+++ b/frappe/public/js/frappe/form/controls/color.js
@@ -1,12 +1,13 @@
import Picker from '../../color_picker/color_picker';
-frappe.ui.form.ControlColor = frappe.ui.form.ControlData.extend({
- make_input: function () {
+frappe.ui.form.ControlColor = class ControlColor extends frappe.ui.form.ControlData {
+ make_input() {
this.df.placeholder = this.df.placeholder || __('Choose a color');
- this._super();
+ super.make_input();
this.make_color_input();
- },
- make_color_input: function () {
+ }
+
+ make_color_input() {
let picker_wrapper = $('
');
this.picker = new Picker({
parent: picker_wrapper[0],
@@ -73,27 +74,31 @@ frappe.ui.form.ControlColor = frappe.ui.form.ControlData.extend({
this.$wrapper.popover('hide');
});
});
- },
+ }
+
refresh() {
- this._super();
+ super.refresh();
let color = this.get_color();
if (this.picker && this.picker.color !== color) {
this.picker.color = color;
this.picker.refresh();
}
- },
- set_formatted_input: function(value) {
- this._super(value);
+ }
+
+ set_formatted_input(value) {
+ super.set_formatted_input(value);
this.$input.val(value);
this.selected_color.css({
"background-color": value || 'transparent',
});
this.selected_color.toggleClass('no-value', !value);
- },
+ }
+
get_color() {
return this.validate(this.get_value());
- },
- validate: function (value) {
+ }
+
+ validate(value) {
if (value === '') {
return '';
}
@@ -103,4 +108,4 @@ frappe.ui.form.ControlColor = frappe.ui.form.ControlData.extend({
}
return null;
}
-});
+};
diff --git a/frappe/public/js/frappe/form/controls/comment.js b/frappe/public/js/frappe/form/controls/comment.js
index 7efc60b61d..7c10b61366 100644
--- a/frappe/public/js/frappe/form/controls/comment.js
+++ b/frappe/public/js/frappe/form/controls/comment.js
@@ -3,7 +3,7 @@ import Mention from './quill-mention/quill.mention';
Quill.register('modules/mention', Mention, true);
-frappe.ui.form.ControlComment = frappe.ui.form.ControlTextEditor.extend({
+frappe.ui.form.ControlComment = class ControlComment extends frappe.ui.form.ControlTextEditor {
make_wrapper() {
this.comment_wrapper = !this.no_wrapper ? $(`
' + time_zone; } } - this._super(); - }, - set_datepicker: function() { - this._super(); + super.set_description(); + } + set_datepicker() { + super.set_datepicker(); if (this.datepicker.opts.timeFormat.indexOf('s') == -1) { // No seconds in time format const $tp = this.datepicker.timepicker; @@ -36,4 +36,4 @@ frappe.ui.form.ControlDatetime = frappe.ui.form.ControlDate.extend({ $tp.$secondsText.prev().css('display', 'none'); } } -}); +}; diff --git a/frappe/public/js/frappe/form/controls/duration.js b/frappe/public/js/frappe/form/controls/duration.js index e70afd6e65..361d10982e 100644 --- a/frappe/public/js/frappe/form/controls/duration.js +++ b/frappe/public/js/frappe/form/controls/duration.js @@ -1,10 +1,10 @@ -frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({ - make_input: function() { - this._super(); +frappe.ui.form.ControlDuration = class ControlDuration extends frappe.ui.form.ControlData { + make_input() { + super.make_input(); this.make_picker(); - }, + } - make_picker: function() { + make_picker() { this.inputs = []; this.set_duration_options(); this.$picker = $( @@ -21,9 +21,9 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({ this.$picker.hide(); this.bind_events(); this.refresh(); - }, + } - build_numeric_input: function(label, hidden, max) { + build_numeric_input(label, hidden, max) { let $duration_input = $(` `); @@ -47,13 +47,13 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({ } $control.prepend($input); $control.appendTo(this.$picker.find(".picker-row")); - }, + } set_duration_options() { this.duration_options = frappe.utils.get_duration_options(this.df); - }, + } - set_duration_picker_value: function(value) { + set_duration_picker_value(value) { let total_duration = frappe.utils.seconds_to_duration(value, this.duration_options); if (this.$picker) { @@ -61,9 +61,9 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({ this.inputs[duration].prop("value", total_duration[duration]); }); } - }, + } - bind_events: function() { + bind_events() { // flag to handle the display property of the picker let clicked = false; @@ -103,21 +103,21 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({ this.$picker.hide(); } }); - }, + } get_value() { return cint(this.value); - }, + } - refresh_input: function() { - this._super(); + refresh_input() { + super.refresh_input(); this.set_duration_options(); this.set_duration_picker_value(this.value); - }, + } - format_for_input: function(value) { + format_for_input(value) { return frappe.utils.get_formatted_duration(value, this.duration_options); - }, + } get_duration() { // returns an object of days, hours, minutes and seconds from the inputs array @@ -138,7 +138,7 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({ } } return total_duration; - }, + } is_duration_picker_set(inputs) { let is_set = false; @@ -149,4 +149,4 @@ frappe.ui.form.ControlDuration = frappe.ui.form.ControlData.extend({ }); return is_set; } -}); \ No newline at end of file +}; diff --git a/frappe/public/js/frappe/form/controls/dynamic_link.js b/frappe/public/js/frappe/form/controls/dynamic_link.js index 00bb02a5fc..2c5661ca87 100644 --- a/frappe/public/js/frappe/form/controls/dynamic_link.js +++ b/frappe/public/js/frappe/form/controls/dynamic_link.js @@ -1,5 +1,5 @@ -frappe.ui.form.ControlDynamicLink = frappe.ui.form.ControlLink.extend({ - get_options: function() { +frappe.ui.form.ControlDynamicLink = class ControlDynamicLink extends frappe.ui.form.ControlLink { + get_options() { let options = ''; if (this.df.get_options) { options = this.df.get_options(); @@ -28,5 +28,5 @@ frappe.ui.form.ControlDynamicLink = frappe.ui.form.ControlLink.extend({ } return options; - }, -}); + } +}; diff --git a/frappe/public/js/frappe/form/controls/float.js b/frappe/public/js/frappe/form/controls/float.js index 306c05dc1b..89f8f23cc5 100644 --- a/frappe/public/js/frappe/form/controls/float.js +++ b/frappe/public/js/frappe/form/controls/float.js @@ -1,27 +1,27 @@ -frappe.ui.form.ControlFloat = frappe.ui.form.ControlInt.extend({ - parse: function(value) { +frappe.ui.form.ControlFloat = class ControlFloat extends frappe.ui.form.ControlInt { + parse(value) { value = this.eval_expression(value); return isNaN(parseFloat(value)) ? null : flt(value, this.get_precision()); - }, + } - format_for_input: function(value) { + format_for_input(value) { var number_format; if (this.df.fieldtype==="Float" && this.df.options && this.df.options.trim()) { number_format = this.get_number_format(); } var formatted_value = format_number(value, number_format, this.get_precision()); return isNaN(Number(value)) ? "" : formatted_value; - }, + } - get_number_format: function() { + get_number_format() { var currency = frappe.meta.get_field_currency(this.df, this.get_doc()); return get_number_format(currency); - }, + } - get_precision: function() { + get_precision() { // round based on field precision or float precision, else don't round return this.df.precision || cint(frappe.boot.sysdefaults.float_precision, null); } -}); +}; frappe.ui.form.ControlPercent = frappe.ui.form.ControlFloat; diff --git a/frappe/public/js/frappe/form/controls/geolocation.js b/frappe/public/js/frappe/form/controls/geolocation.js index dfd0f4d174..080a1cbb48 100644 --- a/frappe/public/js/frappe/form/controls/geolocation.js +++ b/frappe/public/js/frappe/form/controls/geolocation.js @@ -1,11 +1,11 @@ frappe.provide('frappe.utils.utils'); -frappe.ui.form.ControlGeolocation = frappe.ui.form.ControlData.extend({ - horizontal: false, +frappe.ui.form.ControlGeolocation = class ControlGeolocation extends frappe.ui.form.ControlData { + static horizontal = false make_wrapper() { // Create the elements for map area - this._super(); + super.make_wrapper(); let $input_wrapper = this.$wrapper.find('.control-input-wrapper'); this.map_id = frappe.dom.get_unique_id(); @@ -24,14 +24,14 @@ frappe.ui.form.ControlGeolocation = frappe.ui.form.ControlData.extend({ this.make_map(); }); } - }, + } make_map() { this.bind_leaflet_map(); this.bind_leaflet_draw_control(); this.bind_leaflet_locate_control(); this.bind_leaflet_refresh_button(); - }, + } format_for_input(value) { if (!this.map) return; @@ -65,7 +65,7 @@ frappe.ui.form.ControlGeolocation = frappe.ui.form.ControlData.extend({ } else if ((value===undefined) || (value == JSON.stringify(new L.FeatureGroup().toGeoJSON()))) { this.locate_control.start(); } - }, + } bind_leaflet_map() { var circleToGeoJSON = L.Circle.prototype.toGeoJSON; @@ -97,13 +97,13 @@ frappe.ui.form.ControlGeolocation = frappe.ui.form.ControlData.extend({ L.tileLayer(frappe.utils.map_defaults.tiles, frappe.utils.map_defaults.options).addTo(this.map); - }, + } bind_leaflet_locate_control() { // To request location update and set location, sets current geolocation on load this.locate_control = L.control.locate({position:'topright'}); this.locate_control.addTo(this.map); - }, + } bind_leaflet_draw_control() { this.editableLayers = new L.FeatureGroup(); @@ -160,7 +160,7 @@ frappe.ui.form.ControlGeolocation = frappe.ui.form.ControlData.extend({ this.editableLayers.removeLayer(layer); this.set_value(JSON.stringify(this.editableLayers.toGeoJSON())); }); - }, + } bind_leaflet_refresh_button() { L.easyButton({ @@ -177,7 +177,7 @@ frappe.ui.form.ControlGeolocation = frappe.ui.form.ControlData.extend({ icon: 'fa fa-refresh' }] }).addTo(this.map); - }, + } add_non_group_layers(source_layer, target_group) { // https://gis.stackexchange.com/a/203773 @@ -189,11 +189,11 @@ frappe.ui.form.ControlGeolocation = frappe.ui.form.ControlData.extend({ } else { target_group.addLayer(source_layer); } - }, + } clear_editable_layers() { this.editableLayers.eachLayer((l)=>{ this.editableLayers.removeLayer(l); }); } -}); +}; diff --git a/frappe/public/js/frappe/form/controls/heading.js b/frappe/public/js/frappe/form/controls/heading.js index 7b9dd043f2..ccce412eaf 100644 --- a/frappe/public/js/frappe/form/controls/heading.js +++ b/frappe/public/js/frappe/form/controls/heading.js @@ -1,5 +1,5 @@ -frappe.ui.form.ControlHeading = frappe.ui.form.ControlHTML.extend({ - get_content: function() { +frappe.ui.form.ControlHeading = class ControlHeading extends frappe.ui.form.ControlHTML { + get_content() { return "
" + __(this.df.label) + "
"; } -}); +}; diff --git a/frappe/public/js/frappe/form/controls/html.js b/frappe/public/js/frappe/form/controls/html.js index f8a3645705..b2f18d4ccc 100644 --- a/frappe/public/js/frappe/form/controls/html.js +++ b/frappe/public/js/frappe/form/controls/html.js @@ -1,13 +1,13 @@ -frappe.ui.form.ControlHTML = frappe.ui.form.Control.extend({ - make: function() { - this._super(); +frappe.ui.form.ControlHTML = class ControlHTML extends frappe.ui.form.Control { + make() { + super.make(); this.disp_area = this.wrapper; - }, - refresh_input: function() { + } + refresh_input() { var content = this.get_content(); if(content) this.$wrapper.html(content); - }, - get_content: function() { + } + get_content() { var content = this.df.options || ""; content = __(content); try { @@ -15,11 +15,11 @@ frappe.ui.form.ControlHTML = frappe.ui.form.Control.extend({ } catch (e) { return content; } - }, - html: function(html) { + } + html(html) { this.$wrapper.html(html || this.get_content()); - }, - set_value: function(html) { + } + set_value(html) { if(html.appendTo) { // jquery object html.appendTo(this.$wrapper.empty()); @@ -29,4 +29,4 @@ frappe.ui.form.ControlHTML = frappe.ui.form.Control.extend({ this.html(html); } } -}); +}; diff --git a/frappe/public/js/frappe/form/controls/html_editor.js b/frappe/public/js/frappe/form/controls/html_editor.js index f708bdbd98..7d43112e6a 100644 --- a/frappe/public/js/frappe/form/controls/html_editor.js +++ b/frappe/public/js/frappe/form/controls/html_editor.js @@ -1,13 +1,13 @@ -frappe.ui.form.ControlHTMLEditor = frappe.ui.form.ControlMarkdownEditor.extend({ - editor_class: 'html', +frappe.ui.form.ControlHTMLEditor = class ControlHTMLEditor extends frappe.ui.form.ControlMarkdownEditor { + static editor_class = 'html'; set_language() { this.df.options = 'HTML'; - this._super(); - }, + super.set_language(); + } update_preview() { if (!this.markdown_preview) return; let value = this.get_value() || ''; value = frappe.dom.remove_script_and_style(value); this.markdown_preview.html(value); } -}); +}; diff --git a/frappe/public/js/frappe/form/controls/image.js b/frappe/public/js/frappe/form/controls/image.js index 90de22138a..d175330947 100644 --- a/frappe/public/js/frappe/form/controls/image.js +++ b/frappe/public/js/frappe/form/controls/image.js @@ -1,12 +1,12 @@ -frappe.ui.form.ControlImage = frappe.ui.form.Control.extend({ - make: function() { - this._super(); +frappe.ui.form.ControlImage = class ControlImage extends frappe.ui.form.Control { + make() { + super.make(); this.$wrapper.css({"margin": "0px"}); this.$body = $("").appendTo(this.$wrapper) .css({"margin-bottom": "10px"}); $('').appendTo(this.$wrapper); - }, - refresh_input: function() { + } + refresh_input() { this.$body.empty(); var doc = this.get_doc(); @@ -19,4 +19,4 @@ frappe.ui.form.ControlImage = frappe.ui.form.Control.extend({ } return false; } -}); +}; diff --git a/frappe/public/js/frappe/form/controls/int.js b/frappe/public/js/frappe/form/controls/int.js index aca3a85603..12652bf86e 100644 --- a/frappe/public/js/frappe/form/controls/int.js +++ b/frappe/public/js/frappe/form/controls/int.js @@ -1,13 +1,13 @@ -frappe.ui.form.ControlInt = frappe.ui.form.ControlData.extend({ - trigger_change_on_input_event: false, - make: function () { - this._super(); +frappe.ui.form.ControlInt = class ControlInt extends frappe.ui.form.ControlData { + static trigger_change_on_input_event = false + make () { + super.make(); // $(this.label_area).addClass('pull-right'); // $(this.disp_area).addClass('text-right'); - }, - make_input: function () { + } + make_input () { var me = this; - this._super(); + super.make_input(); this.$input // .addClass("text-right") .on("focus", function () { @@ -19,11 +19,11 @@ frappe.ui.form.ControlInt = frappe.ui.form.ControlData.extend({ }, 100); return false; }); - }, - validate: function (value) { + } + validate (value) { return this.parse(value); - }, - eval_expression: function (value) { + } + eval_expression (value) { if (typeof value === 'string') { if (value.match(/^[0-9+\-/* ]+$/)) { // If it is a string containing operators @@ -36,8 +36,8 @@ frappe.ui.form.ControlInt = frappe.ui.form.ControlData.extend({ } } return value; - }, - parse: function (value) { + } + parse (value) { return cint(this.eval_expression(value), null); } -}); +}; diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index c32c99f0ed..43bd7443ab 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -8,9 +8,9 @@ import Awesomplete from 'awesomplete'; frappe.ui.form.recent_link_validations = {}; -frappe.ui.form.ControlLink = frappe.ui.form.ControlData.extend({ - trigger_change_on_input_event: false, - make_input: function() { +frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlData { + static trigger_change_on_input_event = false + make_input() { var me = this; $(`