├── lib ├── .npmignore └── index.js ├── .npmignore ├── .husky ├── commit-msg ├── pre-push ├── pre-commit └── prepare-commit-msg ├── .gitignore ├── scripts ├── prepare-commit-msg.sh ├── commit-msg.sh ├── pre-push.sh ├── pre-commit.sh ├── recompile.sh └── setup.sh ├── webpack.prod.js ├── webpack.dev.js ├── src ├── helpers │ ├── ios │ │ ├── index.js │ │ └── flattenObject.js │ ├── web │ │ ├── index.js │ │ └── parseParams.js │ ├── common │ │ ├── index.js │ │ ├── callbackInvoker.js │ │ └── callbackMapper.js │ ├── index.js │ └── android │ │ ├── objMap.js │ │ └── index.js ├── doms │ ├── index.js │ ├── web.js │ ├── ios.js │ └── android.js ├── handler.js ├── computeIOS.js ├── components │ ├── Button.js │ ├── SwitchButton.js │ ├── DateRangePicker.js │ ├── NavBar.js │ ├── DropdownBox.js │ └── DropdownSearchBox.js ├── WEB │ ├── ViewPageAdapter.js │ ├── ListPresto.js │ ├── Utils.js │ └── AndroidInterface.js ├── IOS │ ├── Render.js │ └── AndroidInterface.js ├── animations.js ├── helper.js ├── compute.js ├── component.js ├── baseView.js ├── PrestoUIInterface.js └── init.js ├── index.js ├── webpack.common.js ├── README.MD └── package.json /lib/.npmignore: -------------------------------------------------------------------------------- 1 | !presto* -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | * 2 | !lib/**/* 3 | !package.json 4 | !*.md 5 | scripts 6 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | ./scripts/commit-msg.sh -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | ./scripts/pre-push.sh 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | ./scripts/pre-commit.sh 5 | -------------------------------------------------------------------------------- /.husky/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | ./scripts/prepare-commit-msg.sh 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | *.tgz 4 | dist 5 | npm-debug.log 6 | .yarn-error.log 7 | .idea 8 | package-lock.json 9 | /Session.vim 10 | -------------------------------------------------------------------------------- /scripts/prepare-commit-msg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Adding hooks to prepare commit message. 4 | # This hook triggers Jira input modules. 5 | # Helps in maintaining code compatblity. 6 | exec < /dev/tty && node_modules/.bin/git-cz --hook || true -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var prestoUI 4 | 5 | if (window.__OS == "WEB") { 6 | prestoUI = require("./index.web.js") 7 | } else if (window.__OS == "IOS") { 8 | prestoUI = require("./index.ios.js") 9 | } else { 10 | prestoUI = require("./index.android.js") 11 | } 12 | 13 | module.exports = prestoUI 14 | -------------------------------------------------------------------------------- /scripts/commit-msg.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e; 4 | 5 | # Adding commit linter. Validates commit messges are according to format. 6 | # Runs when commit message is created. 7 | # TODO: need to verify 8 | npx --no -- commitlint --edit 9 | 10 | finish() { 11 | result=$? 12 | # Add cleanup code here 13 | exit ${result} 14 | } 15 | trap finish EXIT ERR -------------------------------------------------------------------------------- /scripts/pre-push.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e; 4 | 5 | # Adding commit linter. Validates commit messges are according to format. 6 | # Runs before pushing commits. 7 | # To try and fail VSCode and other editor commits if not properly fomratted. 8 | npx --no -- commitlint --edit 9 | 10 | finish() { 11 | result=$? 12 | # Add cleanup code here 13 | exit ${result} 14 | } 15 | trap finish EXIT ERR -------------------------------------------------------------------------------- /scripts/pre-commit.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e; 4 | 5 | # Adding commit linter. Validates commit messges are according to format. 6 | # Runs before commit is created. 7 | # To try and fail VSCode and other editor commits if not properly fomratted. 8 | npx --no -- commitlint --edit 9 | 10 | finish() { 11 | result=$? 12 | # Add cleanup code here 13 | exit ${result} 14 | } 15 | trap finish EXIT ERR 16 | -------------------------------------------------------------------------------- /webpack.prod.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | const { merge } = require("webpack-merge"); 3 | const common = require("./webpack.common.js"); 4 | 5 | const prodConfig = merge(common, { 6 | mode: "production" 7 | }); 8 | 9 | function getPlatformOverride(platform) { 10 | return merge(prodConfig, { 11 | entry: ["./index.js"], 12 | output: { 13 | filename: `index.${platform.toLowerCase()}.js` 14 | }, 15 | plugins: [ 16 | new webpack.DefinePlugin({ 17 | "window.__OS": JSON.stringify(platform) 18 | }) 19 | ] 20 | }); 21 | } 22 | 23 | module.exports = [ 24 | getPlatformOverride("ANDROID"), 25 | getPlatformOverride("IOS"), 26 | getPlatformOverride("WEB") 27 | ]; 28 | -------------------------------------------------------------------------------- /webpack.dev.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | const { merge } = require("webpack-merge"); 3 | const common = require("./webpack.common.js"); 4 | 5 | const devConfig = merge(common, { 6 | mode: "development", 7 | devtool: 'source-map', 8 | plugins: [ 9 | new webpack.DefinePlugin({ 10 | "process.env.NODE_ENV": JSON.stringify("development") 11 | }) 12 | ] 13 | }); 14 | 15 | function getPlatformOverride(platform) { 16 | return merge(devConfig, { 17 | entry: ["./index.js"], 18 | output: { 19 | filename: `index.${platform.toLowerCase()}.js` 20 | }, 21 | plugins: [ 22 | new webpack.DefinePlugin({ 23 | "window.__OS": JSON.stringify(platform) 24 | }) 25 | ] 26 | }); 27 | } 28 | 29 | module.exports = [ 30 | getPlatformOverride("ANDROID"), 31 | getPlatformOverride("IOS"), 32 | getPlatformOverride("WEB") 33 | ]; 34 | -------------------------------------------------------------------------------- /src/helpers/ios/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | module.exports = { 27 | parseParams : require("./parseParams"), 28 | } 29 | -------------------------------------------------------------------------------- /src/helpers/web/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | module.exports = { 27 | parseParams : require("./parseParams"), 28 | } 29 | -------------------------------------------------------------------------------- /src/helpers/common/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | module.exports = { 27 | callbackMapper : require("./callbackMapper") 28 | } 29 | -------------------------------------------------------------------------------- /src/doms/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | function run() { 27 | if (window.__OS === "IOS") { 28 | return require("./ios") 29 | } else if (window.__OS === "WEB") { 30 | return require("./web") 31 | } else { 32 | return require("./android") 33 | } 34 | } 35 | 36 | module.exports = run(); -------------------------------------------------------------------------------- /src/helpers/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | const helpers = { 27 | common : require("./common") 28 | }; 29 | 30 | 31 | if (window.__OS == "ANDROID") { 32 | helpers.android = require("./android") 33 | } 34 | else if (window.__OS == "WEB") { 35 | helpers.web = require("./web"); 36 | } 37 | else { 38 | helpers.ios = require("./ios"); 39 | } 40 | 41 | 42 | module.exports = helpers; -------------------------------------------------------------------------------- /src/helpers/android/objMap.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | 27 | var map = { 28 | 'PARAMS': { 29 | 'required': 'width, height', 30 | 'viewMethod': 'setLayoutParams,getLayoutParams' 31 | }, 32 | 'DRAWABLE': { 33 | 'ctr': 'android.graphics.drawable.GradientDrawable->new', 34 | 'required': '', 35 | 'viewMethod': 'setBackground,getBackground' 36 | } 37 | } 38 | 39 | 40 | module.exports = map; 41 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | module.exports = { 27 | init: require("./src/init"), 28 | doms : require("./src/doms"), 29 | handler : require("./src/handler"), 30 | helpers : require("./src/helpers"), 31 | baseView : require("./src/baseView"), 32 | animations: require('./src/animations'), 33 | callbackMapper: require('./src/helpers/common/callbackMapper'), 34 | getOS: require('./src/helper').getOS, 35 | prestoMerge: require('./src/helper').merge, 36 | prestoClone: require('./src/helper').clone 37 | }; 38 | -------------------------------------------------------------------------------- /src/helpers/android/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | if (!String.prototype.startsWith) { 27 | Object.defineProperty(String.prototype, 'startsWith', { 28 | value: function(search, rawPos) { 29 | var pos = rawPos > 0 ? rawPos|0 : 0; 30 | return this.substring(pos, pos + search.length) === search; 31 | } 32 | }); 33 | } 34 | 35 | module.exports = { 36 | parseParams : require("./parseParams"), 37 | mapPrams : require("./mapParams"), 38 | callbackMapper : require("../common/callbackMapper") 39 | } 40 | -------------------------------------------------------------------------------- /src/helpers/ios/flattenObject.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | module.exports['flattenObject'] = function(ob) { 27 | var toReturn = {}; 28 | for (var i in ob) { 29 | if (!ob.hasOwnProperty(i)) continue; 30 | if ((typeof ob[i]) == 'object') { 31 | var flatObject = flattenObject(ob[i]); 32 | for (var x in flatObject) { 33 | if (!flatObject.hasOwnProperty(x)) continue; 34 | toReturn[x] = flatObject[x]; 35 | } 36 | } else { 37 | toReturn[i] = ob[i]; 38 | } 39 | } 40 | 41 | return toReturn; 42 | }; -------------------------------------------------------------------------------- /scripts/recompile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e; 4 | 5 | # Running conditional version upgrades 6 | LAST_COMMIT_MSG=$(git log -1) 7 | if echo "$LAST_COMMIT_MSG" | grep -E "!:";then 8 | echo "Matches !:.\nBumping major version" 9 | npx standard-version --no-verify --release-as major 10 | elif echo "$LAST_COMMIT_MSG" | grep -E "feat:|perf:|refactor:";then 11 | echo "Matches feat:|perf:|refactor:.\nBumping minor version." 12 | npx standard-version --no-verify --release-as minor 13 | elif echo "$LAST_COMMIT_MSG" | grep -E "fix:";then 14 | echo "Matches fix:.\nBumping patch version." 15 | npx standard-version --no-verify --release-as patch 16 | else 17 | echo "No defined Matches.\n No version bump." 18 | return; 19 | fi 20 | 21 | # Triggered via CI pipeline workflow on merge to release branch. 22 | # Generate changelog 23 | npx auto-changelog 24 | # Push updated files. 25 | # --------------------- 26 | # Extract last commit ticket 27 | TICKET=$(git log -1 | grep -o -E "[A-Z]+-[0-9]+" | head -n1) 28 | # Getting last generated tags 29 | LATEST_TAG=$(git describe --tags --abbrev=0) 30 | # Setting up few git properties. 31 | # SSH Url, Username and Email needs to be set to make sure 32 | # automation is pushed using Titan creds. 33 | git remote set-url origin ${BITBUCKET_GIT_SSH_ORIGIN} 34 | git config user.name "$GIT_USERNAME" 35 | git config user.email "$GIT_EMAIL" 36 | # Add and push updated files. 37 | # :Can be made better by lopping on files and only commiting if any files have changed. 38 | npm run compile 39 | git add docs/CHANGELOG.md package.json lib 40 | git commit -m "[skip ci] $TICKET: chore: Released $LATEST_TAG" 41 | git push origin 42 | # Pushing the tag manually instead of via standard-version library 43 | # because want to make sure above steps are completed before pushing a version to git. 44 | git tag -d $LATEST_TAG 45 | git tag $LATEST_TAG 46 | git push origin $LATEST_TAG 47 | # ----------------------- 48 | -------------------------------------------------------------------------------- /webpack.common.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack'); 3 | const WebpackBar = require('webpackbar'); 4 | const DashboardPlugin = require("webpack-dashboard/plugin"); 5 | const WebpackBuildNotifierPlugin = require('webpack-build-notifier'); 6 | const packageJSON = require("./package.json"); 7 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; 8 | 9 | const isWebpackDevServer = process.argv.some(a => path.basename(a) === 'serve'); 10 | const isWatch = process.argv.includes('--watch'); 11 | const isAnalyse = process.argv.includes('--analyse'); 12 | 13 | __VERSION__ = packageJSON.version 14 | 15 | var plugins = [ 16 | new WebpackBar(), 17 | new webpack.DefinePlugin({ 18 | // Set this to false to create a build that contains DateRangePicker for web 19 | '__ignoreDateRangePickerWeb': JSON.stringify(true), 20 | __VERSION__: JSON.stringify(packageJSON.version) 21 | }), 22 | ]; 23 | 24 | if (isWatch || isWebpackDevServer) { 25 | plugins.push( 26 | new DashboardPlugin(), 27 | new WebpackBuildNotifierPlugin({ 28 | title: "PrestoUI", 29 | }) 30 | ) 31 | } 32 | 33 | if (isAnalyse) { 34 | plugins.push( 35 | new BundleAnalyzerPlugin() 36 | ) 37 | } 38 | 39 | let config = { 40 | devtool: "source-map", 41 | output: { 42 | path: path.join(__dirname,"/lib"), 43 | filename: "index.js", 44 | libraryTarget: 'commonjs2', 45 | environment: { 46 | arrowFunction: false, 47 | bigIntLiteral: false, 48 | const: false, 49 | destructuring: false, 50 | dynamicImport: false, 51 | forOf: false, 52 | module: false 53 | }, 54 | }, 55 | module: { 56 | rules: [ 57 | { test: /\.js$/, 58 | exclude: /node_modules/, 59 | use: { 60 | loader: "babel-loader", 61 | options: { 62 | presets: ["@babel/preset-env"], 63 | } 64 | } 65 | }, 66 | ] 67 | }, 68 | plugins: plugins 69 | } 70 | 71 | module.exports = config -------------------------------------------------------------------------------- /src/helpers/common/callbackInvoker.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This function executes a function stored in a hashmap. 4 | It takes atleast a callback string (key for hashmap) to excute the function. 5 | In essence this is the read function for the global hashmap. 6 | ./callbackMapper.j is the write function for the global hashmap. 7 | 8 | While the function name contains `UI` in it, it is not related to UI is any way. 9 | It is mostly called after the UI is rendered but JBridge also calls it to call APIs. 10 | 11 | */ 12 | 13 | 14 | function invokeUICallback () { 15 | let args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, 16 | arguments)); 17 | var fName = args[0] 18 | var functionArgs = args.slice(1) 19 | var currTime; 20 | var timeDiff; 21 | 22 | if (window.__ALL_ONCLICKS && window.__ALL_ONCLICKS.indexOf(fName) != -1 && args[2] == "feedback" && JBridge && JBridge.setClickFeedback) { 23 | return JBridge.setClickFeedback(args[1]); 24 | } 25 | 26 | if (window.__THROTTELED_ACTIONS && window.__THROTTELED_ACTIONS.indexOf(fName) == -1) { 27 | let proxyFnKey = fName; 28 | if (proxyFnKey.charAt(0) == '"') 29 | proxyFnKey = fName.substring(1, fName.length - 1); 30 | window.__PROXY_FN[proxyFnKey].apply(null, functionArgs); 31 | } else if (window.__LAST_FN_CALLED && (fName == window.__LAST_FN_CALLED.fName)) { 32 | currTime = getCurrTime(); 33 | timeDiff = currTime - window.__LAST_FN_CALLED.timeStamp; 34 | 35 | if (timeDiff >= 300) { 36 | window.__PROXY_FN[fName].apply(null, functionArgs); 37 | window.__LAST_FN_CALLED.timeStamp = currTime; 38 | } else { 39 | console.warn("function throtteled", fName); 40 | console.warn("time diff", timeDiff); 41 | } 42 | } else { 43 | window.__PROXY_FN[fName].apply(null, functionArgs); 44 | window.__LAST_FN_CALLED = { 45 | timeStamp: (new Date()).getTime(), 46 | fName: fName 47 | } 48 | } 49 | }; 50 | 51 | module.exports = { 52 | invoke : invokeUICallback 53 | }; -------------------------------------------------------------------------------- /src/handler.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | module.exports = { 27 | handle : (ui, callback) => { 28 | if (!ui) { 29 | return; 30 | } 31 | if(ui.render) { 32 | if (typeof Android === "undefined") 33 | throw new Error("Android is undefined"); 34 | 35 | if (window.__OS != "ANDROID") 36 | return Android.Render(ui.render, null); 37 | else { 38 | if(typeof Android.getNewID == "function") { 39 | return Android.Render(JSON.stringify(ui.render), null, "false"); 40 | } else { 41 | return Android.Render(JSON.stringify(ui.render), null); 42 | } 43 | } 44 | } 45 | 46 | if(ui.runInUI) { 47 | Android.runInUI(ui.runInUI, null); 48 | } 49 | if(ui.addViewToParent) { 50 | Android.addViewToParent(ui.addViewToParent.parentId, JSON.stringify(ui.addViewToParent.jsx), ui.addViewToParent.index, null); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/computeIOS.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | 27 | 28 | var computeChildDimens = function(view) { 29 | if (view.props) { 30 | view.props.useConstraits = true; 31 | } 32 | // if (view.type == "linearLayout" && view.children && view.children.length){ 33 | // view.props.weightSum = 0.0; 34 | // view.props.diffHeight = 0.0; 35 | // for(var i in view.children){ 36 | // var child = view.children[i]; 37 | // if(child.props && child.props.weight){ 38 | // view.props.weightSum += parseInt(child.props.weight) 39 | // } else if(child.props && child.props.height) { 40 | // view.props.diffHeight += parseInt(child.props.height) 41 | // } 42 | // } 43 | // } 44 | return view; 45 | } 46 | 47 | module.exports = { 48 | computeLinearlayout:(view) => computeChildDimens(view), 49 | computeRelativeLayout: (view) => computeChildDimens(view), 50 | computeScrollView: (view) => computeChildDimens(view), 51 | computeChildDimens 52 | }; 53 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | # Presto UI 9 | 10 | 11 | 12 | > Javascript framework for building platform independent apps. 13 | 14 | [![npm version](https://img.shields.io/npm/v/presto-ui.svg?style=flat)](https://www.npmjs.com/package/presto-ui) 15 | 16 | Presto UI is a Javascript framework which connects with other Presto and Hyper libraries to provide a cross platform compatible and type safe environment for development. 17 | 18 | In itself presto-ui takes markup as input and generates commands consumed by low level platform specific libraries. 19 | 20 | For **web**, presto-ui generates vanilla js and css. 21 | 22 | For general use case you would need to use a micro-app sample and native test tools. 23 | 24 | ## Getting Started 25 | 26 | To add presto-ui to your repo you will need run below commands. 27 | ``` 28 | npm install presto-ui --save 29 | ``` 30 | ## Debugging 31 | 32 | For debugging presto-ui locally, you can start a server and link the project locally in node modules. 33 | 34 | Install dev and other dependencies. 35 | ``` 36 | npm install 37 | ``` 38 | This will install required dependencies and setup required git hooks. 39 | 40 | Start a local dev server 41 | ``` 42 | npm start 43 | ``` 44 | 45 | A server is started at [http://localhost:8080](http://localhost:8080) 46 | 47 | For debugging we will link presto-ui locally from current running server. 48 | 49 | In the project where you want to test presto-ui, run below commands to link presto-ui in node_modules. 50 | ``` 51 | rm -rf ./node_modules/presto-ui/lib 52 | ln -s ./node_modules/presto-ui/lib /lib 53 | ``` 54 | > After every `npm i` you will need to link again 55 | ## Contributing 56 | 57 | We use git hooks to verify commit messages so 58 | make sure to use `terminal` to commit your changes. 59 | 60 | > Do not use VSCode to commit any changes !!! 61 | 62 | Git push will fail if commit message is not proper. 63 | 64 | ``` 65 | git commit 66 | ``` 67 | or 68 | ``` zsh 69 | gc 70 | ``` 71 | 72 | Changelog is auto-generated. 73 | 74 | Make sure to add proper details in commits or PR's can be rejected. 75 | -------------------------------------------------------------------------------- /src/components/Button.js: -------------------------------------------------------------------------------- 1 | function Button() {} 2 | 3 | Button.prototype._renderMain = function(elem, props, renderEvent) { 4 | let guid = props.guid 5 | if(elem.getAttribute('guid')) 6 | guid = elem.getAttribute('guid') 7 | 8 | elem.style.cursor = 'pointer' 9 | elem.style.padding = 0 10 | elem.innerHTML = '' 11 | 12 | let linkElem = document.createElement('A') 13 | elem.appendChild(linkElem) 14 | 15 | linkElem.style.pointerEvents = 'auto' 16 | 17 | if (props.hasOwnProperty('disabled') && props.disabled) { 18 | elem.classList.add(window.__COM_CLASS_GROUP.BT_DISABLED) 19 | linkElem.style.pointerEvents = 'none' 20 | } 21 | 22 | let html = '' 23 | /* Button Image */ 24 | if (props.imageUrl) { // Image URL 25 | let imageUrl = props.imageUrl 26 | 27 | let temp = imageUrl.split('.') 28 | let ext = '' 29 | if(temp && temp.length > 0) 30 | ext = temp[temp.length - 1] 31 | 32 | let exts = ["jpeg", "jpg", "png", "bmp", "svg", "gif"] 33 | ext = ext.toLowerCase() 34 | 35 | if(!imageUrl.includes("data:image/svg+xml") && !exts.includes(ext)) { 36 | imageUrl += '.png' 37 | } 38 | 39 | html += '' 40 | } else if (props.iconName) { // Font Icon 41 | html += '' 42 | } 43 | 44 | /* Button Text */ 45 | if (props.text) 46 | html += "" + props.text + "" 47 | else 48 | html += "Button Text" 49 | 50 | linkElem.innerHTML = html 51 | 52 | if(props.hasOwnProperty('padding') && props.padding) { 53 | let padding = props.padding.split(',').map(a => a * 1) 54 | 55 | linkElem.style.padding = padding[1] + 'px ' + padding[2] + 'px ' + padding[3] + 'px ' + padding[0] + 'px' 56 | } else { 57 | linkElem.style.padding = 0 58 | } 59 | 60 | if (!props.stroke) { 61 | elem.style.border = '1px solid ' + window.__COM_COLOR_GROUP.BT_BORDER_COLOR 62 | } 63 | 64 | if (!props.backgroundColor) { 65 | elem.style.backgroundColor = window.__COM_COLOR_GROUP.BT_BG_COLOR 66 | } 67 | 68 | if (!props.color) { 69 | elem.style.color = window.__COM_COLOR_GROUP.BT_COLOR 70 | } 71 | } 72 | 73 | module.exports = new Button() -------------------------------------------------------------------------------- /src/helpers/common/callbackMapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | /* 27 | This takes a function and puts it in a hashmap called __PROXY_FN and returns the key (a string). 28 | Key is contructed using a global variables. 29 | Hashmap is also a global variable. 30 | Point To Note : Since iframes, we have different DOM Documents. So, these global variables are global to Presto-UI's document. 31 | */ 32 | module.exports.map = (fn) => { 33 | // console.debug("presto-ui callback-mapper document location",document.location); 34 | if(typeof window.__FN_INDEX !== 'undefined' && window.__FN_INDEX !== null) { 35 | var proxyFnName = 'F' + window.__FN_INDEX; 36 | if (window.__payload && window.__payload.service){ 37 | proxyFnName = window.__payload.service + "_" + proxyFnName; 38 | } 39 | window.__PROXY_FN[proxyFnName] = fn; 40 | window.__FN_INDEX++; 41 | // console.log("Presto-UI Callback Mapper proxyFnName is",proxyFnName); 42 | return proxyFnName; 43 | } else { 44 | throw new Error("Please initialise window.__FN_INDEX = 0 in index.js of your project."); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/doms/web.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | var parseParams = require('../helpers/web').parseParams; 27 | 28 | Array.prototype.flatten = function () { 29 | return this.reduce(function (prev, cur) { 30 | var more = [].concat(cur).some(Array.isArray); 31 | return prev.concat(more ? cur.flatten() : cur); 32 | }, []); 33 | }; 34 | 35 | module.exports = function (type, props, children, elemType, keyId) { 36 | // console.log("type is",type); 37 | // console.log("props are",props); 38 | // console.log("children is",children); 39 | // children = children.flatten(); 40 | // console.log("children after flatten is",children); 41 | 42 | if (!props) 43 | props = {}; 44 | 45 | if (typeof type === "string") { 46 | //props = parseParams(type, props); 47 | 48 | let obj = { 49 | props: props, 50 | type: type, 51 | children: children 52 | }; 53 | if(elemType){ 54 | obj.elemType = elemType; 55 | } 56 | if(keyId){ 57 | obj.keyId = keyId; 58 | } 59 | 60 | window.__VIEWS[props.id] = obj; 61 | window.__VIEW_DIMENSIONS[props.id] = null; 62 | return obj; 63 | } else { 64 | return new type(props, children); 65 | } 66 | } -------------------------------------------------------------------------------- /src/doms/ios.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | Array.prototype.flatten = function() { 27 | return this.reduce(function(prev, cur) { 28 | var more = [].concat(cur).some(Array.isArray); 29 | return prev.concat(more ? cur.flatten() : cur); 30 | },[]); 31 | }; 32 | 33 | function flattenObject(ob) { 34 | var toReturn = {}; 35 | for (var i in ob) { 36 | if (!ob.hasOwnProperty(i)) continue; 37 | if ((typeof ob[i]) == 'object') { 38 | var flatObject = flattenObject(ob[i]); 39 | for (var x in flatObject) { 40 | if (!flatObject.hasOwnProperty(x)) continue; 41 | if (typeof flatObject[x] !== "undefined") 42 | toReturn[x] = flatObject[x]; 43 | } 44 | } else { 45 | toReturn[i] = ob[i]; 46 | } 47 | } 48 | return toReturn; 49 | }; 50 | 51 | module.exports = function(type, props, ...children){ 52 | var paramType; 53 | 54 | children = children.flatten(); 55 | 56 | if (!props) 57 | props = {}; 58 | 59 | if(typeof type === "string") { 60 | props.node_id = window.__NODE_ID + ''; 61 | props = flattenObject(props); 62 | window.__NODE_ID++; 63 | children.forEach(child => { 64 | child.props.parentId = props.id; 65 | }) 66 | 67 | if (!props.__filename) 68 | props.__filename = "filename not added"; 69 | 70 | return {type: type, props: props, children: children} 71 | 72 | } else { 73 | return new type(props, children); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /scripts/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e; 4 | 5 | # Disabling post install on CI flag 6 | # CI = true is added by bitbucket 7 | # CI = 1 is added by Vercel 8 | # Might require more CI/Dev flags based on environments. 9 | if [ "$CI" == "true" ] || [ "$CI" == "1" ]; then 10 | echo "Skipping package's postinstall routine for CI environment"; 11 | exit 0; 12 | fi 13 | 14 | # Ensuring this scrip runs only for when current repo is in dev. 15 | # Adding to prepare command runs the scripts when lib 16 | # is used as a dependency. 17 | # Checking if Npm directory is same as current directory. 18 | if [ "$INIT_CWD" != "$PWD" ]; then 19 | echo "Skipping package's postinstall when used as an npm module"; 20 | exit 0; 21 | fi 22 | 23 | # Adding check for .npm folder 24 | if [[ $PWD =~ ".npm" ]]; then 25 | echo "Skipping package's postinstall when used as an npm module"; 26 | exit 0; 27 | fi 28 | 29 | # Setting other scripts as executable 30 | chmod +x ./scripts/pre-commit.sh 31 | chmod +x ./scripts/prepare-commit-msg.sh 32 | chmod +x ./scripts/commit-msg.sh 33 | chmod +x ./scripts/pre-push.sh 34 | 35 | # Check for Homebrew, 36 | # Install if we don't have it 37 | if ! [ -x "$(command -v brew)" ]; then 38 | # Ask for the administrator password upfront 39 | echo "------------------------------" 40 | echo "Sudo access is required for setup." 41 | sudo -v 42 | # Keep-alive: update existing `sudo` time stamp until `setup.sh` has finished 43 | while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null & 44 | 45 | echo "------------------------------" 46 | echo "Installing Xcode Command Line Tools." 47 | # Install Xcode command line tools 48 | xcode-select --install 49 | 50 | 51 | echo "Installing homebrew..." 52 | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 53 | else 54 | echo "Brew already installed." 55 | fi 56 | 57 | # Check for git 58 | # Install if we don't have it 59 | if ! [ -x "$(command -v git)" ]; then 60 | echo "Installing git..." 61 | brew install git 62 | else 63 | echo "GIT already installed." 64 | fi 65 | 66 | # Check for VSCode 67 | # Install if we don't have it 68 | if mdfind -name 'Visual Studio Code' | grep 'Visual Studio Code'; 69 | then 70 | echo 'VSCode already installed' 71 | else 72 | echo 'Installing VSCode' >&2 73 | brew install --cask visual-studio-code 74 | fi 75 | 76 | # Check for node 77 | # Install if we don't have it 78 | if ! [ -x "$(command -v node)" ]; then 79 | echo "Installing node..." 80 | brew install node 81 | else 82 | echo "Node already installed." 83 | fi 84 | 85 | # Installing plugins for VSCode 86 | # Incase command fails, in VSCode cmd+shift+p 87 | # and install 'code' command in path 88 | # code --install-extension jebbs.plantuml 89 | 90 | # husky install to setup pre-commit hooks 91 | npx husky install 92 | 93 | # Used to sync markdown files with confluence as part of bitbucket pipeline 94 | # TODO: Think if mark should be added everywhere? 95 | # +Helps verify HTML generated locally. 96 | # -?? 97 | brew tap kovetskiy/mark 98 | brew install mark -------------------------------------------------------------------------------- /src/components/SwitchButton.js: -------------------------------------------------------------------------------- 1 | function SwitchButton() {} 2 | 3 | SwitchButton.prototype._renderMain = function(elem, props, renderEvent) { 4 | if (renderEvent) { 5 | elem.innerHTML = '' 6 | 7 | let guid = props.guid 8 | if(elem.getAttribute('guid')) 9 | guid = elem.getAttribute('guid') 10 | 11 | this._renderStyle(elem, props, guid) 12 | 13 | let virtualElem = document.createElement('LABEL') 14 | virtualElem.classList.add(window.__COM_CLASS_GROUP.SWITCH_BODY) 15 | virtualElem.setAttribute('guid', guid) 16 | 17 | elem.appendChild(virtualElem) 18 | 19 | this._renderSlider(virtualElem, props, guid, renderEvent) 20 | } 21 | } 22 | 23 | SwitchButton.prototype._renderSlider = function(parentElem, props, guid, renderEvent) { 24 | let selectedDefault = false 25 | if(props.hasOwnProperty('selectedDefault')) 26 | selectedDefault = props.selectedDefault 27 | 28 | let checkbox = document.createElement('INPUT') 29 | checkbox.setAttribute('type', 'checkbox') 30 | 31 | if(selectedDefault) { 32 | checkbox.setAttribute('checked', 'checked') 33 | } 34 | 35 | let elem = document.createElement('SPAN') 36 | elem.classList.add(window.__COM_CLASS_GROUP.SWITCH_SLIDER) 37 | 38 | parentElem.appendChild(checkbox) 39 | parentElem.appendChild(elem) 40 | 41 | // Events 42 | if (renderEvent && props.onSwitch && typeof props.onSwitch == "function") { 43 | checkbox.addEventListener('change', function() { 44 | props.onSwitch(this.checked) 45 | }) 46 | } 47 | } 48 | 49 | SwitchButton.prototype._renderStyle = function(parentElem, props, guid) { 50 | let gap = 4 51 | let parentWidth = parentElem.offsetWidth 52 | let parentHeight = parentElem.offsetHeight 53 | 54 | let size = Math.min(parentWidth, parentHeight) 55 | let actualSize = size - 2 * gap 56 | if (actualSize < 0) { 57 | actualSize = size 58 | gap = 0 59 | } 60 | 61 | let tx = parentWidth - 2 * gap - actualSize 62 | if (tx < 0) 63 | tx = 0 64 | 65 | let styleElem = document.createElement('style') 66 | styleElem.type = 'text/css' 67 | 68 | let css = '' 69 | css += '.' + window.__COM_CLASS_GROUP.SWITCH_BODY + '[guid="' + guid + '"] .' + window.__COM_CLASS_GROUP.SWITCH_SLIDER + '{border-radius: ' + size + 'px} ' 70 | css += '.' + window.__COM_CLASS_GROUP.SWITCH_BODY + '[guid="' + guid + '"] .' + window.__COM_CLASS_GROUP.SWITCH_SLIDER + ':before {border-radius: 50%; content: ""; position: absolute; width: ' + actualSize + 'px; height: ' + actualSize + 'px; left: ' + gap + 'px; bottom: ' + gap + 'px; background-color: white; -webkit-transition: .4s; transition: .4s;} ' 71 | css += '.' + window.__COM_CLASS_GROUP.SWITCH_BODY + '[guid="' + guid + '"] input:checked + .' + window.__COM_CLASS_GROUP.SWITCH_SLIDER + ':before {transform: translateX(' + tx + 'px);} ' 72 | 73 | styleElem.appendChild(document.createTextNode(css)) 74 | document.head.appendChild(styleElem) 75 | } 76 | 77 | module.exports = new SwitchButton() -------------------------------------------------------------------------------- /src/WEB/ViewPageAdapter.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | var Renderer = require("./Render"); 27 | 28 | function generateTabWrapper () { 29 | var elem = document.createElement("div"); 30 | 31 | elem.className = "tab"; 32 | 33 | return elem; 34 | } 35 | 36 | function generateTabTitleWrapper () { 37 | var elem = document.createElement("div"); 38 | elem.className = "tab-titleWrapper"; 39 | 40 | return elem; 41 | } 42 | 43 | function generateTabContentWrapper () { 44 | var elem = document.createElement("div"); 45 | 46 | elem.className = "tab-contentWrapper"; 47 | 48 | return elem; 49 | } 50 | 51 | function generateTabTitleEl (title) { 52 | var elem = document.createElement("div"); 53 | 54 | elem.innerHTML = title; 55 | elem.className = "tab-title"; 56 | 57 | return elem; 58 | }; 59 | 60 | function generateTabContentEl (view) { 61 | var elem = Renderer.inflateView({view:view.view}); 62 | elem.className += " tab-content"; 63 | 64 | return elem; 65 | } 66 | 67 | var createTabs = function(Android, id, tabJsx, jsx, cb) { 68 | var view = generateTabWrapper(); 69 | var tabTitleWrapper = generateTabTitleWrapper(); 70 | var tabContentWrapper = generateTabContentWrapper(); 71 | var tabItem; 72 | 73 | view.appendChild(tabTitleWrapper); 74 | view.appendChild(tabContentWrapper); 75 | 76 | jsx.forEach((each, index) => { 77 | tabItem = generateTabTitleEl(each.value); 78 | 79 | if (index == 0) 80 | tabItem.className += " tab-title-active"; 81 | 82 | tabTitleWrapper.appendChild(tabItem); 83 | }); 84 | 85 | tabJsx.forEach((each, index) => { 86 | tabItem = generateTabContentEl(each); 87 | 88 | if (index == 0) 89 | tabItem.className += " tab-content-active"; 90 | 91 | tabContentWrapper.appendChild(tabItem); 92 | }); 93 | 94 | document.getElementById(id).appendChild(view); 95 | 96 | window.callUICallback(cb , "0"); 97 | }; 98 | 99 | module.exports = { 100 | createTabs: createTabs, 101 | } 102 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "presto-ui", 3 | "version": "1.4.1", 4 | "description": "Javascript framework for building platform independent apps.", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "prepare": "./scripts/setup.sh", 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "compile": "webpack --mode=production --config webpack.prod.js", 10 | "watch": "webpack --mode=development --config webpack.dev.js --watch --progress" 11 | }, 12 | "devDependencies": { 13 | "@babel/core": "^7.14.3", 14 | "@babel/preset-env": "^7.3.1", 15 | "@commitlint/cli": "^16.2.3", 16 | "@commitlint/config-conventional": "^16.2.1", 17 | "@digitalroute/cz-conventional-changelog-for-jira": "^7.2.1", 18 | "auto-changelog": "^2.4.0", 19 | "babel-loader": "^8.0.5", 20 | "commitizen": "^4.2.4", 21 | "commitlint-config-jira": "^1.6.3", 22 | "commitlint-plugin-jira-rules": "^1.6.3", 23 | "husky": "^7.0.0", 24 | "prettier": "2.6.2", 25 | "standard-version": "^9.3.2", 26 | "webpack": "^5.37.1", 27 | "webpack-build-notifier": "^2.2.1", 28 | "webpack-bundle-analyzer": "^4.4.2", 29 | "webpack-cli": "^4.7.0", 30 | "webpack-dashboard": "^3.3.3", 31 | "webpack-merge": "^5.7.3", 32 | "webpackbar": "^5.0.0-3" 33 | }, 34 | "config": { 35 | "commitizen": { 36 | "path": "./node_modules/@digitalroute/cz-conventional-changelog-for-jira", 37 | "jiraPrefix": "PICAF", 38 | "jiraLocation": "pre-type", 39 | "jiraAppend": ":" 40 | } 41 | }, 42 | "standard-version": { 43 | "skip": { 44 | "commit": true, 45 | "changelog": true 46 | }, 47 | "types": [ 48 | { 49 | "type": "feat", 50 | "section": "Features", 51 | "release": "minor" 52 | }, 53 | { 54 | "type": "fix", 55 | "section": "Bug Fixes", 56 | "release": "minor" 57 | }, 58 | { 59 | "type": "test", 60 | "section": "Tests" 61 | }, 62 | { 63 | "type": "build", 64 | "section": "Build System" 65 | }, 66 | { 67 | "type": "docs", 68 | "section": "Documentation" 69 | }, 70 | { 71 | "type": "style", 72 | "hidden": true 73 | }, 74 | { 75 | "type": "refactor", 76 | "section": "Improvements" 77 | }, 78 | { 79 | "type": "perf", 80 | "section": "Improvements" 81 | }, 82 | { 83 | "type": "ci", 84 | "hidden": true 85 | }, 86 | { 87 | "type": "chore", 88 | "hidden": true 89 | } 90 | ], 91 | "issuePrefixes": [ 92 | "PICAF-" 93 | ], 94 | "issueUrlFormat": "https://juspay.atlassian.net/browse/{{prefix}}{{id}}", 95 | "commitUrlFormat": "https://bitbucket.org/juspay/presto-ui/commits/{{hash}}", 96 | "compareUrlFormat": "https://bitbucket.org/juspay/presto-ui/branches/compare/{{currentTag}}%0D{{previousTag}}", 97 | "releaseCommitMessageFormat": "chore(release): {{currentTag}} {{prefix}}{{id}}" 98 | }, 99 | "commitlint": { 100 | "plugins": [ 101 | "commitlint-plugin-jira-rules" 102 | ], 103 | "extends": [ 104 | "jira" 105 | ], 106 | "rules": { 107 | "jira-task-id-max-length": [ 108 | 0 109 | ] 110 | } 111 | }, 112 | "auto-changelog": { 113 | "output": "docs/CHANGELOG.md", 114 | "template": "./docs/templates/changelog.hbs", 115 | "commitLimit": false, 116 | "breakingPattern": "(!:)", 117 | "issueUrl": "https://juspay.atlassian.net/browse/{id}", 118 | "issuePattern": "[A-Z]{2,}-\\d+", 119 | "compareUrl": "https://bitbucket.org/juspay/presto-ui/branches/compare/{to}%0D{from}", 120 | "releaseSummary": true, 121 | "ignoreCommitPattern": "(\\[skip ci\\])", 122 | "startingVersion": "v1.0.0" 123 | }, 124 | "repository": { 125 | "type": "git", 126 | "url": "git@bitbucket.org:juspay/presto-ui.git" 127 | }, 128 | "author": "JUSPAY Technologies ", 129 | "license": "AGPL-3.0", 130 | "homepage": "https://bitbucket.org/juspay/presto-ui" 131 | } 132 | -------------------------------------------------------------------------------- /src/components/DateRangePicker.js: -------------------------------------------------------------------------------- 1 | const $ = require('jquery') 2 | const moment = require('moment') 3 | const daterangepicker = require('daterangepicker') 4 | 5 | function DateRangePicker() {} 6 | 7 | DateRangePicker.prototype._getBodyObject = function(guid) { 8 | let query = '.' + window.__COM_CLASS_GROUP.DRP_BODY + '[guid="' + guid +'"]' 9 | return document.body.querySelector(query) 10 | } 11 | 12 | DateRangePicker.prototype._getStartDate = function(props) { 13 | let start = moment().subtract(29, 'days') 14 | 15 | if(props.startDate && props.startDate.trim() != "") { 16 | start = moment(props.startDate) 17 | } 18 | 19 | return start 20 | } 21 | 22 | DateRangePicker.prototype._getEndDate = function(props) { 23 | let end = moment() 24 | 25 | if(props.endDate && props.endDate.trim() != "") { 26 | end = moment(props.endDate) 27 | } 28 | 29 | return end 30 | } 31 | 32 | DateRangePicker.prototype._renderText = function(elem, start, end) { 33 | let children = elem.childNodes 34 | let article = null 35 | 36 | if(children.length){ 37 | for(let i = 0; i < children.length; i++){ 38 | if(children[i].nodeName.toLowerCase() == 'article'){ 39 | article = children[i] 40 | break 41 | } 42 | } 43 | } 44 | 45 | if (article) { 46 | article.innerHTML = start.format('MMMM D, YYYY') + ' - ' + end.format('MMMM D, YYYY') 47 | } 48 | 49 | elem.style.color = window.__COM_COLOR_GROUP.INACTIVE_COLOR 50 | } 51 | 52 | DateRangePicker.prototype._renderMain = function(elem, props, renderEvent) { 53 | // GUID 54 | let guid = props.guid 55 | if(elem.getAttribute('guid')) 56 | guid = elem.getAttribute('guid') 57 | 58 | if(renderEvent) { 59 | let start = this._getStartDate(props) 60 | let end = this._getEndDate(props) 61 | 62 | let cb = (start, end) => { 63 | let res = { 64 | startLabel: start.format('MMMM D, YYYY'), 65 | endLabel: end.format('MMMM D, YYYY'), 66 | startDate: start.format('YYYY-MM-DD'), 67 | endDate: end.format('YYYY-MM-DD') 68 | } 69 | 70 | /* Update UI */ 71 | let bodyElem = this._getBodyObject(guid) 72 | this._renderText(bodyElem, start, end) 73 | 74 | if (!window.__COM_RENDERED.DRP[guid]) 75 | window.__COM_RENDERED.DRP[guid] = {} 76 | window.__COM_RENDERED.DRP[guid].start = start 77 | window.__COM_RENDERED.DRP[guid].end = end 78 | 79 | /* Events Trigger */ 80 | if (props.onDateChange && typeof props.onDateChange == "function") { 81 | props.onDateChange(JSON.stringify(res)) 82 | } 83 | } 84 | 85 | $('.' + window.__COM_CLASS_GROUP.DRP + '[guid='+ guid +']').daterangepicker({ 86 | startDate: start, 87 | endDate: end, 88 | ranges: { 89 | 'Today': [moment(), moment()], 90 | 'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')], 91 | 'Last 7 Days': [moment().subtract(6, 'days'), moment()], 92 | 'Last 30 Days': [moment().subtract(29, 'days'), moment()], 93 | 'This Month': [moment().startOf('month'), moment().endOf('month')], 94 | 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')] 95 | } 96 | }, cb) 97 | } 98 | } 99 | 100 | DateRangePicker.prototype._renderBody = function(elem, props, renderEvent) { 101 | // GUID 102 | let guid = props.guid 103 | if(elem.getAttribute('guid')) 104 | guid = elem.getAttribute('guid') 105 | 106 | let start = this._getStartDate(props) 107 | let end = this._getEndDate(props) 108 | 109 | if (window.__COM_RENDERED.DRP[guid]) { 110 | if (window.__COM_RENDERED.DRP[guid].start) 111 | start = window.__COM_RENDERED.DRP[guid].start 112 | if (window.__COM_RENDERED.DRP[guid].end) 113 | end = window.__COM_RENDERED.DRP[guid].end 114 | } 115 | 116 | this._renderText(elem, start, end) 117 | } 118 | 119 | module.exports = new DateRangePicker() -------------------------------------------------------------------------------- /src/WEB/ListPresto.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | //view.data= itemView && holderViews 4 | //itemView=template 5 | //holderViews= array of props that will be changed from the template 6 | 7 | const {invoke} = require("../helpers/common/callbackInvoker") 8 | const {clone} = require("../helper") 9 | 10 | //sets the holder attributes 11 | let setChildAttributes = function(view,holder,itemData,index){ 12 | 13 | for(var key in holder) { 14 | if(key == 'onClick') { 15 | let onClickFunction =()=> invoke(holder.onClick,index); 16 | view.props.onClick = onClickFunction; 17 | } else if (itemData.hasOwnProperty(holder[key])) { 18 | view.props[key] = itemData[holder[key]] 19 | } 20 | } 21 | } 22 | 23 | let setList = function(view,holderViews,itemData,index){ 24 | if(holderViews[view.props.holderId]){ 25 | setChildAttributes(view,holderViews[view.props.holderId], itemData,index); 26 | } 27 | for(let i=0;i10?10:newItems.length; 58 | for(let i =0;i isNewInflated 137 | //add spaces 138 | //add summary -------------------------------------------------------------------------------- /src/IOS/Render.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | const parseParams = require('../helpers/ios/parseParams'); 27 | const {computeChildDimens} = require('../computeIOS'); 28 | const helper = require('../helper'); 29 | 30 | 31 | function inflate(view, namespace) { 32 | const id = view.props.id; 33 | 34 | if (!window.__VIEWS.hasOwnProperty(id)) { 35 | computeChildDimens(view); 36 | helper.cacheDimen(view); 37 | window.__VIEWS[id] = view; 38 | const newView = helper.clone(view,null); 39 | view.children.forEach((child, i) => { 40 | newView.children[i] = inflate(child); 41 | }); 42 | const obj = parseParams(newView.type, newView.props, "set", namespace); 43 | newView.props = obj.config; 44 | newView.type = obj.type[0].toUpperCase() + obj.type.substr(1, obj.type.length); 45 | return newView; 46 | } 47 | 48 | const move = helper.shouldMove(view); 49 | helper.cacheDimen(view); 50 | let ranRunInUI = false; 51 | if (move) { 52 | move.id = id; 53 | runInUIHelper(view.type, view.props, undefined, namespace); 54 | ranRunInUI = true; 55 | } 56 | 57 | computeChildDimens(view) 58 | view.children.forEach(function(v) { 59 | inflate(v, namespace) 60 | }); 61 | return ranRunInUI; 62 | } 63 | 64 | function runInUI(cmd, fromInflate, namespace) { 65 | if (!(cmd instanceof Array)) cmd = [cmd]; 66 | 67 | if (cmd.length==1 && cmd[0]=="removeAllUI"){ 68 | window.webkit.messageHandlers.IOS.postMessage(JSON.stringify({ 69 | methodName: "removeAllUI", 70 | parameters: {"animated":"false", namespace : namespace} 71 | })); 72 | }else{ 73 | cmd.forEach(function (each) { 74 | var id = each.id; 75 | each.useConstraits = true; 76 | var view = window.__VIEWS[id]; 77 | if (view) { 78 | var parent = window.__VIEWS[view.props.parentId]; 79 | view.props = helper.merge(view.props, each); 80 | if(view.props && view.props.hasOwnProperty("listItem") && each.hasOwnProperty("listData") && !each.hasOwnProperty("listItem")){ 81 | each.listItem = view.props.listItem 82 | } 83 | //Adding as stop gag solution for editText in ios where text 84 | //was getting set empty in case other properties were modified. 85 | if (!each.hasOwnProperty('text') && view.props) { 86 | delete view.props.text; 87 | } 88 | if (each.hasOwnProperty('textFromHtml') && view.props) { 89 | if (view.props.hasOwnProperty('color')) { 90 | each.color = view.props.color; 91 | } 92 | if (view.props.hasOwnProperty('fontStyle')) { 93 | each.fontStyle = view.props.fontStyle; 94 | } 95 | if (view.props.hasOwnProperty('textSize')) { 96 | each.textSize = view.props.textSize; 97 | } 98 | } 99 | if (parent && !fromInflate) { 100 | if (parent.type.indexOf("scroll") != -1) { 101 | inflate(parent); 102 | } 103 | computeChildDimens(parent); 104 | var children = parent.children; 105 | if (!inflate(view)) { 106 | runInUIHelper(view.type, each, true, namespace); 107 | }; 108 | for (var i = 0, len = children.length; i < len; i++) { 109 | if (view != children[i]) { 110 | inflate(children[i], namespace); 111 | } 112 | } 113 | } else { 114 | runInUIHelper(view.type, each, undefined, namespace); 115 | } 116 | } 117 | }); 118 | } 119 | }; 120 | 121 | function runInUIHelper(type, obj, removeFrame, namespace) { 122 | var cmd = parseParams(type, obj, "get", namespace).config.methods; 123 | if(removeFrame){ 124 | cmd = cmd.filter(function(itm){ 125 | return itm.methodName != "setFrame:" 126 | }) 127 | } 128 | window.webkit.messageHandlers.IOS.postMessage(JSON.stringify({ 129 | methodName: "runInUI", 130 | parameters: cmd 131 | })); 132 | } 133 | 134 | module.exports = { 135 | inflate, 136 | computeChildDimens, 137 | runInUI 138 | }; 139 | -------------------------------------------------------------------------------- /src/WEB/Utils.js: -------------------------------------------------------------------------------- 1 | function parseColors(color) { 2 | if (color.length < 8) 3 | return color; 4 | 5 | if (color.indexOf("rgba") !== -1 || color.indexOf("rgb") !== -1) 6 | return color; 7 | 8 | var alpha = parseInt(color.substring(1, 3), 16); 9 | alpha = (alpha / 255).toFixed(2); 10 | 11 | var hexColor = color.substring(3, 9); 12 | var rgbaColor = "rgba(" + convertHexToRgb(hexColor) + "," + alpha + ")"; 13 | 14 | return rgbaColor; 15 | } 16 | 17 | let postRenderElements = {} 18 | 19 | function rWS(value) { 20 | return value.replace(/ /g, ''); 21 | } 22 | 23 | function cS(value) { 24 | return value ? value + '' : ""; 25 | } 26 | 27 | function convertColorToRgba(color) { 28 | color = rWS(cS(color)); 29 | 30 | var values; 31 | var alpha = "1.00"; 32 | 33 | if (color.length >= 8) { 34 | alpha = parseInt(color.substring(1, 3), 16); 35 | alpha = (alpha / 255).toFixed(2); 36 | color = color.substring(3, 9); 37 | } else { 38 | color = color.substring(1, color.length); 39 | } 40 | 41 | color = convertHexToRgb(rWS(color)); 42 | values = color.split(','); 43 | 44 | return { 45 | r: parseInt(rWS(values[0])), 46 | g: parseInt(rWS(values[1])), 47 | b: parseInt(rWS(values[2])), 48 | a: parseFloat(alpha) 49 | }; 50 | } 51 | 52 | function getValueFromPixel (pixel) { return parseInt(pixel.substring(0,pixel.length-1)) }; 53 | 54 | function calculateHeight (elem) { 55 | if(!elem) return 0; 56 | let childNodes = elem.children; 57 | let maxHeight = 0; 58 | for (var i = 0; i < childNodes.length; ++i) { 59 | let style = childNodes[i].currentStyle || window.getComputedStyle(childNodes[i]); 60 | maxHeight = maxHeight > childNodes[i].offsetHeight ? maxHeight : (childNodes[i].offsetHeight + getValueFromPixel(style.marginTop) + getValueFromPixel(style.marginBottom)); 61 | } 62 | return maxHeight; 63 | } 64 | 65 | function convertHexToRgb(hex) { 66 | var r = parseInt(hex.substring(0, 2), 16); 67 | var g = parseInt(hex.substring(2, 4), 16); 68 | var b = parseInt(hex.substring(4, 6), 16); 69 | 70 | return r + "," + g + "," + b; 71 | } 72 | 73 | const state = { 74 | fragments : {}, 75 | fragmentTypes : {}, 76 | mainRootView : { 77 | type: "relativeLayout", 78 | props: { 79 | "h": document.getElementById("content").clientHeight, 80 | "w": document.getElementById("content").clientWidth 81 | } 82 | } 83 | } 84 | 85 | function addToContainerList(id , namespace){ 86 | let container = getContainer(namespace, true); 87 | if(container) 88 | { 89 | let key = function () { 90 | function s4() { 91 | return Math.floor((1 + Math.random()) * 0x10000) 92 | .toString(16) 93 | .substring(1); 94 | } 95 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 96 | s4() + '-' + s4() + s4() + s4(); 97 | }(); 98 | state.fragments[key] = document.getElementById(id); 99 | state.fragmentTypes[key] = window.__VIEWS[id]; 100 | return key; 101 | } 102 | return "__failed" 103 | } 104 | 105 | function getContainer( namespace ){ 106 | if(namespace){ 107 | let container = state.fragments[namespace]; 108 | if(container) 109 | return container; 110 | else return undefined; 111 | } 112 | return document.getElementById("content"); 113 | } 114 | 115 | function getParentView(namespace, view) { 116 | if (window.__VIEWS && !window.__VIEWS.content) { 117 | window.__VIEWS.content = state.mainRootView; 118 | } 119 | if (namespace) { 120 | let containerType = state.fragmentTypes[namespace]; 121 | if (containerType) { 122 | containerType.children = containerType.children || [] 123 | containerType.children.push(view); 124 | containerType.oldView = true; 125 | return containerType; 126 | } 127 | } 128 | 129 | if (state.mainRootView.children) { 130 | state.mainRootView.oldView = true; 131 | } 132 | state.mainRootView.children = state.mainRootView.children || []; 133 | state.mainRootView.children.push(view); 134 | return state.mainRootView; 135 | } 136 | 137 | // function modifyTranslation(config){ 138 | // var x = config.x || "0"; 139 | // var y = config.y || "0"; 140 | // var animationArray = JSON.parse(config.inlineAnimation); 141 | // var animationArray = animationArray.map(function(a){ 142 | // if(a.hasOwnProperty("fromX")){ 143 | // a.fromX = parseInt(a.fromX) + parseInt(x) + ''; 144 | // if(!a.hasOwnProperty("toX")){ 145 | // a.toX = 0 + parseInt(x) + ''; 146 | // } 147 | // } 148 | // if(a.hasOwnProperty("toX")){ 149 | // a.toX = parseInt(a.toX) + parseInt(x) + ''; 150 | // } 151 | // if(a.hasOwnProperty("fromY")){ 152 | // a.fromY = parseInt(a.fromY) + parseInt(y) + ''; 153 | // if(!a.hasOwnProperty("toY")){ 154 | // a.toY = 0 + parseInt(y) + ''; 155 | // } 156 | // } 157 | // if(a.hasOwnProperty("toY")){ 158 | // a.toY = parseInt(a.toY) + parseInt(y) + ''; 159 | // } 160 | // return a; 161 | // }) 162 | // return (animationArray); 163 | // } 164 | 165 | module.exports = { 166 | parseColors, 167 | rWS, 168 | cS, 169 | convertColorToRgba, 170 | addToContainerList, 171 | getParentView, 172 | getContainer, 173 | calculateHeight, 174 | postRenderElements 175 | } 176 | -------------------------------------------------------------------------------- /src/animations.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | const BaseView = require('./baseView'); 27 | const VIEW = new BaseView(null, null, {}); 28 | 29 | // Object of types of animations available on screen change 30 | const AnimationTypes = { 31 | SLIDE: { 32 | name: "slide", 33 | handler: slideHandler 34 | }, 35 | SLIDE_VERTICAL: { 36 | name: "slide_down", 37 | handler: slideDownHandler 38 | }, 39 | SIMPLE: { 40 | name: "simple", 41 | handler: simpleHandler 42 | } 43 | } 44 | 45 | const DEFAULT_ANIMATION = AnimationTypes.SLIDE; 46 | 47 | function simpleHandler(fromId, toId, direction) { 48 | let cmd = VIEW.cmd({ 49 | id: toId, 50 | translationZ: ++window.ZIndex 51 | }); 52 | return cmd; 53 | } 54 | 55 | function slideDownHandler(fromId, toId, direction) { 56 | let container = VIEW.cmdContainer(); 57 | let cmd; 58 | 59 | if (direction == 1) { 60 | cmd = VIEW.cmd({ 61 | id: toId, 62 | translationY: window.__HEIGHT, 63 | a_duration: '300', 64 | a_translationY: '0', 65 | translationZ: ++window.ZIndex, 66 | }); 67 | container = container.addCmd(cmd); 68 | 69 | cmd = VIEW.cmd({ 70 | id: fromId, 71 | a_translationY: -0.2 * window.__HEIGHT, 72 | a_duration: '300' 73 | }); 74 | container = container.addCmd(cmd); 75 | 76 | return container; 77 | } 78 | 79 | window.ZIndex++; 80 | cmd = VIEW.cmd({ 81 | id: fromId, 82 | translationY: 0, 83 | a_translationY: window.__HEIGHT, 84 | a_duration: '300', 85 | translationZ: ++window.ZIndex 86 | }); 87 | container = container.addCmd(cmd); 88 | 89 | cmd = VIEW.cmd({ 90 | id: toId, 91 | translationZ: window.ZIndex-1, 92 | a_translationY: '0', 93 | translationY: -0.2 * window.__HEIGHT, 94 | a_duration: '300', 95 | }); 96 | container = container.addCmd(cmd); 97 | 98 | return container; 99 | } 100 | 101 | function slideHandler(fromId, toId, direction) { 102 | let container = VIEW.cmdContainer(); 103 | let cmd; 104 | if (direction == 1) { 105 | cmd = VIEW.cmd({ 106 | id: toId, 107 | visibility: 'visible', 108 | translationX: window.__WIDTH, 109 | a_duration: '300', 110 | a_translationX: '0', 111 | translationZ: ++window.ZIndex, 112 | }); 113 | container = container.addCmd(cmd); 114 | cmd = VIEW.cmd({ 115 | id: fromId, 116 | translationX: '0', 117 | visibility: 'visible', 118 | a_duration: '300', 119 | a_translationX: (-.2 * window.__WIDTH) + '', 120 | }); 121 | container = container.addCmd(cmd); 122 | } 123 | 124 | if (direction == -1) { 125 | window.ZIndex++; 126 | cmd = VIEW.cmd({ 127 | id: fromId, 128 | translationX: '0', 129 | a_duration: '300', 130 | a_translationX: window.__WIDTH, 131 | translationZ: ++window.ZIndex, 132 | }); 133 | container = container.addCmd(cmd); 134 | 135 | cmd = VIEW.cmd({ 136 | id: toId, 137 | visibility: 'visible', 138 | translationX: (-.2 * window.__WIDTH) + '', 139 | a_duration: '300', 140 | a_translationX: '0', 141 | translationZ: window.ZIndex - 1, 142 | }); 143 | container = container.addCmd(cmd); 144 | } 145 | 146 | return container; 147 | } 148 | 149 | /* 150 | Generic Handler for Animation 151 | Arguments: 152 | fromViewId: Number - Id of the view currently visible on the screen 153 | toViewID: Number - Id of the view which has to be brought on the screen 154 | ID: Number - Id of the view to be animated 155 | animator: Obj - {name:string, handler:function} 156 | direction: Number - Represents of the direction of the screen 157 | -1 : Go back to the already rendered screen (right to left) 158 | 1 : Go to the new screen (left to right) 159 | */ 160 | function getAnimation(fromViewID, toViewId, direction, animator) { 161 | if (!animator) 162 | animator = DEFAULT_ANIMATION; 163 | 164 | if (typeof animator === "object" && typeof animator.handler === "function") 165 | return animator.handler(fromViewID, toViewId, direction); 166 | else 167 | console.log(new Error("Animation handler is not a function, type: " + 168 | animator)); 169 | } 170 | 171 | module.exports.types = AnimationTypes; 172 | module.exports.getAnimation = getAnimation; -------------------------------------------------------------------------------- /src/helper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | // Following functions are used by WEB and IOS Render 27 | function cacheDimen(view) { 28 | let props = view.props; 29 | let dimen = { 30 | w: props.w, 31 | h: props.h, 32 | x: props.x, 33 | y: props.y, 34 | gravity: props.gravity, 35 | padding: props.padding, 36 | orientation: props.orientation, 37 | stroke: props.stroke ? props.stroke.split(",")[0] * 1 : 0, 38 | } 39 | window.__VIEW_DIMENSIONS[props.id] = dimen; 40 | } 41 | 42 | function shouldMove(view) { 43 | let props = view.props; 44 | let dimen = { 45 | w: props.w, 46 | h: props.h, 47 | x: props.x, 48 | y: props.y 49 | }; 50 | let cachedDimen = window.__VIEW_DIMENSIONS[props.id]; 51 | let changed = false; 52 | if (!cachedDimen) 53 | return dimen; 54 | for (let key in dimen) { 55 | if (cachedDimen[key] != dimen[key]) { 56 | dimen.id = props.id; 57 | changed = true; 58 | } 59 | } 60 | if (changed) 61 | return dimen; 62 | return null; 63 | } 64 | 65 | function getOS() { 66 | var userAgent = navigator.userAgent; 67 | if (!userAgent) 68 | return console.error(new Error("UserAgent is null")); 69 | if (userAgent.includes("Android") && userAgent.includes("Version")) 70 | return "ANDROID"; 71 | if ((userAgent.includes("iPhone") || userAgent.includes("iPad")) && !userAgent.includes("Version")) 72 | return "IOS"; 73 | return "WEB"; 74 | } 75 | 76 | function clearViewExternals(view) { 77 | if (!view) 78 | return; 79 | 80 | delete window.__VIEWS[view.props.id]; 81 | delete window.__VIEW_DIMENSIONS[view.props.id]; 82 | 83 | if(__OBSERVERS[view.props.id]) 84 | delete window.__OBSERVERS[view.props.id]; 85 | 86 | view.children.forEach(clearViewExternals); 87 | } 88 | 89 | // use: clone( ) returns 90 | function clone(o, m){ 91 | // return non object values 92 | if('object' !==typeof o) return o 93 | // m: a map of old refs to new object refs to stop recursion 94 | if('object' !==typeof m || null ===m) m =new WeakMap() 95 | var n =m.get(o) 96 | if('undefined' !==typeof n) return n 97 | // shallow/leaf clone object 98 | var c =Object.getPrototypeOf(o).constructor 99 | // TODO: specialize copies for expected built in types i.e. Date etc 100 | switch(c) { 101 | // shouldn't be copied, keep reference 102 | case Boolean: 103 | case Error: 104 | case Function: 105 | case Number: 106 | case Promise: 107 | case String: 108 | case Symbol: 109 | case WeakMap: 110 | case WeakSet: 111 | n =o 112 | break; 113 | // array like/collection objects 114 | case Array: 115 | m.set(o, n =o.slice(0)) 116 | // recursive copy for child objects 117 | n.forEach(function(v,i){ 118 | if('object' ===typeof v) n[i] =clone(v, m) 119 | }); 120 | break; 121 | case ArrayBuffer: 122 | m.set(o, n =o.slice(0)) 123 | break; 124 | case DataView: 125 | m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.byteLength)) 126 | break; 127 | case Map: 128 | case Set: 129 | m.set(o, n =new (c)(clone(Array.from(o.entries()), m))) 130 | break; 131 | case Int8Array: 132 | case Uint8Array: 133 | case Uint8ClampedArray: 134 | case Int16Array: 135 | case Uint16Array: 136 | case Int32Array: 137 | case Uint32Array: 138 | case Float32Array: 139 | case Float64Array: 140 | m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.length)) 141 | break; 142 | // use built in copy constructor 143 | case Date: 144 | case RegExp: 145 | m.set(o, n =new (c)(o)) 146 | break; 147 | // fallback generic object copy 148 | default: 149 | m.set(o, n =Object.assign(new (c)(), o)) 150 | // recursive copy for child objects 151 | for(c in n) if('object' ===typeof n[c]) n[c] =clone(n[c], m) 152 | } 153 | return n 154 | } 155 | 156 | function merge() { 157 | var obj = {}, 158 | i = 0, 159 | il = arguments.length, 160 | key; 161 | for (; i < il; i++) { 162 | for (key in arguments[i]) { 163 | if (arguments[i].hasOwnProperty(key)) { 164 | obj[key] = arguments[i][key]; 165 | } 166 | } 167 | } 168 | return obj; 169 | }; 170 | 171 | module.exports = { 172 | shouldMove, 173 | cacheDimen, 174 | getOS, 175 | merge, 176 | clearViewExternals, 177 | clone, 178 | } -------------------------------------------------------------------------------- /src/compute.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | /* 27 | ViewContext of the Parent.f 28 | Obj holds the available width and height of it's parent container 29 | and x & y offset for it's children 30 | */ 31 | function viewCtxObj(view) { 32 | let props = view.props; 33 | let obj = { 34 | w: props.w * 1, 35 | h: props.h * 1, 36 | width: props.w * 1, 37 | }; 38 | 39 | view.children.forEach(child => { 40 | child.props.w = child.props.width; 41 | child.props.h = child.props.height; 42 | }); 43 | 44 | return obj; 45 | } 46 | 47 | /* 48 | Returns true if container has a child with match_parent for a 49 | given dimension (width or height) 50 | */ 51 | function hasMatchParentChild(childs, dimen) { 52 | for (let i = 0; i < childs.length; i++) { 53 | let childProp = childs[i].props; 54 | 55 | if (childProp[dimen] && (childProp[dimen] == "match_parent")) { 56 | return true; 57 | } 58 | } 59 | return false; 60 | } 61 | 62 | /* 63 | Returns true if container has weighted child 64 | */ 65 | function hasWeightChild(type, childs) { 66 | if (type != "linearLayout") { 67 | return false; 68 | } 69 | 70 | for (let i = 0; i < childs.length; i++) { 71 | let child = childs[i].props; 72 | 73 | if (child.weight && parseFloat(child.weight) > 0) { 74 | return true; 75 | } 76 | } 77 | return false; 78 | } 79 | 80 | function computeRelativeLayout(view) { 81 | let viewCtx = viewCtxObj(view); 82 | let children = view.children; 83 | 84 | children.forEach(child2 => { 85 | let child = window.__VIEWS[child2.props.id] || child2; 86 | let props = child.props; 87 | var pl = 0, pr = 0, pt = 0, pb =0; 88 | if(view.props && view.props.hasOwnProperty('padding')) { 89 | var padding = view.props.padding.split(',') 90 | pl = padding[0] 91 | pt = padding[1] 92 | pr = padding[2] 93 | pb = padding[3] 94 | } 95 | props.absolute = true; 96 | props.fromTop = pt; 97 | props.fromBottom = 'auto'; 98 | props.fromLeft = pl; 99 | props.fromRight = 'auto'; 100 | 101 | if(props.hasOwnProperty('alignParentLeft') && props.alignParentLeft){ 102 | props.fromLeft = 0; 103 | props.fromRight = 'auto'; 104 | } 105 | if(props.hasOwnProperty('alignParentRight') && props.alignParentRight){ 106 | props.fromLeft = 'auto'; 107 | props.fromRight = pr; 108 | } 109 | 110 | if(props.hasOwnProperty('alignParentBottom') && props.alignParentBottom){ 111 | props.fromTop = 'auto'; 112 | props.fromBottom = pb; 113 | } 114 | }); 115 | } 116 | 117 | function computeLinearlayout(view) { 118 | let viewCtx = viewCtxObj(view); 119 | let parentProps = view.props; 120 | let children = view.children; 121 | let isHorizontal = (parentProps.orientation === "vertical") ? false : true; 122 | 123 | let activeDimen = (isHorizontal) ? "w" : "h"; 124 | let passiveDimen = (isHorizontal) ? "h" : "w"; 125 | 126 | let hasWeight = hasWeightChild(view.type, children); 127 | let hasMatchParent = hasMatchParentChild(children, activeDimen); 128 | 129 | if (hasWeight && hasMatchParent) { 130 | // We can't use both at the same time 131 | return; 132 | } 133 | 134 | /* Initialize */ 135 | children.forEach(child2 => { 136 | let child = window.__VIEWS[child2.props.id] || child2; 137 | let props = child.props; 138 | 139 | if (props.hasOwnProperty("activeDimen")) 140 | delete props["activeDimen"]; 141 | if (props.hasOwnProperty("activeWeight")) 142 | delete props["activeWeight"]; 143 | }); 144 | /* Initialize End */ 145 | 146 | if(hasMatchParent || hasWeight){ 147 | let first = true; 148 | /* Iterate Child */ 149 | children.forEach(child2 => { 150 | let child = window.__VIEWS[child2.props.id] || child2; 151 | let props = child.props; 152 | 153 | if(props.hasOwnProperty(activeDimen) && props[activeDimen] == 'match_parent'){ 154 | props['activeDimen'] = activeDimen; 155 | 156 | if(first){ 157 | props['activeWeight'] = 1; 158 | first = false; 159 | }else{ 160 | props['activeWeight'] = 0; 161 | } 162 | }else{ 163 | if(props.hasOwnProperty('weight') && !isNaN(props['weight'])){ 164 | let weight = parseFloat(props['weight']); 165 | 166 | if(weight > 0){ 167 | props['activeDimen'] = activeDimen; 168 | props['activeWeight'] = weight; 169 | } 170 | } 171 | } 172 | }); 173 | /* Iterate Child End */ 174 | } 175 | } // End Compute LinearLayout 176 | 177 | function computeChildDimens(view) { 178 | if (view.type == "linearLayout") { 179 | computeLinearlayout(view); 180 | } else if (view.type == "scrollView" || view.type == "listView") { 181 | view.props.orientation = "vertical"; 182 | computeLinearlayout(view); 183 | } else if (view.type == "horizontalScrollView") { 184 | view.props.orientation = "horizontal"; 185 | computeLinearlayout(view); 186 | } else if (view.type == "relativeLayout") { 187 | computeRelativeLayout(view); 188 | } else { 189 | // Do Nothing 190 | } 191 | 192 | return view; 193 | } 194 | 195 | module.exports = { 196 | computeChildDimens 197 | }; -------------------------------------------------------------------------------- /src/components/NavBar.js: -------------------------------------------------------------------------------- 1 | function NavBar() {} 2 | 3 | NavBar.prototype._getActiveRouteItem = function(guid) { 4 | let query = '.' + window.__COM_CLASS_GROUP.NAVBAR_ITEM_WITH_SUB + '.selected[guid="' + guid +'"]' 5 | return document.body.querySelector(query) 6 | } 7 | 8 | NavBar.prototype._getMainObject = function(guid) { 9 | let query = '.' + window.__COM_CLASS_GROUP.NAVBAR + '[guid="' + guid +'"]' 10 | return document.body.querySelector(query) 11 | } 12 | 13 | NavBar.prototype._openByGUID = function(guid, parentElem) { 14 | parentElem.classList.add('selected') 15 | window.__COM_ACTIVE.NAVBAR = guid 16 | } 17 | 18 | NavBar.prototype._closeByGUID = function(guid) { 19 | let object = this._getActiveRouteItem(guid) 20 | if(object) 21 | object.classList.remove('selected') 22 | 23 | window.__COM_ACTIVE.NAVBAR = '' 24 | } 25 | 26 | NavBar.prototype._selectRouteByGUID = function(guid, elem) { 27 | let key = elem.getAttribute('route-key') 28 | let text = elem.getAttribute('route-text') 29 | 30 | let object = this._getMainObject(guid) 31 | let objectID = object.id 32 | 33 | if(!key || !text || !objectID) 34 | return 35 | 36 | let view = window.__VIEWS[objectID] 37 | 38 | if(!view || !view.props) 39 | return 40 | 41 | /* Update UI */ 42 | if (window.__COM_RENDERED.NAVBAR[guid] && window.__COM_RENDERED.NAVBAR[guid].activeElem) 43 | window.__COM_RENDERED.NAVBAR[guid].activeElem.classList.remove('selected') 44 | 45 | elem.classList.add('selected') 46 | 47 | if (!window.__COM_RENDERED.NAVBAR[guid]) 48 | window.__COM_RENDERED.NAVBAR[guid] = {} 49 | 50 | window.__COM_RENDERED.NAVBAR[guid].activeElem = elem 51 | window.__COM_RENDERED.NAVBAR[guid].defaultValue = key 52 | 53 | /* Event Trigger */ 54 | if (view.props.onSelect && typeof view.props.onSelect == "function") { 55 | view.props.onSelect(key) 56 | } 57 | } 58 | 59 | NavBar.prototype._renderRoute = function(parentElem, props, guid, route, renderEvent) { 60 | if(!route.key || !route.text) 61 | return 62 | 63 | let elem = document.createElement('LI') 64 | elem.classList.add(window.__COM_CLASS_GROUP.NAVBAR_ITEM) 65 | 66 | elem.setAttribute('route-key', route.key) 67 | elem.setAttribute('route-text', route.text) 68 | elem.setAttribute('guid', guid) 69 | 70 | let defaultValue = null 71 | if (props.defaultValue) 72 | defaultValue = props.defaultValue 73 | if (window.__COM_RENDERED.NAVBAR[guid] && window.__COM_RENDERED.NAVBAR[guid].defaultValue) 74 | defaultValue = window.__COM_RENDERED.NAVBAR[guid].defaultValue + "" 75 | 76 | if(defaultValue && defaultValue == route.key) { 77 | elem.classList.add('selected') 78 | 79 | // Save 80 | if (!window.__COM_RENDERED.NAVBAR[guid]) 81 | window.__COM_RENDERED.NAVBAR[guid] = {} 82 | 83 | window.__COM_RENDERED.NAVBAR[guid].activeElem = elem 84 | window.__COM_RENDERED.NAVBAR[guid].defaultValue = route.key 85 | } else 86 | elem.classList.remove('selected') 87 | 88 | let article = document.createElement('article') 89 | if(props.height && !isNaN(props.height)) 90 | article.style.lineHeight = props.height + 'px' 91 | 92 | elem.appendChild(article) 93 | 94 | if(route.subroutes && route.subroutes.length > 0) { 95 | article.innerHTML = route.text + ' ▾' 96 | elem.classList.add(window.__COM_CLASS_GROUP.NAVBAR_ITEM_WITH_SUB) 97 | 98 | let virtualElem = document.createElement('UL') 99 | virtualElem.classList.add(window.__COM_CLASS_GROUP.NAVBAR_SUB) 100 | 101 | if(route.navDirection && route.navDirection == 'from_right') 102 | virtualElem.classList.add(window.__COM_CLASS_GROUP.NAVBAR_SUB_RIGHT) 103 | 104 | virtualElem.setAttribute('guid', guid) 105 | 106 | if(props.translationZ_nav) 107 | virtualElem.style.zIndex = props.translationZ_nav 108 | 109 | elem.appendChild(virtualElem) 110 | 111 | for(let i = 0; i < route.subroutes.length; i++) { 112 | let subroute = route.subroutes[i] 113 | 114 | this._renderSubRoute(virtualElem, props, guid, subroute, renderEvent) 115 | } 116 | } else { 117 | article.innerHTML = route.text 118 | } 119 | 120 | parentElem.appendChild(elem) 121 | } 122 | 123 | NavBar.prototype._renderSubRoute = function(parentElem, props, guid, route, renderEvent) { 124 | if(!route.key || !route.text) 125 | return 126 | 127 | let elem = document.createElement('LI') 128 | elem.classList.add(window.__COM_CLASS_GROUP.NAVBAR_SUB_ITEM) 129 | 130 | elem.setAttribute('route-key', route.key) 131 | elem.setAttribute('route-text', route.text) 132 | elem.setAttribute('guid', guid) 133 | 134 | let defaultValue = null 135 | if (props.defaultValue) 136 | defaultValue = props.defaultValue 137 | if (window.__COM_RENDERED.NAVBAR[guid] && window.__COM_RENDERED.NAVBAR[guid].defaultValue) 138 | defaultValue = window.__COM_RENDERED.NAVBAR[guid].defaultValue + "" 139 | 140 | if(defaultValue && defaultValue == route.key) { 141 | elem.classList.add('selected') 142 | 143 | // Save 144 | if (!window.__COM_RENDERED.NAVBAR[guid]) 145 | window.__COM_RENDERED.NAVBAR[guid] = {} 146 | 147 | window.__COM_RENDERED.NAVBAR[guid].activeElem = elem 148 | window.__COM_RENDERED.NAVBAR[guid].defaultValue = route.key 149 | } else { 150 | elem.classList.remove('selected') 151 | } 152 | 153 | let article = document.createElement('article') 154 | article.innerHTML = route.text 155 | 156 | elem.appendChild(article) 157 | parentElem.appendChild(elem) 158 | } 159 | 160 | NavBar.prototype._renderMain = function(elem, props, renderEvent) { 161 | if (!renderEvent) 162 | return 163 | 164 | // GUID 165 | let guid = props.guid 166 | if(elem.getAttribute('guid')) 167 | guid = elem.getAttribute('guid') 168 | 169 | // ROUTES 170 | let routes = [] 171 | if(props && props.routes) 172 | routes = JSON.parse(props.routes) 173 | 174 | elem.innerHTML = '' 175 | let virtualElem = document.createElement('UL') 176 | elem.appendChild(virtualElem) 177 | 178 | if(routes.length > 0) { 179 | for(let i = 0; i < routes.length; i++) { 180 | let route = routes[i] 181 | 182 | this._renderRoute(virtualElem, props, guid, route, renderEvent) 183 | } 184 | } 185 | } 186 | 187 | module.exports = new NavBar() -------------------------------------------------------------------------------- /src/component.js: -------------------------------------------------------------------------------- 1 | const DropdownBox = require('./components/DropdownBox') 2 | const DropdownSearchBox = require('./components/DropdownSearchBox') 3 | const NavBar = require('./components/NavBar') 4 | // const DateRangePicker = require('./components/DateRangePicker') 5 | const SwitchButton = require('./components/SwitchButton') 6 | const Button = require('./components/Button') 7 | 8 | 9 | 10 | /* Components */ 11 | function closeAllActiveComponents() { 12 | if(window.__COM_ACTIVE.NAVBAR != '') 13 | NavBar._closeByGUID(window.__COM_ACTIVE.NAVBAR) 14 | if(window.__COM_ACTIVE.DSB != '') 15 | DropdownSearchBox._closeByGUID(window.__COM_ACTIVE.DSB) 16 | if(window.__COM_ACTIVE.DB != '') 17 | DropdownBox._closeByGUID(window.__COM_ACTIVE.DB) 18 | } 19 | 20 | function renderComponent(elem, props, firstRender) { 21 | if(!props.componentType) 22 | return 23 | 24 | let componentType = props.componentType 25 | 26 | /* Attaching GUID only when firstRender */ 27 | if(firstRender && props.guid) 28 | elem.setAttribute('guid', props.guid) 29 | 30 | switch(componentType) { 31 | // Button 32 | case 'BT': 33 | elem.classList.add(window.__COM_CLASS_GROUP.BT) 34 | 35 | Button._renderMain(elem, props, firstRender) 36 | break 37 | // Switch Button 38 | case 'SWITCH': 39 | elem.classList.add(window.__COM_CLASS_GROUP.SWITCH) 40 | 41 | SwitchButton._renderMain(elem, props, firstRender) 42 | break 43 | // Date Range Picker 44 | case 'DRP': 45 | elem.classList.add(window.__COM_CLASS_GROUP.DRP) 46 | 47 | // DateRangePicker._renderMain(elem, props, firstRender) 48 | break 49 | case 'DRP_BODY': 50 | elem.classList.add(window.__COM_CLASS_GROUP.DRP_BODY) 51 | 52 | // DateRangePicker._renderBody(elem, props, firstRender) 53 | break 54 | // Nav Bar 55 | case 'NAVBAR': 56 | elem.classList.add(window.__COM_CLASS_GROUP.NAVBAR) 57 | 58 | NavBar._renderMain(elem, props, firstRender) 59 | break 60 | // Dropdown Box 61 | case 'DB': 62 | elem.classList.add(window.__COM_CLASS_GROUP.DB) 63 | 64 | DropdownBox._renderMain(elem, props, firstRender) 65 | break 66 | case 'DB_FULL_BODY': 67 | elem.classList.add(window.__COM_CLASS_GROUP.DB_FULL_BODY) 68 | 69 | DropdownBox._renderFullBody(elem, props, firstRender) 70 | break 71 | // Dropdown Search Box 72 | case 'DSB': 73 | elem.classList.add(window.__COM_CLASS_GROUP.DSB) 74 | 75 | DropdownSearchBox._renderMain(elem, props, firstRender) 76 | break 77 | case 'DSB_FULL_BODY': 78 | elem.classList.add(window.__COM_CLASS_GROUP.DSB_FULL_BODY) 79 | 80 | DropdownSearchBox._renderFullBody(elem, props, firstRender) 81 | break 82 | } 83 | 84 | if(!window.__COM_EVENT) { 85 | window.__COM_EVENT = true 86 | 87 | // Body click event 88 | document.body.addEventListener('click', function(e) { 89 | let target = e.target 90 | let guid = target.getAttribute('guid') 91 | 92 | if(!guid) { // We need to close any active components 93 | closeAllActiveComponents() 94 | } else { 95 | let classList = target.classList 96 | 97 | if(classList.contains(window.__COM_CLASS_GROUP.NAVBAR_ITEM_WITH_SUB)) { // NAVBAR Menu Item with SubRoute 98 | if(classList.contains('selected')) { 99 | if(!window.__COM_ACTIVE.NAVBAR) 100 | return 101 | 102 | NavBar._closeByGUID(window.__COM_ACTIVE.NAVBAR) 103 | } else { 104 | closeAllActiveComponents() 105 | NavBar._openByGUID(guid, target) 106 | } 107 | } else if(classList.contains(window.__COM_CLASS_GROUP.NAVBAR_ITEM) || classList.contains(window.__COM_CLASS_GROUP.NAVBAR_SUB_ITEM)) { // NAVBAR Menu Item (Sub Item as well) 108 | if(window.__COM_ACTIVE.NAVBAR) 109 | NavBar._closeByGUID(window.__COM_ACTIVE.NAVBAR) 110 | 111 | if(classList.contains('selected')) 112 | return 113 | 114 | NavBar._selectRouteByGUID(guid, target) 115 | } else if(classList.contains(window.__COM_CLASS_GROUP.DB)) { // DB Main Click 116 | if(guid == window.__COM_ACTIVE.DB) { 117 | DropdownBox._closeByGUID(guid) 118 | } else { 119 | closeAllActiveComponents() 120 | DropdownBox._openByGUID(guid) 121 | } 122 | } else if(classList.contains(window.__COM_CLASS_GROUP.DB_OPTION)) { // DB Option Select 123 | DropdownBox._closeByGUID(guid) 124 | DropdownBox._selectOptionByGUID(guid, target) 125 | } else if(classList.contains(window.__COM_CLASS_GROUP.DSB)) { // DSB Main Click 126 | if(guid == window.__COM_ACTIVE.DSB) { 127 | DropdownSearchBox._closeByGUID(guid) 128 | } else { 129 | closeAllActiveComponents() 130 | DropdownSearchBox._openByGUID(guid) 131 | } 132 | } else if(classList.contains(window.__COM_CLASS_GROUP.DSB_OPTION)) { // DSB Option Select 133 | DropdownSearchBox._closeByGUID(guid) 134 | DropdownSearchBox._selectOptionByGUID(guid, target) 135 | } 136 | } 137 | }) 138 | 139 | // Keydown event 140 | document.body.addEventListener('keyup', function(e) { 141 | let target = e.target 142 | let guid = target.getAttribute('guid') 143 | 144 | if(!guid) 145 | return 146 | 147 | let classList = target.classList 148 | 149 | if(classList.contains(window.__COM_CLASS_GROUP.DSB_SEARCH)) { 150 | if(guid == window.__COM_ACTIVE.DSB) { 151 | let query = target.value.trim() 152 | DropdownSearchBox._filterOptions(guid, query) 153 | } 154 | } 155 | }) 156 | } 157 | } 158 | 159 | module.exports = { 160 | renderComponent 161 | } -------------------------------------------------------------------------------- /src/doms/android.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | var excluded = { 27 | "CoordinatorLayout": "androidx.coordinatorlayout.widget.", 28 | "SwipeRefreshLayout": "androidx.swiperefreshlayout.widget.", 29 | "FloatingActionButton": "android.support.design.widget.", 30 | "Toolbar": "android.support.v7.widget.", 31 | "AppBarLayout": "android.support.design.widget.", 32 | "CollapsingToolbarLayout": "android.support.design.widget.", 33 | "View": "android.view.", 34 | "WebView": "android.webkit.", 35 | "ViewPager": "android.support.v4.view.", 36 | "RotateAnimation": "android.view.animation.", 37 | "LinearInterpolator": "android.view.animation.", 38 | "Animation": "android.view.animation.", 39 | "RecyclerView": "androidx.recyclerview.widget.", 40 | "TextureView": "android.view.", 41 | "PagerTabStrip": "android.support.v4.view.", 42 | "PagerTitleStrip": "android.support.v4.view.", 43 | "TabLayout": "in.org.npci.upiapp.", 44 | "ShimmerFrameLayout": "com.facebook.shimmer.", 45 | "SwypeLayout": "in.juspay.mystique.", 46 | "SwypeScroll": "in.juspay.mystique.", 47 | "AccordionLayout": "in.juspay.mystique.", 48 | "Shape": "in.juspay.mystique.", 49 | "BottomSheetLayout": "in.juspay.mystique.", 50 | "LottieAnimationView": "com.airbnb.lottie.", 51 | "NestedScrollView": "androidx.core.widget." 52 | } 53 | 54 | function getCtr(viewGroup) { 55 | var viewGroupMap = { 56 | 'linearLayout': 'android.widget.LinearLayout$LayoutParams->new', 57 | 'coordinatorLayout': 'androidx.coordinatorlayout.widget.CoordinatorLayout$LayoutParams->new', 58 | 'swipeRefreshLayout': 'androidx.swiperefreshlayout.widget.SwipeRefreshLayout$LayoutParams->new', 59 | 'scrollView': 'android.widget.LinearLayout$LayoutParams->new', 60 | 'nestedScrollView': 'android.widget.LinearLayout$LayoutParams->new', 61 | 'horizontalScrollView': 'android.widget.LinearLayout$LayoutParams->new', 62 | 'relativeLayout': 'android.widget.RelativeLayout$LayoutParams->new', 63 | 'frameLayout': 'android.widget.FrameLayout$LayoutParams->new', 64 | 'toolbar': 'android.support.v7.widget.Toolbar$LayoutParams->new', 65 | 'collapsingToolbarLayout': 'androidx.coordinatorlayout.widget.CoordinatorLayout$LayoutParams->new', 66 | 'appBarLayout': 'android.support.design.widget.AppBarLayout$LayoutParams->new', 67 | 'view': 'android.widget.LinearLayout$LayoutParams->new', 68 | 'tabLayout': 'android.widget.LinearLayout$LayoutParams->new', 69 | 'viewPager': 'android.support.v4.view.ViewPager$LayoutParams->new', 70 | 'listView': 'android.widget.LinearLayout$LayoutParams->new', 71 | 'expandableListView': 'android.widget.LinearLayout$LayoutParams->new', 72 | 'recyclerView': 'android.support.v7.widget.RecyclerView$LayoutParams->new', 73 | 'ratingBar': 'android.widget.LinearLayout$LayoutParams->new', 74 | 'accordionLayout': 'android.widget.FrameLayout$LayoutParams->new', 75 | 'swypeLayout': 'android.widget.FrameLayout$LayoutParams->new', 76 | 'swypeScroll': 'android.widget.LinearLayout$LayoutParams->new', 77 | 'shimmerFrameLayout': 'android.widget.FrameLayout$LayoutParams->new', 78 | "bottomSheetLayout": 'android.widget.FrameLayout$LayoutParams->new' 79 | } 80 | 81 | return viewGroupMap[viewGroup]; 82 | } 83 | 84 | Array.prototype.flatten = function() { 85 | return this.reduce(function(prev, cur) { 86 | var more = [].concat(cur).some(Array.isArray); 87 | return prev.concat(more ? cur.flatten() : cur); 88 | }, []); 89 | }; 90 | 91 | var parseParams = require('../helpers/android').parseParams; 92 | 93 | function setAutogenId(props) { 94 | props.node_id = window.__NODE_ID + ''; 95 | window.__NODE_ID++; 96 | 97 | if (!props.__filename) 98 | props.__filename = "filename not added"; 99 | return props; 100 | } 101 | 102 | module.exports = function(type, props, ...children) { 103 | var paramType; 104 | 105 | children = children.flatten(); 106 | 107 | if (!props) 108 | props = {}; 109 | 110 | if(typeof type === "object") { 111 | paramType = getCtr(type.parentType); 112 | props = parseParams(type.elemType, props, "set"); 113 | props = setAutogenId(props); 114 | props.runInUI = props.runInUI.replace('PARAM_CTR_HOLDER', paramType); 115 | if(type.elemType == "webView") { 116 | props.runInUI = "set_xyz=android.webkit.WebViewClient->new;this->setWebViewClient:get_xyz;" + props.runInUI; 117 | } 118 | var finalType = type.elemType; 119 | finalType = finalType[0].toUpperCase() + finalType.substr(1, finalType.length); 120 | for (var excludedType in excluded) { 121 | if (excludedType === finalType) { 122 | return { type: excluded[excludedType] + finalType, props: props, children: children } 123 | } 124 | } 125 | return {type: "android.widget." + finalType, props: props, children: children} 126 | } else if (typeof type === "string") { 127 | paramType = getCtr(type); 128 | props = parseParams(type, props, "set"); 129 | 130 | props = setAutogenId(props); 131 | 132 | for (let j = 0; j < children.length; j++) { 133 | if (children[j].props.runInUI) { 134 | children[j].props.runInUI = children[j].props.runInUI.replace('PARAM_CTR_HOLDER', paramType); 135 | } 136 | } 137 | 138 | type = type[0].toUpperCase() + type.substr(1, type.length) 139 | 140 | for (var excludedType in excluded) { 141 | if (excludedType === type) { 142 | return { type: excluded[excludedType] + type, props: props, children: children } 143 | } 144 | } 145 | 146 | return { type: "android.widget." + type, props: props, children: children } 147 | 148 | } else { 149 | return new type(props, children); 150 | } 151 | } -------------------------------------------------------------------------------- /src/baseView.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | const merge = require("./helper").merge; 27 | 28 | String.prototype.addCmd = function(data) { 29 | return this.concat(data); 30 | }; 31 | 32 | Array.prototype.addCmd = function(data) { 33 | this.push(data); 34 | return this; 35 | }; 36 | 37 | let parseParams; 38 | if (window.__OS == "ANDROID") 39 | parseParams = require('./helpers/android/parseParams'); 40 | else if (window.__OS == "WEB") 41 | parseParams = require('./helpers/web/parseParams'); 42 | else 43 | parseParams = require('./helpers/ios/parseParams'); 44 | 45 | class BaseView { 46 | constructor(props, children) { 47 | this.props = props || {}; 48 | this.children = children || []; 49 | 50 | window.__SETFN = function (config) { 51 | Android.runInUI( 52 | this.set(config), 53 | null 54 | ) 55 | }.bind(this) 56 | 57 | this.idSet = {}; 58 | 59 | if (this.props.id) { 60 | this.idSet["id"] = this.props.id; 61 | } else { 62 | this.props.id = this.id("id"); 63 | } 64 | 65 | if (this.props._ref) { 66 | this.props._ref(this); 67 | } 68 | } 69 | 70 | resolveChildren() { 71 | return this.children.map(function(child) { 72 | return child.render(); 73 | }); 74 | } 75 | 76 | findRecurse(obj, selector) { 77 | var children = obj.children; 78 | 79 | for (var i = 0; i < children.length; i++) { 80 | if (children[i].displayName && children[i].displayName == selector) { 81 | this.foundChildren.push(children[i]); 82 | } 83 | 84 | if (children[i].children && children[i].children.length) 85 | this.findRecurse(children[i], selector); 86 | } 87 | 88 | return; 89 | } 90 | 91 | find(selector, obj) { 92 | this.foundChildren = []; 93 | 94 | if (!obj) 95 | this.findRecurse(this.layout, selector); 96 | else 97 | this.findRecurse(obj, selector); 98 | 99 | return this.foundChildren; 100 | } 101 | 102 | id(name) { 103 | if (!this.idSet[name]) { 104 | window.__ID++; 105 | this.idSet[name] = window.__ID; 106 | } 107 | 108 | return this.idSet[name]; 109 | } 110 | 111 | setIds(arr) { 112 | if (!this.idSet) 113 | this.idSet = {}; 114 | 115 | for (var i = 0; i < arr.length; i++) { 116 | if (!this.idSet[arr[i]]) { 117 | window.__ID++; 118 | this.idSet[arr[i]] = window.__ID + ''; 119 | } 120 | } 121 | } 122 | 123 | handleSpecialChars(value) { 124 | value = value.indexOf(',') > -1 ? value.replace(/\,/g, '\\\\,') : value; 125 | value = value.indexOf(':') > -1 ? value.replace(/\:/g, '\\\\:') : value; 126 | value = value.indexOf('=') > -1 ? value.replace(/\=/g, '\\\\=') : value; 127 | value = value.indexOf(';') > -1 ? value.replace(/\;/g, '\\\\;') : value; 128 | 129 | return value; 130 | } 131 | 132 | cmdForAndroid(config) { 133 | var cmd = "set_view=ctx->findViewById:i_" + config.id + ";"; 134 | var runInUI; 135 | 136 | if (config.toast) { 137 | cmd = "set_TOAST=android.widget.Toast->makeText:ctx_ctx,cs_" + this.handleSpecialChars( 138 | config.text) + ",i_" + (config.duration == "short" ? 0 : 1) + 139 | ";get_TOAST->show"; 140 | } else if (config && Object.keys(config).length) { 141 | delete config.id; 142 | 143 | config.root = "true"; 144 | runInUI = parseParams("linearLayout", config, "get").runInUI; 145 | 146 | cmd += runInUI + ';'; 147 | } 148 | 149 | return cmd; 150 | } 151 | 152 | cmdContainer() { 153 | if (window.__OS == "ANDROID") 154 | return ""; 155 | return []; 156 | } 157 | 158 | cmdForWeb(config) { 159 | if (Object.keys(config).length) 160 | return parseParams("linearLayout", config, "set"); 161 | } 162 | 163 | cmd(config) { 164 | if (window.__OS == "ANDROID") 165 | return this.cmdForAndroid(config); 166 | else if (window.__OS == "WEB") 167 | return this.cmdForWeb(config); 168 | return config; 169 | } 170 | 171 | appendChild(id, jsx, index, fn, replaceChild) { 172 | var proxyFnName; 173 | if (!replaceChild) { 174 | replaceChild = false; 175 | } 176 | 177 | if (fn) { 178 | proxyFnName = 'F' + window.__FN_INDEX; 179 | window.__PROXY_FN[proxyFnName] = fn; 180 | window.__FN_INDEX++; 181 | } 182 | 183 | jsx = (window.__OS != "ANDROID") ? jsx : JSON.stringify(jsx); 184 | 185 | if (proxyFnName) 186 | Android.addViewToParent(id, jsx, index, proxyFnName, replaceChild); 187 | else 188 | Android.addViewToParent(id, jsx, index, null, replaceChild); 189 | } 190 | 191 | getView(jsx) { 192 | if (window.__OS != "ANDROID") 193 | return jsx; 194 | 195 | return JSON.stringify(jsx); 196 | } 197 | 198 | updateProps(props) { 199 | this.props = merge(this.props, props); 200 | const oldContainerId = this.layout.idSet.id; 201 | const layout = this.render(); 202 | for (let i=0; ifindViewById:i_" + id + 213 | ";set_PARENT=get_VIEW->getParent;get_PARENT->removeView:get_VIEW;" 214 | } 215 | 216 | removeChildren(id) { 217 | if (__OS == "WEB") 218 | return Android.removeChildren(id); 219 | return "set_VIEW=ctx->findViewById:i_" + id + 220 | ";get_VIEW->removeAllViews;" 221 | } 222 | 223 | replaceChild(id, jsx, index, fn) { 224 | this.appendChild(id, jsx, index, fn, true); 225 | } 226 | } 227 | 228 | module.exports = BaseView; -------------------------------------------------------------------------------- /src/IOS/AndroidInterface.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | const render = require('./Render'); 27 | const helper = require('../helper'); 28 | const parseParams = require('../helpers').ios.parseParams; 29 | 30 | let rootid; 31 | 32 | function clearViewExternals(view) { 33 | delete window.__VIEWS[view.props.id]; 34 | delete window.__VIEW_DIMENSIONS[view.props.id]; 35 | view.children.forEach(clearViewExternals); 36 | } 37 | 38 | function getSerializeableView(view, recurse) { 39 | var obj = parseParams(view.type, helper.clone(view.props,null), "set"); 40 | var newView = {}; 41 | newView.props = obj.config; 42 | newView.type = obj.type[0].toUpperCase() + obj.type.substr(1, obj.type.length); 43 | if (recurse) 44 | newView.children = view.children.map(getSerializeableView); 45 | else 46 | newView.children = []; 47 | return newView; 48 | } 49 | 50 | var renderWrapper = function(view, cb, namespace){ 51 | // Query JOS if ns is available. 52 | // if null call render 53 | // if not null find namespace and call AddViewToParent 54 | top.fragments = top.fragments || {} 55 | top.fragmentObjects = top.fragmentObjects || {}; 56 | if(namespace == null || namespace == undefined || typeof top.fragments[namespace] != "number") { 57 | return renderFunction(view, cb) 58 | } 59 | var rootId = top.fragments[namespace]; 60 | window.__VIEWS[rootId] = top.fragmentObjects[namespace] 61 | return addViewToParentImpl(rootId, view, 0, null, null); 62 | } 63 | 64 | var renderFunction = function (view, cb, namespace) { 65 | let obj = { 66 | type: "linearLayout", 67 | props: { 68 | h: window.__HEIGHT, 69 | w: window.__WIDTH 70 | }, 71 | children: [view] 72 | }; 73 | rootid = view.props.id; 74 | render.computeChildDimens(obj); 75 | view = render.inflate(view); 76 | if (view) { 77 | window.webkit.messageHandlers.IOS.postMessage( 78 | JSON.stringify({ 79 | methodName: "render", 80 | parameters: { 81 | view: view, 82 | namespace : namespace 83 | } 84 | })); 85 | } 86 | if (cb) 87 | window.callUICallback(cb); 88 | } 89 | 90 | var addViewToParentImpl = function (id, view, index, cb, x, namespace) { 91 | if (!window.__VIEWS[id]) { 92 | return console.error(new Error("AddViewToParent: Invalid parent ID: " + 93 | id)); 94 | } 95 | const parent = window.__VIEWS[id]; 96 | parent.children.splice(index, 0, view); 97 | view.props.parentId = id; 98 | render.computeChildDimens(parent); 99 | const renderedView = render.inflate(view); 100 | if (renderedView) { 101 | window.webkit.messageHandlers.IOS.postMessage(JSON.stringify({ 102 | methodName: "addViewToParent", 103 | parameters: { 104 | index: index, 105 | parentId: id, 106 | view: renderedView, 107 | afterRender : cb+"", 108 | namespace : namespace 109 | } 110 | })); 111 | } 112 | recomputeView(parent); 113 | } 114 | 115 | var recomputeView = function (view) { 116 | if (view.type.indexOf("scroll") != -1) { 117 | render.inflate(view); 118 | } 119 | render.computeChildDimens(view); 120 | const children = view.children; 121 | for (let i = 0, len = children.length; i < len; i++) { 122 | render.inflate(children[i]); 123 | } 124 | } 125 | 126 | module.exports = { 127 | 128 | addToContainerList : function(id, namespace) { 129 | // Check if JOS has an id store from another m-app 130 | // Add id, and get a return string identifier 131 | // Use the same to decide between render and and addview to parent 132 | if(typeof top.addToContianerList != "function" ){ 133 | top.fragments = top.fragments || {}; 134 | top.fragmentObjects = top.fragmentObjects || {}; 135 | var generateUUID = function() { 136 | function s4() { 137 | return Math.floor((1 + Math.random()) * 0x10000) 138 | .toString(16) 139 | .substring(1); 140 | } 141 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 142 | s4() + '-' + s4() + s4() + s4(); 143 | } 144 | top.addToContainerList = function(id, namespace) { 145 | // Namespace not needed, for cases where we do not have merchant fragment 146 | var uuid = generateUUID() 147 | top.fragments[uuid] = id; 148 | top.fragmentObjects[uuid] = window.__VIEWS[id]; 149 | return uuid; 150 | } 151 | } 152 | return top.addToContainerList(id, namespace); 153 | }, 154 | 155 | getScreenDimensions: function () { 156 | return JSON.stringify({ 157 | width: window.__DEVICE_DETAILS.screen_width, 158 | height: window.__DEVICE_DETAILS.screen_height 159 | }); 160 | }, 161 | 162 | recomputeView: recomputeView, 163 | 164 | recompute: function () { 165 | const rootview = window.__VIEWS[rootid]; 166 | let obj = { 167 | type: "linearLayout", 168 | props: { 169 | h: window.__HEIGHT, 170 | w: window.__WIDTH 171 | }, 172 | children: [rootview] 173 | }; 174 | render.computeChildDimens(obj); 175 | render.inflate(rootview); 176 | }, 177 | 178 | runInUI: function (cmd, namespace) { 179 | render.runInUI(cmd, undefined, namespace); 180 | }, 181 | 182 | render: renderWrapper, 183 | Render: renderWrapper, 184 | 185 | moveView: function moveView(id, index) { 186 | if (!window.__VIEWS[id]) { 187 | return console.error(new Error("MoveView: Invalid view ID: " + id)); 188 | } 189 | const view = window.__VIEWS[id]; 190 | const parent = window.__VIEWS[view.props.parentId]; 191 | const children = parent.children; 192 | children.splice(children.indexOf(view), 1); 193 | children.splice(index, 0, view); 194 | this.recomputeView(parent); 195 | }, 196 | 197 | addViewToParent: addViewToParentImpl, 198 | 199 | createListData: function (id, view) { 200 | const parent = window.__VIEWS[id]; 201 | if (!parent) { 202 | return "{}"; 203 | } 204 | const views = window.__VIEWS; 205 | window.__VIEWS = {}; 206 | parent.children = [view]; 207 | view.props.parentId = id; 208 | render.computeChildDimens(parent); 209 | const inflatedView = render.inflate(view); 210 | window.__VIEWS = views; 211 | return JSON.stringify(inflatedView); 212 | }, 213 | 214 | replaceView: function (view, id, namespace) { 215 | if (!window.__VIEWS[id]) { 216 | return console.error(new Error("AddViewToParent: Invalid parent ID: " + id)); 217 | } 218 | var oldview = window.__VIEWS[id]; 219 | var parentid = oldview.props.parentId; 220 | oldview.props = helper.clone(view.props,null); 221 | oldview.props.parentId = parentid; 222 | var parent = window.__VIEWS[parentid]; 223 | var index = parent.children.indexOf(oldview); 224 | this.recomputeView(parent); 225 | var newView = getSerializeableView(oldview); 226 | window.webkit.messageHandlers.IOS.postMessage(JSON.stringify({ 227 | methodName: "replaceView", 228 | parameters: { 229 | id: id, 230 | view: newView, 231 | parentId: parentid, 232 | index: index 233 | } 234 | })); 235 | }, 236 | 237 | removeView: function (id, namespace) { 238 | const view = window.__VIEWS[id]; 239 | const parent = window.__VIEWS[view.props.parentId]; 240 | const index = parent ? parent.children.indexOf(view) : 0; 241 | if(parent){ 242 | parent.children.splice(index, 1); 243 | } 244 | clearViewExternals(view); 245 | window.webkit.messageHandlers.IOS.postMessage(JSON.stringify({ 246 | methodName: "removeView", 247 | parameters: { 248 | id: id, 249 | index, 250 | namespace : namespace 251 | } 252 | })); 253 | if(parent){ 254 | this.recomputeView(parent); 255 | } 256 | }, 257 | 258 | startAnim: function(id, cbName) { 259 | // TODO implement native side with callback 260 | // Thus, currently not providing on complete hook 261 | // onEnd property can still be used for chaining animations 262 | window.webkit.messageHandlers.IOS.postMessage(JSON.stringify({ 263 | methodName: "startAnimation", 264 | parameters: { 265 | animId: id 266 | } 267 | })); 268 | }, 269 | 270 | cancelAnim: function() { 271 | // TODO implement native side 272 | }, 273 | 274 | updateAnim: function() { 275 | // TODO implement native side 276 | } 277 | }; 278 | -------------------------------------------------------------------------------- /src/components/DropdownBox.js: -------------------------------------------------------------------------------- 1 | function DropdownBox() {} 2 | 3 | DropdownBox.prototype._equalOptions = function(a, b, j) { 4 | let stringA = a.join(j).trim() 5 | let stringB = b.join(j).trim() 6 | 7 | if (stringA == stringB) 8 | return true 9 | return false 10 | } 11 | 12 | DropdownBox.prototype._selectOptionByGUID = function(guid, optionElem) { 13 | let value = optionElem.getAttribute('option-value') 14 | let text = optionElem.getAttribute('option-text') 15 | 16 | let mainObject = this._getMainObject(guid) 17 | let mainObjectID = mainObject.id 18 | 19 | if(!value || !text || !mainObjectID) 20 | return 21 | 22 | let view = window.__VIEWS[mainObjectID] 23 | 24 | if(!view || !view.props) 25 | return 26 | 27 | // Save Option Value 28 | if (!window.__COM_RENDERED.DB[guid]) 29 | window.__COM_RENDERED.DB[guid] = {} 30 | window.__COM_RENDERED.DB[guid].optionValue = value 31 | 32 | // Update UI 33 | let optionsElem = this._getOptionsObject(guid) 34 | if (optionsElem && optionsElem.childNodes) { 35 | for (let i = 0; i < optionsElem.childNodes.length; i++) { 36 | if (optionsElem.childNodes[i].getAttribute('option-value') == value && optionsElem.childNodes[i].getAttribute('option-text') == text) { 37 | optionsElem.childNodes[i].classList.add('selected') 38 | } else { 39 | optionsElem.childNodes[i].classList.remove('selected') 40 | } 41 | } 42 | } 43 | 44 | let children = mainObject.childNodes 45 | let article = null 46 | 47 | if(children.length){ 48 | for(let i = 0; i < children.length; i++){ 49 | if(children[i].nodeName.toLowerCase() == 'article'){ 50 | article = children[i] 51 | break 52 | } 53 | } 54 | } 55 | 56 | mainObject.style.color = window.__COM_COLOR_GROUP.ACTIVE_COLOR 57 | if (article) 58 | article.innerText = text 59 | 60 | // Event Trigger 61 | if (view.props.onSelect && typeof view.props.onSelect == "function") { 62 | view.props.onSelect(value) 63 | } 64 | } 65 | 66 | DropdownBox.prototype._isValidOV = function(optionValue, options) { 67 | let text = "" 68 | 69 | if(options && options.length > 0) { 70 | for(let i in options) { 71 | if(options[i].value == optionValue) { 72 | text = options[i].text 73 | break 74 | } 75 | } 76 | } 77 | 78 | return text 79 | } 80 | 81 | DropdownBox.prototype._getMainObject = function(guid) { 82 | let query = '.' + window.__COM_CLASS_GROUP.DB + '[guid="' + guid +'"]' 83 | return document.body.querySelector(query) 84 | } 85 | 86 | DropdownBox.prototype._getFullBodyObject = function(guid) { 87 | let query = '.' + window.__COM_CLASS_GROUP.DB_FULL_BODY + '[guid="' + guid +'"]' 88 | return document.body.querySelector(query) 89 | } 90 | 91 | DropdownBox.prototype._getBodyObject = function(guid) { 92 | let query = '.' + window.__COM_CLASS_GROUP.DB_BODY + '[guid="' + guid +'"]' 93 | return document.body.querySelector(query) 94 | } 95 | 96 | DropdownBox.prototype._getOptionsObject = function(guid) { 97 | let query = '.' + window.__COM_CLASS_GROUP.DB_OPTIONS + '[guid="' + guid +'"]' 98 | return document.body.querySelector(query) 99 | } 100 | 101 | DropdownBox.prototype._renderOption = function(parentElem, props, guid, option, optionValue, renderEvent) { 102 | let elem = document.createElement('div') 103 | 104 | elem.className = window.__COM_CLASS_GROUP.DB_OPTION 105 | elem.setAttribute('option-value', option.value) 106 | elem.setAttribute('option-text', option.text) 107 | 108 | let height = 50 109 | if(props.optionHeight && !isNaN(props.optionHeight)) { 110 | height = parseFloat(props.optionHeight) 111 | } 112 | 113 | elem.style.height = height + 'px' 114 | 115 | if(props.fontSize) 116 | elem.style.fontSize = props.fontSize + 'px' 117 | if(props.fontFamily) 118 | elem.style.fontFamily = props.fontFamily 119 | if(props.optionPadding) { 120 | let padding = props.optionPadding.split(',').map(a => a * 1); 121 | 122 | elem.style.padding = padding[1] + 'px ' + padding[2] + 'px ' + padding[3] + 'px ' + padding[0] + 'px' 123 | } 124 | 125 | elem.setAttribute('guid', guid) 126 | 127 | if(optionValue && optionValue == option.value) { 128 | elem.classList.add('selected') 129 | } else { 130 | elem.classList.remove('selected') 131 | } 132 | 133 | let article = document.createElement('ARTICLE') 134 | article.innerText = option.text 135 | 136 | elem.appendChild(article) 137 | parentElem.appendChild(elem) 138 | } 139 | 140 | DropdownBox.prototype._openByGUID = function(guid) { 141 | let object = this._getMainObject(guid) 142 | let bodyElem = this._getBodyObject(guid) 143 | 144 | if(!object || !bodyElem) 145 | return 146 | 147 | object.classList.add('selected') 148 | bodyElem.style.display = 'block' 149 | window.__COM_ACTIVE.DB = guid 150 | } 151 | 152 | DropdownBox.prototype._closeByGUID = function(guid) { 153 | let object = this._getMainObject(guid) 154 | let bodyElem = this._getBodyObject(guid) 155 | 156 | if(!object || !bodyElem) 157 | return 158 | 159 | object.classList.remove('selected') 160 | bodyElem.style.display = 'none' 161 | window.__COM_ACTIVE.DB = '' 162 | } 163 | 164 | DropdownBox.prototype._renderMain = function(elem, props, renderEvent) { 165 | // GUID 166 | let guid = props.guid 167 | if(elem.getAttribute('guid')) 168 | guid = elem.getAttribute('guid') 169 | 170 | let children = elem.childNodes 171 | let article = null 172 | 173 | if(children.length){ 174 | for(let i = 0; i < children.length; i++){ 175 | if(children[i].nodeName.toLowerCase() == 'article'){ 176 | article = children[i] 177 | break 178 | } 179 | } 180 | } 181 | 182 | if(!props.stroke) 183 | elem.style.border = "1px solid " + window.__COM_COLOR_GROUP.BORDER_COLOR 184 | 185 | if(props.options) { 186 | let options = JSON.parse(props.options) 187 | let optionValue = null 188 | 189 | if (props.optionValue) 190 | optionValue = props.optionValue 191 | 192 | // Read option value 193 | if (window.__COM_RENDERED.DB[guid] && window.__COM_RENDERED.DB[guid].optionValue) 194 | optionValue = window.__COM_RENDERED.DB[guid].optionValue + "" 195 | 196 | if(optionValue) { 197 | let optionText = this._isValidOV(optionValue, options) 198 | 199 | if (optionText != "") { 200 | elem.style.color = window.__COM_COLOR_GROUP.ACTIVE_COLOR 201 | 202 | if(article) 203 | article.innerText = optionText 204 | } 205 | } 206 | } 207 | /* Default Styles End */ 208 | } 209 | 210 | DropdownBox.prototype._renderFullBody = function(elem, props, renderEvent) { 211 | // GUID 212 | let guid = props.guid 213 | if(elem.getAttribute('guid')) 214 | guid = elem.getAttribute('guid') 215 | 216 | let bodyElem = null 217 | let optionsElement = null 218 | 219 | if (renderEvent) { 220 | bodyElem = document.createElement('div') 221 | bodyElem.classList.add(window.__COM_CLASS_GROUP.DB_BODY) 222 | bodyElem.style.display = 'none' 223 | bodyElem.setAttribute('guid', guid) 224 | 225 | optionsElement = document.createElement('div') 226 | optionsElement.classList.add(window.__COM_CLASS_GROUP.DB_OPTIONS) 227 | optionsElement.setAttribute('guid', guid) 228 | 229 | bodyElem.appendChild(optionsElement) 230 | 231 | elem.appendChild(bodyElem) 232 | } else { 233 | bodyElem = this._getBodyObject(guid) 234 | 235 | optionsElement = this._getOptionsObject(guid) 236 | } 237 | 238 | if (!bodyElem || !optionsElement) 239 | return 240 | 241 | // Options 242 | this._renderOptions(optionsElement, props, guid, renderEvent) 243 | } 244 | 245 | DropdownBox.prototype._renderOptions = function(elem, props, guid, renderEvent) { 246 | let previousOptions = [] 247 | let options = [] 248 | 249 | // Current Options 250 | if(props.options) { 251 | options = JSON.parse(props.options) 252 | } 253 | 254 | // Previous Options 255 | if (window.__COM_RENDERED.DB[guid] && window.__COM_RENDERED.DB[guid].options) { 256 | previousOptions = JSON.parse(window.__COM_RENDERED.DB[guid].options) 257 | } 258 | 259 | // Read option value 260 | let optionValue = null 261 | if (props.optionValue) 262 | optionValue = props.optionValue 263 | if (window.__COM_RENDERED.DB[guid] && window.__COM_RENDERED.DB[guid].optionValue) 264 | optionValue = window.__COM_RENDERED.DB[guid].optionValue + "" 265 | 266 | // Save Options 267 | if (!window.__COM_RENDERED.DB[guid]) 268 | window.__COM_RENDERED.DB[guid] = {} 269 | window.__COM_RENDERED.DB[guid].options = props.options 270 | 271 | // Style 272 | let height = 50 273 | if(props.optionHeight && !isNaN(props.optionHeight)) { 274 | height = parseFloat(props.optionHeight) 275 | } 276 | 277 | if(options.length > 5) { 278 | elem.style.maxHeight = (height * 5) + 'px' 279 | } else { 280 | elem.style.maxHeight = 'auto' 281 | } 282 | 283 | if (!this._equalOptions(previousOptions, options, guid)) { // Options Changed - Need to Re-render 284 | elem.innerHTML = '' 285 | 286 | if(options && options.length > 0) { 287 | for(let i = 0; i < options.length; i++) { 288 | let option = options[i] 289 | 290 | this._renderOption(elem, props, guid, option, optionValue, renderEvent) 291 | } 292 | } 293 | } 294 | } 295 | 296 | module.exports = new DropdownBox() -------------------------------------------------------------------------------- /src/PrestoUIInterface.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | var prestoDom = require("./doms"); 27 | var helpers = require("./helpers") 28 | var webParseParams; 29 | var iOSParseParams; 30 | var parseParams; 31 | const helper = require('./helper'); 32 | 33 | if (window.__OS === "WEB") { 34 | webParseParams = helpers.web.parseParams; 35 | } else if (window.__OS === "IOS") { 36 | iOSParseParams = helpers.ios.parseParams; 37 | } else if (window.__OS === "ANDROID") { 38 | parseParams = helpers.android.parseParams; 39 | } 40 | 41 | function domAll(elem) { 42 | if (!elem.__ref) { 43 | elem.__ref = createPrestoElement(); 44 | } 45 | 46 | if (elem.props.id) { 47 | elem.__ref.__id = parseInt(elem.props.id, 10) || elem.__ref.__id; 48 | } 49 | 50 | var type = helper.clone(elem.type,null); 51 | var props = helper.clone(elem.props,null); 52 | 53 | if (props.focus == false && window.__OS === "ANDROID") { 54 | delete props.focus; 55 | } 56 | 57 | var children = []; 58 | 59 | for (var i = 0; i < elem.children.length; i++) { 60 | children.push(domAll(elem.children[i])); 61 | } 62 | 63 | 64 | if (__OS == "WEB" && props.onResize) { 65 | window.__resizeEvent = props.onResize; 66 | } 67 | 68 | props.id = elem.__ref.__id; 69 | if(elem.parentType && window.__OS == "ANDROID") 70 | return prestoDom({elemType: type, parentType: elem.parentType}, props, children); 71 | 72 | return prestoDom(type, props, children); 73 | } 74 | 75 | function getDomToRender(elem) { 76 | var res = domAll(elem); 77 | if (window.__OS == "ANDROID") { 78 | return JSON.stringify(res); 79 | } else { 80 | return res; 81 | } 82 | } 83 | 84 | 85 | // Not exported 86 | function cmdForAndroid(config, set, type) { 87 | if (set) { 88 | if (config.id) { 89 | var obj = parseParams(type, config, "set"); 90 | var cmd = obj.runInUI.replace("this->setId", "set_view=ctx->findViewById").replace(/this->/g, "get_view->"); 91 | cmd = cmd.replace(/PARAM_CTR_HOLDER[^;]*/g, "get_view->getLayoutParams;"); 92 | obj.runInUI = cmd; 93 | return obj; 94 | } else { 95 | console.error("ID null, this is not supposed to happen. Debug this or/and raise a issue in bitbucket."); 96 | } 97 | return {}; 98 | } 99 | 100 | var _id = config.id; 101 | var cmd = "set_view=ctx->findViewById:i_" + _id + ";"; 102 | // var runInUI; 103 | delete config.id; 104 | config.root = "true"; 105 | var obj = parseParams(type, config, "get"); 106 | obj.runInUI = cmd + obj.runInUI + ';'; 107 | obj.id = _id; 108 | return obj; 109 | } 110 | 111 | function getRunInUICmd(prop) { 112 | var cmd; 113 | if (window.__OS == "ANDROID") { 114 | cmd = cmdForAndroid(prop, true, "linearLayout").runInUI; 115 | } else if (window.__OS == "IOS"){ 116 | cmd = prop; 117 | } else { 118 | cmd = webParseParams("linearLayout", prop, "set"); 119 | } 120 | return cmd; 121 | } 122 | 123 | 124 | 125 | // Not exported 126 | function applyProp(element, attribute, set) { 127 | var prop = { 128 | id: element.__ref.__id 129 | } 130 | prop[attribute.value0] = attribute.value1; 131 | 132 | if (attribute.value0 == 'focus' && attribute.value1 == false && window.__OS == "ANDROID") { 133 | return; 134 | } 135 | 136 | if (window.__OS == "ANDROID") { 137 | var cmd = cmdForAndroid(prop, set, element.type); 138 | if (Android.updateProperties) { 139 | Android.updateProperties(JSON.stringify(cmd)); 140 | } else { 141 | Android.runInUI(cmd.runInUI, null); 142 | } 143 | } else if (window.__OS == "IOS"){ 144 | Android.runInUI(prop); 145 | } else { 146 | Android.runInUI(webParseParams("linearLayout", prop, "set")); 147 | } 148 | // Android.runInUI(parseParams("linearLayout", prop, "set")); 149 | } 150 | 151 | function replaceView(element, attribute, removeProp) { 152 | // console.log("REPLACE VIEW", element.__ref.__id, element.props); 153 | var props = helper.clone(element.props,null); 154 | props.id = element.__ref.__id; 155 | var rep; 156 | var viewGroups = ["linearLayout", "relativeLayout", "scrollView", "frameLayout", "horizontalScrollView"]; 157 | 158 | if (viewGroups.indexOf(element.type) != -1){ 159 | props.root = true; 160 | rep = prestoDom(element.type, props, []); 161 | } else if (window.__OS == "ANDROID") { 162 | rep = prestoDom({elemType: element.type, parentType: element.parentNode.type}, props, []); 163 | } else { 164 | rep = prestoDom(element.type, props, []); 165 | } 166 | if(window.__OS == "ANDROID"){ 167 | Android.replaceView(JSON.stringify(rep), element.__ref.__id); 168 | } else { 169 | Android.replaceView(rep, element.__ref.__id); 170 | } 171 | } 172 | 173 | 174 | function createPrestoElement() { 175 | if(typeof(window.__ui_id_sequence) != "undefined" && window.__ui_id_sequence != null) { 176 | return { __id : ++window.__ui_id_sequence }; 177 | } else { 178 | window.__ui_id_sequence = typeof Android.getNewID == "function" ? parseInt(Android.getNewID()) * 1000000 : window.__PRESTO_ID ; 179 | return { __id : ++window.__ui_id_sequence }; 180 | } 181 | }; 182 | 183 | 184 | function moveChild(child, parent, index) { 185 | Android.moveView(child.__ref.__id, index); 186 | } 187 | 188 | 189 | 190 | function removeChild(child, parent, index) { 191 | // console.log("Remove child :", child.type); 192 | Android.removeView(child.__ref.__id + ""); 193 | } 194 | 195 | function addChild(child, parent, index) { 196 | if(child.type == null) { 197 | console.warn("child null"); 198 | } 199 | // console.log("Add child :", child.__ref.__id, child.type); 200 | var viewGroups = ["linearLayout", "relativeLayout", "scrollView", "frameLayout", "horizontalScrollView"]; 201 | if (window.__OS == "ANDROID") { 202 | if (viewGroups.indexOf(child.type) != -1){ 203 | child.props.root = true; 204 | } else { 205 | child.parentType = parent.type; 206 | } 207 | Android.addViewToParent(parent.__ref.__id + "", JSON.stringify(domAll(child)), index, null, null); 208 | } 209 | else 210 | Android.addViewToParent(parent.__ref.__id, domAll(child), index, null, null); 211 | } 212 | 213 | function addAttribute(element, attribute) { 214 | // console.log("add attr :", attribute); 215 | element.props[attribute.value0] = attribute.value1; 216 | applyProp(element, attribute, true); 217 | } 218 | 219 | function removeAttribute(element, attribute) { 220 | // console.log("remove attr :", attribute); 221 | replaceView(element, attribute, true); 222 | } 223 | 224 | function updateAttribute(element, attribute) { 225 | // console.log("update attr :", attribute); 226 | element.props[attribute.value0] = attribute.value1; 227 | 228 | applyProp(element, attribute, false); 229 | } 230 | 231 | 232 | function insertDom(root, dom, cb) { 233 | root.children.push(dom); 234 | dom.parentNode = root; 235 | dom.__ref = createPrestoElement(); 236 | window.N = root; 237 | 238 | var rootId = window.__ROOTSCREEN.idSet.root; 239 | 240 | dom.props.root = true; 241 | 242 | var length = window.__ROOTSCREEN.idSet.child.push(dom.__ref.__id); 243 | 244 | var callback = window.callbackMapper(executePostProcess(cb)); 245 | if (window.__OS == "ANDROID") { 246 | Android.addViewToParent(rootId + "", JSON.stringify(domAll(dom)), length - 1, callback, null); 247 | } 248 | else { 249 | Android.addViewToParent(rootId, domAll(dom), length - 1, callback, null); 250 | } 251 | 252 | return dom.__ref.__id; 253 | 254 | } 255 | 256 | 257 | function executePostProcess(cb) { 258 | return function() { 259 | cb(); 260 | 261 | if(window.__dui_screen && window["afterRender"]) { 262 | for (var tag in window["afterRender"][window.__dui_screen]) { 263 | try { 264 | window["afterRender"][window.__dui_screen][tag]()(); 265 | window["afterRender"][window.__dui_screen]["executed"] = true; 266 | } 267 | catch (err) { 268 | console.warn(err); 269 | } 270 | } 271 | } 272 | 273 | if (JBridge && JBridge.setShadow) { 274 | for (var tag in window.shadowObject) { 275 | JBridge.setShadow(window.shadowObject[tag]["level"], 276 | JSON.stringify(window.shadowObject[tag]["viewId"]), 277 | JSON.stringify(window.shadowObject[tag]["backgroundColor"]), 278 | JSON.stringify(window.shadowObject[tag]["blurValue"]), 279 | JSON.stringify(window.shadowObject[tag]["shadowColor"]), 280 | JSON.stringify(window.shadowObject[tag]["dx"]), 281 | JSON.stringify(window.shadowObject[tag]["dy"]), 282 | JSON.stringify(window.shadowObject[tag]["spread"]), 283 | JSON.stringify(window.shadowObject[tag]["factor"]) 284 | ); 285 | } 286 | } 287 | } 288 | } 289 | 290 | module.exports = { 291 | domAll: domAll, 292 | getDomToRender: getDomToRender, 293 | getRunInUICmd: getRunInUICmd, 294 | replaceView: replaceView, 295 | createPrestoElement: createPrestoElement, 296 | moveChild: moveChild, 297 | removeChild: removeChild, 298 | addChild: addChild, 299 | addAttribute: addAttribute, 300 | removeAttribute: removeAttribute, 301 | updateProperty: updateAttribute, 302 | addProperty: addAttribute, 303 | insertDom: insertDom 304 | }; 305 | 306 | -------------------------------------------------------------------------------- /src/init.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | /* 27 | Used to initialize defaults for window funcions and variables. 28 | */ 29 | 30 | window.presto_ui_version = __VERSION__; 31 | const { merge } = require('./helper'); 32 | window.PrestoUI = require("./PrestoUIInterface"); 33 | 34 | const getCurrTime = () => (new Date()).getTime() 35 | if(window.__OS == "ANDROID"){ 36 | var getScreenDetails = function() { 37 | try { 38 | let details = JSON.parse(Android.getScreenDimensions()); 39 | return { 40 | screen_width: details.width + "", 41 | screen_height: details.height + "" 42 | }; 43 | } catch (err) { 44 | console.error( 45 | "error in gettting screen dimensions, setting default values", err); 46 | return { 47 | screen_width: "1080", 48 | screen_height: "1920" 49 | }; 50 | } 51 | }; 52 | window.__DEVICE_DETAILS = getScreenDetails(); 53 | } 54 | 55 | if(window.__OS !="WEB"){ 56 | if(!window.parent.juspayAssetConfig){ 57 | try{ 58 | window.parent.juspayAssetConfig = JSON.parse(JBridge.loadFileInDUI("juspay_assets.json")); 59 | }catch(e){ 60 | window.parent.juspayAssetConfig = {}; 61 | } 62 | 63 | } 64 | window.juspayAssetConfig = window.parent.juspayAssetConfig; 65 | } 66 | 67 | if (window.__OS == "WEB") { 68 | // In case of iFrame based integration, this will cause CORS exception. 69 | try { 70 | window.Android = window.parent.Android?window.parent.Android:require('./WEB/AndroidInterface') 71 | } catch (e){ 72 | window.Android = require('./WEB/AndroidInterface') 73 | console.log("Failed to load window.parent.Android"); 74 | } 75 | 76 | window.JBridge = window.JBridge ? window.JBridge:window.parent.JBridge; 77 | } else if (window.__OS == "IOS") { 78 | window.Android = require("./IOS/AndroidInterface") 79 | window.JBridge = merge(window.JBridge, {}) 80 | } 81 | 82 | if (window.__DEVICE_DETAILS && window.__DEVICE_DETAILS.hasOwnProperty("screen_width")) { 83 | window.__WIDTH = window.__DEVICE_DETAILS.screen_width; 84 | } else { 85 | window.__WIDTH = "1080"; 86 | } 87 | 88 | if (window.__DEVICE_DETAILS && window.__DEVICE_DETAILS.hasOwnProperty("screen_height")) { 89 | window.__HEIGHT = window.__DEVICE_DETAILS.screen_height; 90 | } else { 91 | window.__HEIGHT = "1920"; 92 | } 93 | 94 | const guid = Math.random().toString(36).substr(2, 9) 95 | 96 | //Intializing defaults 97 | if (window.__OS == "WEB") { 98 | /* Components */ 99 | window.__COM_EVENT = false 100 | window.__COM_RENDERED = { 101 | SWITCH_GLOBAL: false, 102 | DRP: {}, 103 | DSB: {}, 104 | DB: {}, 105 | NAVBAR: {} 106 | } 107 | 108 | window.__COM_CLASS_GROUP = { 109 | WRAPPER: 'PDC_com_wrapper', 110 | NAVBAR: 'PDC_com_navbar', 111 | NAVBAR_ITEM: 'PDC_com_navbar_item', 112 | NAVBAR_ITEM_WITH_SUB: 'PDC_com_navbar_has_sub', 113 | NAVBAR_SUB: 'PDC_com_navbar_sub', 114 | NAVBAR_SUB_RIGHT: 'PDC_com_navbar_sub_right', 115 | NAVBAR_SUB_ITEM: 'PDC_com_navbar_subitem', 116 | DRP: 'PDC_com_drp', 117 | DRP_BODY: 'PDC_com_drp_body', 118 | DB: 'PDC_com_db', 119 | DB_FULL_BODY: 'PDC_com_db_full_body', 120 | DB_BODY: 'PDC_com_db_body', 121 | DB_OPTIONS: 'PDC_com_db_options', 122 | DB_OPTION: 'PDC_com_db_option', 123 | DSB: 'PDC_com_dsb', 124 | DSB_FULL_BODY: 'PDC_com_dsb_full_body', 125 | DSB_BODY: 'PDC_com_dsb_body', 126 | DSB_OPTIONS: 'PDC_com_dsb_options', 127 | DSB_OPTION: 'PDC_com_dsb_option', 128 | DSB_SEARCH_WRAP: 'PDC_com_dsb_search_wrap', 129 | DSB_SEARCH: 'PDC_com_dsb_search', 130 | SWITCH: 'PDC_com_switch', 131 | SWITCH_BODY: 'PDC_com_switch_body', 132 | SWITCH_SLIDER: 'PDC_com_switch_slider', 133 | BT: 'PDC_com_bt', 134 | BT_DISABLED: 'PDC_com_bt_disabled' 135 | } 136 | window.__COM_COLOR_GROUP = { 137 | BG: '#ffffff', 138 | ACTIVE_BG: '#EEF1F8', 139 | ACTIVE_COLOR: 'rgb(53, 64, 82)', 140 | INACTIVE_COLOR: 'rgba(53, 64, 82, 0.5)', 141 | BORDER_COLOR: '#A3AFC2', 142 | ACTIVE_BORDER_COLOR: '#707886', 143 | SEARCH_COLOR: '#dddddd', 144 | INACTIVE_SWITCH: '#B7DBBC', 145 | ACTIVE_SWITCH: '#36AF47', 146 | BT_BORDER_COLOR: '#1585D8', 147 | BT_BG_COLOR: '#1991EB', 148 | BT_COLOR: '#ffffff' 149 | } 150 | window.__COM_ACTIVE = { 151 | DSB: '', 152 | NAVBAR: '', 153 | DB: '' 154 | } 155 | /* Components End */ 156 | 157 | /* Modal */ 158 | window.__STYLE_ID = 'style_' + guid 159 | window.__RENDERED_KEYFRAMES = [] 160 | window.__MODAL_PROPS = {} 161 | window.__CONTENTMODAL_CLASS = 'PDC_modal' 162 | window.__OPENMODAL_CLASS = 'PDC_modal-open' 163 | window.__BACKDROPMODAL_CLASS = 'PDC_modal-backdrop' 164 | window.__DISABLEDBACKDROP_CLASS = 'PDC_modal-backdrop-disabled' 165 | window.__SHOWNMODAL_CLASS = 'PDC_modal-shown' 166 | window.__FADEMODAL_CLASS = 'PDC_modal-fade' 167 | window.__SLIDEMODAL_CLASS = 'PDC_modal-slide' 168 | /* Modal End */ 169 | 170 | window.onclick = (event) => { 171 | if (event.target && 172 | event.target.classList.contains(window.__BACKDROPMODAL_CLASS) && 173 | !event.target.classList.contains(window.__DISABLEDBACKDROP_CLASS) 174 | ) { 175 | document.body.classList.remove(window.__OPENMODAL_CLASS); 176 | event.target.classList.remove(window.__SHOWNMODAL_CLASS); 177 | 178 | let id = event.target.id; 179 | id = id.replace(window.__BACKDROPMODAL_CLASS + '_', ''); 180 | 181 | let view = __VIEWS[id]; 182 | if (view && view.props.onDismiss && typeof view.props.onDismiss == 183 | "function") { 184 | view.props.onDismiss(); 185 | } 186 | } 187 | } 188 | 189 | const duiShowScreen = (callback, state) => { 190 | let popupMenu = document.getElementsByClassName("popupMenu"); 191 | for (let i = 0; i < popupMenu.length; i++) { 192 | popupMenu[i].style.display = "none"; 193 | } 194 | }; 195 | window.__duiShowScreen = duiShowScreen; 196 | 197 | } else if (window.__OS == "IOS") { 198 | window.__COLOR_INDEX = window.__COLOR_INDEX || 1; 199 | window.__FONT_INDEX = window.__FONT_INDEX || 1; 200 | window.__RECT_INDEX = window.RECT_INDEX || 1; 201 | window.__VIEW_INDEX = window.__VIEW_INDEX || 1; 202 | window.__IMAGE_INDEX = window.__IMAGE_INDEX || 1; 203 | window.__POINT_INDEX = window.__POINT_INDEX || 1; 204 | window.__LAYER_INDEX = window.__LAYER_INDEX || 1; 205 | window.__SIZE_INDEX = window.__SIZE_INDEX || 1; 206 | } else { 207 | //This would be case for android 208 | window.__FN_MAP = {}; 209 | window.__ALL_ONCLICKS = []; 210 | } 211 | 212 | window.__ID = 1; 213 | window.__NODE_ID = 1; 214 | window.__SCREEN_INDEX = -1; 215 | // Adding temp fix - Will be moved to mystique-web 216 | try { 217 | window.__PROXY_FN = window.parent.__PROXY_FN?window.parent.__PROXY_FN:{}; 218 | } catch (error) { 219 | window.__PROXY_FN = {}; 220 | } 221 | window.__FN_INDEX = 0; 222 | window.__ROOTSCREEN = null; 223 | window.__CACHED_SCREENS = {}; 224 | window.__SCREEN_COUNT = 0; 225 | window.__CURR_SCREEN = null; 226 | window.__PREV_SCREEN = null; 227 | window.__ANIMATE_DIR = null; 228 | window.__SCREEN_STACK = []; 229 | window.__LAST_FN_CALLED = null; 230 | window.__THROTTELED_ACTIONS = []; 231 | window.__isMOBILE = window.innerWidth < 550 // this line takes up 80ms out of 360ms 232 | window.__isFIREFOX = navigator.userAgent.toLowerCase().indexOf('firefox') > -1 || typeof InstallTrigger !== 'undefined'; 233 | // Adding temp fix - Will be moved to mystique-web 234 | try { 235 | window.__VIEWS = window.parent.__VIEWS?window.parent.__VIEWS:{}; 236 | } catch (error) { 237 | window.__VIEWS = {}; 238 | } 239 | window.__VIEW_DIMENSIONS = {}; 240 | window.__OBSERVERS = {}; 241 | window.ZIndex = 0; 242 | 243 | window.callUICallback = function () { 244 | let args = (arguments.length === 1 ? [arguments[0]] : Array.apply(null, 245 | arguments)); 246 | var fName = args[0] 247 | var functionArgs = args.slice(1) 248 | var currTime; 249 | var timeDiff; 250 | 251 | if (window.__ALL_ONCLICKS && window.__ALL_ONCLICKS.indexOf(fName) != -1 && args[2] == "feedback" && JBridge && JBridge.setClickFeedback) { 252 | return JBridge.setClickFeedback(args[1]); 253 | } 254 | 255 | if (window.__THROTTELED_ACTIONS && window.__THROTTELED_ACTIONS.indexOf(fName) == -1) { 256 | window.__PROXY_FN[fName].apply(null, functionArgs); 257 | } else if (window.__LAST_FN_CALLED && (fName == window.__LAST_FN_CALLED.fName)) { 258 | currTime = getCurrTime(); 259 | timeDiff = currTime - window.__LAST_FN_CALLED.timeStamp; 260 | 261 | if (timeDiff >= 300) { 262 | window.__PROXY_FN[fName].apply(null, functionArgs); 263 | window.__LAST_FN_CALLED.timeStamp = currTime; 264 | } else { 265 | console.warn("function throtteled", fName); 266 | console.warn("time diff", timeDiff); 267 | } 268 | } else { 269 | window.__PROXY_FN[fName].apply(null, functionArgs); 270 | window.__LAST_FN_CALLED = { 271 | timeStamp: (new Date()).getTime(), 272 | fName: fName 273 | } 274 | } 275 | }; 276 | -------------------------------------------------------------------------------- /src/components/DropdownSearchBox.js: -------------------------------------------------------------------------------- 1 | function DropdownSearchBox() {} 2 | 3 | DropdownSearchBox.prototype._equalOptions = function(a, b, j) { 4 | let stringA = a.join(j).trim() 5 | let stringB = b.join(j).trim() 6 | 7 | if (stringA == stringB) 8 | return true 9 | return false 10 | } 11 | 12 | DropdownSearchBox.prototype._selectOptionByGUID = function(guid, optionElem) { 13 | let value = optionElem.getAttribute('option-value') 14 | let text = optionElem.getAttribute('option-text') 15 | 16 | let mainObject = this._getMainObject(guid) 17 | let mainObjectID = mainObject.id 18 | 19 | if(!value || !text || !mainObjectID) 20 | return 21 | 22 | let view = window.__VIEWS[mainObjectID] 23 | 24 | if(!view || !view.props) 25 | return 26 | 27 | // Save Option Value 28 | if (!window.__COM_RENDERED.DSB[guid]) 29 | window.__COM_RENDERED.DSB[guid] = {} 30 | window.__COM_RENDERED.DSB[guid].optionValue = value 31 | 32 | // Update UI 33 | let optionsElem = this._getOptionsObject(guid) 34 | if (optionsElem && optionsElem.childNodes) { 35 | for (let i = 0; i < optionsElem.childNodes.length; i++) { 36 | if (optionsElem.childNodes[i].getAttribute('option-value') == value && optionsElem.childNodes[i].getAttribute('option-text') == text) { 37 | optionsElem.childNodes[i].classList.add('selected') 38 | } else { 39 | optionsElem.childNodes[i].classList.remove('selected') 40 | } 41 | } 42 | } 43 | 44 | let children = mainObject.childNodes 45 | let article = null 46 | 47 | if(children.length){ 48 | for(let i = 0; i < children.length; i++){ 49 | if(children[i].nodeName.toLowerCase() == 'article'){ 50 | article = children[i] 51 | break 52 | } 53 | } 54 | } 55 | 56 | mainObject.style.color = window.__COM_COLOR_GROUP.ACTIVE_COLOR 57 | if (article) 58 | article.innerText = text 59 | 60 | // Event Trigger 61 | if (view.props.onSelect && typeof view.props.onSelect == "function") { 62 | view.props.onSelect(value) 63 | } 64 | } 65 | 66 | DropdownSearchBox.prototype._isValidOV = function(optionValue, options) { 67 | let optionText = "" 68 | 69 | if(options && options.length > 0) { 70 | for(let i in options) { 71 | if(options[i].value == optionValue) { 72 | optionText = options[i].text 73 | break 74 | } 75 | } 76 | } 77 | 78 | return optionText 79 | } 80 | 81 | DropdownSearchBox.prototype._getMainObject = function(guid) { 82 | let query = '.' + window.__COM_CLASS_GROUP.DSB + '[guid="' + guid +'"]' 83 | return document.body.querySelector(query) 84 | } 85 | 86 | DropdownSearchBox.prototype._getFullBodyObject = function(guid) { 87 | let query = '.' + window.__COM_CLASS_GROUP.DSB_FULL_BODY + '[guid="' + guid +'"]' 88 | return document.body.querySelector(query) 89 | } 90 | 91 | DropdownSearchBox.prototype._getBodyObject = function(guid) { 92 | let query = '.' + window.__COM_CLASS_GROUP.DSB_BODY + '[guid="' + guid +'"]' 93 | return document.body.querySelector(query) 94 | } 95 | 96 | DropdownSearchBox.prototype._getSearchWrapObject = function(guid) { 97 | let query = '.' + window.__COM_CLASS_GROUP.DSB_SEARCH_WRAP + '[guid="' + guid +'"]' 98 | return document.body.querySelector(query) 99 | } 100 | 101 | DropdownSearchBox.prototype._getSearchObject = function(guid) { 102 | let query = '.' + window.__COM_CLASS_GROUP.DSB_SEARCH + '[guid="' + guid +'"]' 103 | return document.body.querySelector(query) 104 | } 105 | 106 | DropdownSearchBox.prototype._getOptionsObject = function(guid) { 107 | let query = '.' + window.__COM_CLASS_GROUP.DSB_OPTIONS + '[guid="' + guid +'"]' 108 | return document.body.querySelector(query) 109 | } 110 | 111 | DropdownSearchBox.prototype._filterOptions = function(guid, query) { 112 | let optionsElem = this._getOptionsObject(guid) 113 | 114 | if (!optionsElem) 115 | return 116 | 117 | query = query.toLowerCase() 118 | 119 | if (optionsElem.childNodes) { 120 | for (let i = 0; i < optionsElem.childNodes.length; i++) { 121 | let optionElem = optionsElem.childNodes[i] 122 | let text = optionElem.getAttribute('option-text') 123 | 124 | if (text) { 125 | text = text.toLowerCase() 126 | 127 | if(text.indexOf(query) !== -1) { 128 | optionElem.style.display = 'flex' 129 | } else { 130 | optionElem.style.display = 'none' 131 | } 132 | } 133 | } 134 | } 135 | } 136 | 137 | DropdownSearchBox.prototype._renderOption = function(parentElem, props, guid, option, optionValue, renderEvent) { 138 | let elem = document.createElement('div') 139 | 140 | elem.className = window.__COM_CLASS_GROUP.DSB_OPTION 141 | elem.setAttribute('option-value', option.value) 142 | elem.setAttribute('option-text', option.text) 143 | 144 | let height = 50 145 | if(props.optionHeight && !isNaN(props.optionHeight)) { 146 | height = parseFloat(props.optionHeight) 147 | } 148 | 149 | elem.style.height = height + 'px' 150 | 151 | if(props.fontSize) 152 | elem.style.fontSize = props.fontSize + 'px' 153 | if(props.fontFamily) 154 | elem.style.fontFamily = props.fontFamily 155 | if(props.optionPadding) { 156 | let padding = props.optionPadding.split(',').map(a => a * 1); 157 | 158 | elem.style.padding = padding[1] + 'px ' + padding[2] + 'px ' + padding[3] + 'px ' + padding[0] + 'px' 159 | } 160 | 161 | elem.setAttribute('guid', guid) 162 | 163 | if(optionValue && optionValue == option.value) { 164 | elem.classList.add('selected') 165 | } else { 166 | elem.classList.remove('selected') 167 | } 168 | 169 | let article = document.createElement('ARTICLE') 170 | article.innerText = option.text 171 | 172 | elem.appendChild(article) 173 | parentElem.appendChild(elem) 174 | } 175 | 176 | DropdownSearchBox.prototype._openByGUID = function(guid) { 177 | let object = this._getMainObject(guid) 178 | let bodyElem = this._getBodyObject(guid) 179 | 180 | if(!object || !bodyElem) 181 | return 182 | 183 | object.classList.add('selected') 184 | bodyElem.style.display = 'block' 185 | window.__COM_ACTIVE.DSB = guid 186 | } 187 | 188 | DropdownSearchBox.prototype._closeByGUID = function(guid) { 189 | let object = this._getMainObject(guid) 190 | let bodyElem = this._getBodyObject(guid) 191 | 192 | if(!object || !bodyElem) 193 | return 194 | 195 | object.classList.remove('selected') 196 | bodyElem.style.display = 'none' 197 | window.__COM_ACTIVE.DSB = '' 198 | } 199 | 200 | DropdownSearchBox.prototype._renderMain = function(elem, props, renderEvent) { 201 | // GUID 202 | let guid = props.guid 203 | if(elem.getAttribute('guid')) 204 | guid = elem.getAttribute('guid') 205 | 206 | let children = elem.childNodes 207 | let article = null 208 | 209 | if(children.length){ 210 | for(let i = 0; i < children.length; i++){ 211 | if(children[i].nodeName.toLowerCase() == 'article'){ 212 | article = children[i] 213 | break 214 | } 215 | } 216 | } 217 | 218 | if(!props.stroke) 219 | elem.style.border = "1px solid " + window.__COM_COLOR_GROUP.BORDER_COLOR 220 | 221 | if(props.options) { 222 | let options = JSON.parse(props.options) 223 | let optionValue = null 224 | 225 | if (props.optionValue) 226 | optionValue = props.optionValue 227 | 228 | // Read option value 229 | if (window.__COM_RENDERED.DSB[guid] && window.__COM_RENDERED.DSB[guid].optionValue) 230 | optionValue = window.__COM_RENDERED.DSB[guid].optionValue + "" 231 | 232 | if (optionValue) { 233 | let optionText = this._isValidOV(optionValue, options) 234 | 235 | if(optionText != "") { 236 | elem.style.color = window.__COM_COLOR_GROUP.ACTIVE_COLOR 237 | 238 | if(article) 239 | article.innerText = optionText 240 | } 241 | } 242 | } 243 | /* Default Styles End */ 244 | } 245 | 246 | DropdownSearchBox.prototype._renderFullBody = function(elem, props, renderEvent) { 247 | // GUID 248 | let guid = props.guid 249 | if(elem.getAttribute('guid')) 250 | guid = elem.getAttribute('guid') 251 | 252 | let bodyElem = null 253 | let searchWrapElement = null 254 | let searchElement = null 255 | let optionsElement = null 256 | 257 | if (renderEvent) { 258 | bodyElem = document.createElement('div') 259 | bodyElem.classList.add(window.__COM_CLASS_GROUP.DSB_BODY) 260 | bodyElem.style.display = 'none' 261 | bodyElem.setAttribute('guid', guid) 262 | 263 | searchWrapElement = document.createElement('div') 264 | searchWrapElement.classList.add(window.__COM_CLASS_GROUP.DSB_SEARCH_WRAP) 265 | searchWrapElement.setAttribute('guid', guid) 266 | 267 | searchElement = document.createElement('INPUT') 268 | searchElement.setAttribute('type', 'text') 269 | searchElement.setAttribute('placeholder', 'Search...') 270 | searchElement.classList.add(window.__COM_CLASS_GROUP.DSB_SEARCH) 271 | searchElement.setAttribute('guid', guid) 272 | 273 | searchWrapElement.appendChild(searchElement) 274 | 275 | bodyElem.appendChild(searchWrapElement) 276 | 277 | optionsElement = document.createElement('div') 278 | optionsElement.classList.add(window.__COM_CLASS_GROUP.DSB_OPTIONS) 279 | optionsElement.setAttribute('guid', guid) 280 | 281 | bodyElem.appendChild(optionsElement) 282 | 283 | elem.appendChild(bodyElem) 284 | } else { 285 | bodyElem = this._getBodyObject(guid) 286 | 287 | searchWrapElement = this._getSearchWrapObject(guid) 288 | 289 | searchElement = this._getSearchObject(guid) 290 | 291 | optionsElement = this._getOptionsObject(guid) 292 | } 293 | 294 | if (!bodyElem || !searchWrapElement || !searchElement || !optionsElement) 295 | return 296 | 297 | // Styles 298 | if(props.fontSize) 299 | searchElement.style.fontSize = props.fontSize + 'px' 300 | if(props.fontFamily) 301 | searchElement.style.fontFamily = props.fontFamily 302 | 303 | // Options 304 | this._renderOptions(optionsElement, props, guid, renderEvent) 305 | } 306 | 307 | DropdownSearchBox.prototype._renderOptions = function(elem, props, guid, renderEvent) { 308 | let previousOptions = [] 309 | let options = [] 310 | 311 | // Current Options 312 | if(props.options) { 313 | options = JSON.parse(props.options) 314 | } 315 | 316 | // Previous Options 317 | if (window.__COM_RENDERED.DSB[guid] && window.__COM_RENDERED.DSB[guid].options) { 318 | previousOptions = JSON.parse(window.__COM_RENDERED.DSB[guid].options) 319 | } 320 | 321 | // Read option value 322 | let optionValue = null 323 | if (props.optionValue) 324 | optionValue = props.optionValue 325 | if (window.__COM_RENDERED.DSB[guid] && window.__COM_RENDERED.DSB[guid].optionValue) 326 | optionValue = window.__COM_RENDERED.DSB[guid].optionValue + "" 327 | 328 | // Save Options 329 | if (!window.__COM_RENDERED.DSB[guid]) 330 | window.__COM_RENDERED.DSB[guid] = {} 331 | window.__COM_RENDERED.DSB[guid].options = props.options 332 | 333 | // Style 334 | let height = 50 335 | if(props.optionHeight && !isNaN(props.optionHeight)) { 336 | height = parseFloat(props.optionHeight) 337 | } 338 | 339 | if(options.length > 5) { 340 | elem.style.maxHeight = (height * 5) + 'px' 341 | } else { 342 | elem.style.maxHeight = 'auto' 343 | } 344 | 345 | if (!this._equalOptions(previousOptions, options, guid)) { // Options Changed - Need to Re-render 346 | elem.innerHTML = '' 347 | 348 | if(options && options.length > 0) { 349 | for(let i = 0; i < options.length; i++) { 350 | let option = options[i] 351 | 352 | this._renderOption(elem, props, guid, option, optionValue, renderEvent) 353 | } 354 | } 355 | } 356 | } 357 | 358 | module.exports = new DropdownSearchBox() -------------------------------------------------------------------------------- /src/WEB/AndroidInterface.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | var { 27 | inflateView, 28 | computeChildDimens, 29 | List, 30 | postComputeLayoutDimens, 31 | preComputeLayoutDimens, 32 | postCompute, 33 | isChrome50, 34 | handleMatchParentChrome50 35 | } = require("./Render"); 36 | var helper = require('../helper'); 37 | var callbackInvoker = require("../helpers/common/callbackInvoker"); 38 | const { diffArray } = require("./ListPresto"); 39 | const {setUseHintColor} = require("./MapAttributes"); 40 | var storedDiv = ""; 41 | 42 | var {addToContainerList, getContainer, getParentView, postRenderElements} = require('./Utils') 43 | 44 | function getScreenDimensions(namespace) { 45 | let element = getContainer(namespace); 46 | return JSON.stringify({ 47 | width: element.offsetWidth, 48 | height: element.offsetHeight 49 | }); 50 | } 51 | 52 | // Due to jos, PrestoDOM's document is different from the DOM Document which actaully contains the nodes. 53 | // This utility function allows PrestoDOM to acquire an actual DOM object. 54 | function getUIElement(id, namespace){ 55 | var ele = document.getElementById(id); 56 | return ele; 57 | } 58 | 59 | function findSiblingView(parentView, id) { 60 | for (let i = 0; i < parentView.children.length; i++) { 61 | if(parentView.children[i].props.id == id){ 62 | if(i != 0) 63 | return parentView.children[i-1] 64 | else 65 | return parentView 66 | } 67 | } 68 | return null; 69 | } 70 | 71 | function isOrientatationChanged(props){ 72 | if(props['orientation']) 73 | return false; 74 | return true; 75 | } 76 | 77 | function runInUI(cmd, namespace) { 78 | if (typeof cmd == "string") 79 | return 80 | let id = cmd.id 81 | if(id){ 82 | let elem = document.getElementById(id) 83 | 84 | if(elem){ 85 | let view = window.__VIEWS[id] 86 | if(cmd.itemView){ 87 | return; 88 | } 89 | if(!cmd.listData && view.listData) 90 | return; 91 | 92 | if(cmd.listData){ 93 | //diffing arrrays to find the difference between old data and new data 94 | //console.time('diffArray') 95 | console.log("diff") 96 | var x = cmd.listData 97 | if(typeof cmd.listData == "string"){ 98 | x = JSON.parse(cmd.listData) 99 | } 100 | view.props.diffArray = List.diffArray(view.props.itemDatas,x); 101 | //console.dir(view.props.diffArray) 102 | //console.timeEnd('diffArray') 103 | 104 | //no difference in data 105 | if(view.props.diffArray==[]){ 106 | return; 107 | } 108 | } 109 | view.props = helper.merge(view.props, cmd) 110 | 111 | let parentId = null 112 | let parentElement = null 113 | let parentView = null 114 | 115 | let stopChild = isOrientatationChanged(cmd); 116 | parentId = elem.parentNode.id 117 | 118 | if(parentId){ 119 | parentView = window.__VIEWS[parentId] 120 | parentElement = document.getElementById(parentId) 121 | 122 | if(parentElement && parentView){ 123 | let siblingView = findSiblingView(parentView,id); 124 | computeChildDimens(parentView); 125 | let computeList = []; 126 | // if(view.type == "relativeLayout") 127 | // computeList.push(view.props.id); 128 | var chrome50matchList; 129 | if(isChrome50()) { 130 | chrome50matchList = {h : [], w: []} 131 | } 132 | inflateView({view, parentElement, siblingView, renderStyle: true,stopChild,isListItem:true, computeList, chrome50matchList}); 133 | let parent = view.parent; 134 | for (var i =0;i 0){ 374 | for(let i = 0; i < childrenElem.length; i++){ 375 | elem.appendChild(childrenElem[i]) 376 | } 377 | } 378 | postCompute(computeList); 379 | handleMatchParentChrome50(chrome50matchList) 380 | /* Append Children End */ 381 | setPostRender(); 382 | } 383 | 384 | function addEventListeners(eventListeners) { 385 | for(var i = 0; i { 398 | e.stopPropagation() 399 | 400 | if (e.keyCode == 13) { 401 | callback(e) 402 | } 403 | }) 404 | } 405 | if (eventType == "change") { 406 | elem.addEventListener('input', (e) => { 407 | callback(e.target.value) 408 | }) 409 | } else if (eventType == "focus"){ 410 | elem.addEventListener('focus', (e) => { 411 | callback("true") 412 | }) 413 | elem.addEventListener('blur', (e) => { 414 | callback("false") 415 | }) 416 | } else { 417 | props.oldEventListener = props.oldEventListener || {}; 418 | if (typeof props.oldEventListener[eventType] == "function") { 419 | elem.removeEventListener(eventType, props.oldEventListener[eventType]); 420 | } 421 | props.oldEventListener[eventType] = (e) => { 422 | e.stopPropagation(); 423 | callback(e) 424 | }; 425 | elem.addEventListener(eventType, props.oldEventListener[eventType]); 426 | } 427 | } 428 | } 429 | } 430 | // function recompute() { 431 | // const rootnode = document.getElementById('content'); 432 | // const child = rootnode.firstElementChild; 433 | // const rootview = window.__VIEWS[child.id]; 434 | // Render(rootview, null); 435 | // } 436 | 437 | function getNewID() { 438 | window.JOS_PRESTO_ID = window.JOS_PRESTO_ID || 1; 439 | return window.JOS_PRESTO_ID++; 440 | } 441 | 442 | function getWindow() { 443 | return window; 444 | } 445 | 446 | function getDocument() { 447 | return document; 448 | } 449 | 450 | module.exports = { 451 | addToContainerList : addToContainerList, 452 | 453 | getScreenDimensions: getScreenDimensions, 454 | 455 | getUIElement : getUIElement, 456 | 457 | runInUI: runInUI, 458 | 459 | Render: Render, 460 | 461 | addViewToParent: addViewToParent, 462 | 463 | moveView: moveView, 464 | 465 | removeView: removeView, 466 | 467 | replaceView: replaceView, 468 | 469 | addEventListeners: addEventListeners, 470 | 471 | getNewID: getNewID, 472 | 473 | getWindow: getWindow, 474 | 475 | getDocument: getDocument, 476 | 477 | setUseHintColor : setUseHintColor 478 | 479 | }; -------------------------------------------------------------------------------- /src/helpers/web/parseParams.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2017 "JUSPAY Technologies" 3 | * JUSPAY Technologies Pvt. Ltd. [https://www.juspay.in] 4 | * 5 | * This file is part of JUSPAY Platform. 6 | * 7 | * JUSPAY Platform is free software: you can redistribute it and/or modify 8 | * it for only educational purposes under the terms of the GNU Affero General 9 | * Public License (GNU AGPL) as published by the Free Software Foundation, 10 | * either version 3 of the License, or (at your option) any later version. 11 | * For Enterprise/Commerical licenses, contact . 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The end user will 16 | * be liable for all damages without limitation, which is caused by the 17 | * ABUSE of the LICENSED SOFTWARE and shall INDEMNIFY JUSPAY for such 18 | * damages, claims, cost, including reasonable attorney fee claimed on Juspay. 19 | * The end user has NO right to claim any indemnification based on its use 20 | * of Licensed Software. See the GNU Affero General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Affero General Public License 23 | * along with this program. If not, see . 24 | */ 25 | 26 | 27 | /* 28 | 29 | 30 | THIS FILE IS REDUNDANT NOW, WEB NO LONGER CALLS ANY FUNCTION OF THIS FILE 31 | 32 | 33 | */ 34 | 35 | 36 | Array.prototype.rotate = (function () { 37 | // save references to array functions to make lookup faster 38 | var push = Array.prototype.push, 39 | splice = Array.prototype.splice; 40 | 41 | return function (count) { 42 | var len = this.length >>> 0, // convert to uint 43 | count = count >> 0; // convert to int 44 | 45 | // convert count to value in range [0, len) 46 | count = ((count % len) + len) % len; 47 | 48 | // use splice.call() instead of this.splice() to make function generic 49 | push.apply(this, splice.call(this, 0, count)); 50 | return this; 51 | }; 52 | })(); 53 | 54 | function flattenObject(ob) { 55 | var toReturn = {}; 56 | for (var i in ob) { 57 | if (!ob.hasOwnProperty(i)) continue; 58 | if ((typeof ob[i]) == 'object') { 59 | var flatObject = flattenObject(ob[i]); 60 | for (var x in flatObject) { 61 | if (!flatObject.hasOwnProperty(x)) continue; 62 | if (typeof flatObject[x] !== "undefined") 63 | toReturn[x] = flatObject[x]; 64 | } 65 | } else { 66 | toReturn[i] = ob[i]; 67 | } 68 | } 69 | 70 | return toReturn; 71 | } 72 | 73 | function parseColors(color) { 74 | if (color.length < 8) 75 | return color; 76 | 77 | if (color.indexOf("rgba") !== -1 || color.indexOf("rgb") !== -1) 78 | return color; 79 | 80 | var alpha = parseInt(color.substring(1, 3), 16); 81 | alpha = (alpha / 255).toFixed(2); 82 | 83 | var hexColor = color.substring(3, 9); 84 | var rgbaColor = "rgba(" + convertHexToRgb(hexColor) + "," + alpha + ")"; 85 | 86 | return rgbaColor; 87 | } 88 | 89 | function lookAndReplaceProp(str,match, val){ 90 | var output = match + "(" + val + "px)"; 91 | if (!str) return output; 92 | var start = str.indexOf(match); 93 | if (start >= 0){ 94 | var end = str.substr(start).indexOf(")"); 95 | if (end >= 0) { 96 | var found = str.substr(start,end+1); 97 | return str.replace(found,output); 98 | } 99 | else { 100 | return str+output; 101 | } 102 | } 103 | else return str+output; 104 | } 105 | 106 | 107 | function parseLayoutProps(type, config, key) { 108 | if (typeof config[key] == "undefined" || config[key] == null) { 109 | delete config[key]; 110 | return; 111 | } 112 | 113 | if (!config.style) { 114 | var t = "" 115 | var ele_id = document.getElementById(config.id) 116 | if (ele_id) { 117 | if (ele_id.style) 118 | t = (ele_id.style.transform) ? ele_id.style.transform : "" 119 | } 120 | config.style = {}; 121 | config.style.transform = t; 122 | config.animation = {}; 123 | config.animation.transform = ""; 124 | } 125 | 126 | if (!config.attributes) 127 | config.attributes = {}; 128 | 129 | if (!config.style.className) 130 | config.style.className = ""; 131 | 132 | if ((key == "onClick" || key == "onClickEvent")) { 133 | if(!isMobile && !config.cursorType){ 134 | config.style.cursor = "pointer"; 135 | } 136 | } 137 | if(key == "cursorType") { 138 | config.style.cursor = config.cursorType; 139 | } 140 | if (key == "textSize") 141 | config.style.fontSize = config.textSize + 'px'; 142 | if (key == 'fontSize') 143 | config.style.fontSize = config.fontSize + 'px'; 144 | 145 | if (key == 'url') 146 | config.attributes.src = config.url 147 | 148 | /*if (key == "imageUrl"){ 149 | let imageUrl = config.imageUrl; 150 | 151 | if(config.rawData) { 152 | // Do nothing 153 | } else { 154 | let temp = imageUrl.split('.'); 155 | let ext = ''; 156 | if(temp && temp.length > 0) 157 | ext = temp[temp.length - 1]; 158 | 159 | let exts = ["jpeg", "jpg", "png", "bmp", "svg", "gif"]; 160 | ext = ext.toLowerCase(); 161 | 162 | if(!exts.includes(ext)) { 163 | imageUrl += '.png'; 164 | } 165 | } 166 | 167 | config.attributes.src = imageUrl; 168 | }*/ 169 | 170 | if (key == "backgroundColor") { 171 | config.style.backgroundColor = parseColors(config.backgroundColor); 172 | } 173 | 174 | if (key == "background") { 175 | config.style.background = config.background; 176 | } 177 | if (key == "backgroundDrawable") { 178 | config.style["background-image"] = "url('"+config.backgroundDrawable+"')"; 179 | } 180 | 181 | if (key == "color") { 182 | config.style.color = parseColors(config.color); 183 | } 184 | 185 | if(key == "position") { 186 | config.style.position = config.position; 187 | } 188 | 189 | if(key == "bottomFixed") { 190 | config.style.bottom = config.bottomFixed; 191 | } 192 | if(key == "topFixed") { 193 | config.style.top = config.topFixed; 194 | } 195 | if(key == "leftFixed") { 196 | config.style.left = config.leftFixed; 197 | } 198 | if(key == "rightFixed") { 199 | config.style.right = config.rightFixed; 200 | } 201 | 202 | if(key == "zIndex") { 203 | config.style["z-index"] = config.zIndex; 204 | } 205 | 206 | if(key == "autofocus"){ 207 | var isIPhone = (navigator.userAgent.indexOf("iPhone") !== -1) 208 | if(config.autofocus && !isIPhone){ 209 | config.attributes["autofocus"] = "autofocus"; 210 | } 211 | } 212 | if(key=="focus") { 213 | if (config.focus && config.id){ 214 | var doc = document.getElementById(config.id); 215 | if (doc){ 216 | doc.focus(); 217 | } 218 | 219 | } 220 | } 221 | 222 | if (key == "cornerRadius") { 223 | config.style.borderRadius = config.cornerRadius + "px"; 224 | } 225 | 226 | if (key == "cornerRadii") { 227 | const [radius, ...corners] = config.cornerRadii.split(','); 228 | config.style.borderRadius = corners.map(bool => `${bool * radius}px`).join(' ') + ";"; 229 | } 230 | 231 | if (key == "alpha") { 232 | config.style.opacity = config[key]; 233 | } 234 | 235 | if (key == "a_alpha") { 236 | config.animation.opacity = config[key]; 237 | } 238 | 239 | if (key == "fontFamily") { 240 | config.style.fontFamily = config.fontFamily; 241 | } 242 | 243 | if (key == 'typeface') { 244 | switch(config.typeface){ 245 | case 'normal': 246 | config.style.fontWeight = 400; 247 | break; 248 | case 'bold': 249 | config.style.fontWeight = 700; 250 | break; 251 | case 'italic': 252 | config.style.fontStyle = 'italic'; 253 | break; 254 | case 'bold_italic': 255 | config.style.fontWeight = 700; 256 | config.style.fontStyle = 'italic'; 257 | break; 258 | case 'underline': 259 | config.style.textDecoration = 'underline'; 260 | break; 261 | } 262 | } 263 | 264 | if (key == "fontStyle") { 265 | let match = config.fontStyle.match(/[/-]/); 266 | let fontName = match ? config.fontStyle.slice(0, match.index) : config.fontStyle; 267 | config.style.fontFamily = fontName; 268 | 269 | if (!match) 270 | return; 271 | 272 | let type = config.fontStyle.slice(match.index + 1); 273 | type = type.replace(/[-/]/g, ''); 274 | type = type.toLowerCase(); 275 | 276 | if (type.indexOf('italic') != -1) 277 | config.style.fontStyle = 'italic'; 278 | 279 | let fontWeight = 0; 280 | 281 | if (type.indexOf('extralight') != -1) 282 | fontWeight = 200; 283 | else if (type.indexOf('light') != -1) 284 | fontWeight = 300; 285 | else if (type.indexOf('regular') != -1 || type.indexOf('book') != -1) 286 | fontWeight = 400; 287 | else if (type.indexOf('semibold') != -1 || type.indexOf('medium') != -1) 288 | fontWeight = 500; 289 | else if (type.indexOf('bold') != -1 || type.indexOf('heavy') != -1) 290 | fontWeight = 700; 291 | else if (type.indexOf('black') != -1) 292 | fontWeight = 900; 293 | 294 | if(fontWeight > 0) 295 | config.style.fontWeight = fontWeight; 296 | } 297 | 298 | if (key == 'visibility') { 299 | let visibility = config.visibility; 300 | if (visibility == 'invisible') 301 | config.style.visibility = "hidden"; 302 | else if (visibility == 'gone') 303 | config.style.display = "none"; 304 | else { 305 | config.style.visibility = ''; 306 | } 307 | } 308 | 309 | if (key == 'expand') { 310 | let visibility = config.expand + ''; 311 | if (visibility == 'true'){ 312 | config.style.visibility = ''; 313 | } 314 | else 315 | config.style.display = "none"; 316 | } 317 | 318 | if (key == "stroke") { 319 | let values = config.stroke.split(","); 320 | 321 | if(values.length == 2) 322 | config.style.border = values[0] + "px solid " + values[1]; 323 | else if(values.length == 3){ 324 | 325 | strokeMap = {"b": "borderBottom", "t" : "borderTop", "r" : "borderRight", "l" : "borderLeft"}; 326 | 327 | for (var i of values[2]){ 328 | if (strokeMap[i] !== undefined) 329 | config.style[strokeMap[i]] = values[0] + "px solid " + values[1]; 330 | } 331 | 332 | } 333 | 334 | } 335 | 336 | if (key == "strokeTop") { 337 | let values = config.strokeTop.split(","); 338 | 339 | if(values.length == 2) 340 | config.style.borderTop = values[0] + "px solid " + values[1]; 341 | else if(values.length == 3) 342 | config.style.borderTop = values[0] + "px " + values[1] + " " + values[2]; 343 | } 344 | 345 | if (key == "strokeRight") { 346 | let values = config.strokeRight.split(","); 347 | 348 | if(values.length == 2) 349 | config.style.borderRight = values[0] + "px solid " + values[1]; 350 | else if(values.length == 3) 351 | config.style.borderRight = values[0] + "px " + values[1] + " " + values[2]; 352 | } 353 | 354 | if (key == "strokeBottom") { 355 | let values = config.strokeBottom.split(","); 356 | 357 | if(values.length == 2) 358 | config.style.borderBottom = values[0] + "px solid " + values[1]; 359 | else if(values.length == 3) 360 | config.style.borderBottom = values[0] + "px " + values[1] + " " + values[2]; 361 | } 362 | 363 | if (key == "strokeLeft") { 364 | let values = config.strokeLeft.split(","); 365 | 366 | if(values.length == 2) 367 | config.style.borderLeft = values[0] + "px solid " + values[1]; 368 | else if(values.length == 3) 369 | config.style.borderLeft = values[0] + "px " + values[1] + " " + values[2]; 370 | } 371 | 372 | if (key == "translationY") { 373 | config.style.transform = lookAndReplaceProp(config.style.transform,"translateY",config[key]); 374 | } 375 | 376 | if (key == "a_translationY") { 377 | config.animation.transform += "translateY(" + config[key] + "px) "; 378 | } 379 | 380 | if (key == "translationX") { 381 | config.style.transform = lookAndReplaceProp(config.style.transform,"translateX",config[key]); 382 | } 383 | 384 | if (key == "a_translationX") { 385 | config.animation.transform += "translateX(" + config[key] + "px) "; 386 | } 387 | 388 | if (key == "scaleX") { 389 | config.style.transform += "scaleX(" + config[key] + ") "; 390 | } 391 | 392 | if (key == "a_scaleX") { 393 | config.animation.transform += "scaleX(" + config[key] + ") "; 394 | } 395 | 396 | if (key == "scaleY") { 397 | config.style.transform += "scaleY(" + config[key] + ") "; 398 | } 399 | 400 | if (key == "a_scaleY") { 401 | config.animation.transform += "scaleY(" + config[key] + ") "; 402 | } 403 | 404 | if (key == "rotation") { 405 | config.style.transform += "rotate(" + config[key] + "deg) "; 406 | } 407 | 408 | if (key == "a_rotation") { 409 | config.animation.transform += "rotate(" + config[key] + "deg) "; 410 | } 411 | 412 | if (key == "rotationX") { 413 | config.style.transform += "rotateX(" + config[key] + "deg) "; 414 | } 415 | 416 | if (key == "a_rotationX") { 417 | config.animation.transform += "rotateX(" + config[key] + "deg) "; 418 | } 419 | 420 | if (key == "rotationY") { 421 | config.style.transform += "rotateY(" + config[key] + "deg) "; 422 | } 423 | 424 | if (key == "a_rotationY") { 425 | config.animation.transform += "rotateY(" + config[key] + "deg) "; 426 | } 427 | 428 | if (key == "translationZ") { 429 | var v = config[key]; 430 | config.style["-webkit-box-shadow"] = "0 0 "+v.toString()+"px rgba(0,0,0, .1)"; 431 | config.style["-moz-box-shadow"]= "0 0 "+v.toString()+"px rgba(0,0,0, .1)"; 432 | config.style["box-shadow"]= "0 0 "+v.toString()+"px rgba(0,0,0, .1)"; 433 | 434 | } 435 | 436 | if (key == "a_duration" && !isNaN(config[key])) { 437 | const suffix = config.transitionTimingFunction ? (" " + config.transitionTimingFunction) : ""; 438 | config.animation.transition = config[key] + 'ms all' + suffix; 439 | } 440 | 441 | if (type == "textView" && key == "gravity" && config.gravity) { 442 | config.style.textAlign = config.gravity; 443 | if (config.gravity == "center_vertical") { 444 | config.style["align-items"] = "center"; 445 | config.style.display = "flex"; 446 | } else if (config.gravity == "center_horizontal") { 447 | config.style.display = "flex"; 448 | config.style["justify-content"] = "center"; 449 | } else if (config.gravity == "center") { 450 | config.style.display = "flex"; 451 | config.style["align-items"] = "center"; 452 | config.style["justify-content"] = "center"; 453 | } 454 | else if (config.gravity == "center"){ 455 | config.style.display = "flex"; 456 | config.style["align-items"] = "center"; 457 | config.style["justify-content"] = "center"; 458 | } 459 | } 460 | if (type == "linearLayout" && key == "gravity" && config.gravity){ 461 | if (config.margin && config.gravity=="center_vertical"){ 462 | var margin=config.margin.split(","); 463 | if (config.width == "match_parent"){ 464 | config.style.width="calc(100% - "+(parseInt(margin[0])+parseInt(margin[2])).toString()+"px )"; 465 | } 466 | } 467 | } 468 | 469 | if (key == "hint"){ 470 | config.attributes.placeholder = config.hint; 471 | } 472 | 473 | if (key == "contentEditable" && config.contentEditable){ 474 | config.attributes.contenteditable = true; 475 | } 476 | 477 | if (key == "id") { 478 | config.attributes.id = config.id; 479 | } 480 | 481 | 482 | if (key == "gradient") { 483 | var gradientObj =JSON.parse(config.gradient); 484 | if (gradientObj.type == "linear") { 485 | var angle = gradientObj.angle; 486 | var values = gradientObj.values; 487 | var colors = values.join(", "); 488 | config.style["background-image"] = "linear-gradient("+angle+"deg, "+colors+")" 489 | } 490 | else { 491 | var values = gradientObj.values; 492 | var colors = values.join(", "); 493 | config.style["background-image"] = "radial-gradient("+colors+")" 494 | } 495 | } 496 | 497 | 498 | if (key == "inputType" && window.__isMOBILE && !window.__isFIREFOX) { 499 | var inputType = "text"; 500 | if (config.inputType == "numericPassword" || config.inputType == "password") { 501 | if(config.inputTypeI == 4 && isMobile || navigator.userAgent.search("Firefox") === -1 ){ 502 | inputType = "tel"; 503 | config.style["-webkit-text-security"] = "disc"; 504 | config.style["-moz-text-security"] = "disc"; 505 | config.style["text-security"] = "disc"; 506 | } else { 507 | inputType = "password" 508 | config.attributes.autocomplete = "new-password" 509 | } 510 | } else if (config.inputType == "disabled") { 511 | config.attributes.disabled = 'disabled' 512 | } else if (config.inputType == "numeric") { 513 | inputType = "number" 514 | } 515 | if (config.separator) { 516 | if(config.inputType == "numeric"){ 517 | inputType = "tel" 518 | } else { 519 | inputType = "text" 520 | } 521 | } 522 | 523 | config.attributes.type = inputType 524 | } 525 | 526 | if (key == "rotateImage") { 527 | if(config.rotateImage){ 528 | config.style["-webkit-animation"] = "load3 4s infinite linear"; 529 | config.style["animation-duration"] = "4s"; 530 | config.style["animation-timing-function"] = "linear"; 531 | config.style["animation-delay"] = "0s"; 532 | config.style["animation-iteration-count"] = "infinite"; 533 | config.style["animation-direction"] = "normal"; 534 | config.style["animation-fill-mode"] = "none"; 535 | config.style["animation-play-state"] = "running"; 536 | config.style["animation-name"] = "rotation"; 537 | 538 | } 539 | } 540 | 541 | if (key == "pattern") { 542 | config.attributes["data-pattern"] = config.pattern; 543 | var data = config.pattern.split(','); 544 | var length = parseInt(data.pop()); 545 | if(length>10) {length = 10;} 546 | config.attributes["size"] =length; 547 | 548 | } 549 | // if(key == "inputTypeI"){ 550 | // if(config.inputTypeI == 4){ 551 | // config.attributes["inputmode"] = "numeric"; 552 | // } 553 | 554 | // } 555 | 556 | if (key == "separator") { 557 | config.attributes["separator"] = config.separator; 558 | } 559 | if (key == "separatorRepeat") { 560 | config.attributes["separatorRepeat"] = config.separatorRepeat; 561 | } 562 | 563 | if (key == "myAttr") { 564 | config.attributes["myAttr"] = config.myAttr; 565 | } 566 | if (key == "blurBackground" && config.blurBackground){ 567 | config.style["backdrop-filter"] = "blur(3px)"; 568 | } 569 | if (key == "shadow") { 570 | var shadowValues = config.shadow.split(config.shadowSeparator || ','); 571 | var shadowBlur = rWS(cS(shadowValues[2])); 572 | var shadowSpread = rWS(cS(shadowValues[3])); 573 | var shadowOpacity = rWS(cS(shadowValues[5])); 574 | var shadowOffset = { 575 | x: rWS(cS(shadowValues[0])), 576 | y: rWS(cS(shadowValues[1])) 577 | }; 578 | 579 | var shadowColor = convertColorToRgba(shadowValues[4]); 580 | var shadowType = config.shadowType ? `${config.shadowType} ` : '' 581 | 582 | config["style"]["box-shadow"] = shadowType + parseInt(shadowOffset.x) + "px " + parseInt(shadowOffset.y) + "px " + parseInt(shadowBlur) + "px " + parseInt(shadowSpread) + "px rgba(" + shadowColor.r + ", " + shadowColor.g + ", " + shadowColor.b + ", " + shadowColor.a + ")" ; 583 | } 584 | 585 | if (key == "lineHeight") 586 | config.style.lineHeight = config.lineHeight; 587 | 588 | if (key == "objectFit") 589 | config.style.objectFit = config.objectFit; 590 | 591 | if (key == "clickable") { 592 | config.style.pointerEvents = config.clickable ? "auto" : "none"; 593 | } 594 | if (key == "display") { 595 | config.style.display = config.display 596 | } 597 | } 598 | 599 | function convertColorToRgba(color) { 600 | color = rWS(cS(color)); 601 | 602 | var values; 603 | var alpha = "1.00"; 604 | 605 | if (color.length >= 8) { 606 | alpha = parseInt(color.substring(1, 3), 16); 607 | alpha = (alpha / 255).toFixed(2); 608 | color = color.substring(3, 9); 609 | } else { 610 | color = color.substring(1, color.length); 611 | } 612 | 613 | color = convertHexToRgb(rWS(color)); 614 | values = color.split(','); 615 | 616 | return { 617 | r: parseInt(rWS(values[0])), 618 | g: parseInt(rWS(values[1])), 619 | b: parseInt(rWS(values[2])), 620 | a: parseFloat(alpha) 621 | }; 622 | } 623 | 624 | function convertHexToRgb(hex) { 625 | var r = parseInt(hex.substring(0, 2), 16); 626 | var g = parseInt(hex.substring(2, 4), 16); 627 | var b = parseInt(hex.substring(4, 6), 16); 628 | 629 | return r + "," + g + "," + b; 630 | } 631 | 632 | function cS(value) { 633 | return value ? value + '' : ""; 634 | } 635 | 636 | function rWS(value) { 637 | return value.replace(/ /g, ''); 638 | } 639 | 640 | function setDefaults(type, config) { 641 | if (type == "linearLayout") { 642 | config.orientation = config.orientation; 643 | } 644 | } 645 | function this_inlineAnimation(config) { 646 | var con=modifyTranslation(config); 647 | var element = document.getElementById(con.name); 648 | } 649 | function modifyTranslation(config){ 650 | var x = config.x || "0"; 651 | var y = config.y || "0"; 652 | var animationArray = JSON.parse(config.inlineAnimation); 653 | var animationArray = animationArray.map(function(a){ 654 | if(a.hasOwnProperty("fromX")){ 655 | a.fromX = parseInt(a.fromX) + parseInt(x) + ''; 656 | if(!a.hasOwnProperty("toX")){ 657 | a.toX = 0 + parseInt(x) + ''; 658 | } 659 | } 660 | if(a.hasOwnProperty("toX")){ 661 | a.toX = parseInt(a.toX) + parseInt(x) + ''; 662 | } 663 | if(a.hasOwnProperty("fromY")){ 664 | a.fromY = parseInt(a.fromY) + parseInt(y) + ''; 665 | if(!a.hasOwnProperty("toY")){ 666 | a.toY = 0 + parseInt(y) + ''; 667 | } 668 | } 669 | if(a.hasOwnProperty("toY")){ 670 | a.toY = parseInt(a.toY) + parseInt(y) + ''; 671 | } 672 | return a; 673 | }) 674 | return (animationArray); 675 | } 676 | module.exports = function (type, config, getSetType) { 677 | // var ok = flattenObject(c); 678 | // console.log("object after flatten",ok); 679 | // var config = ok 680 | setDefaults(type, config); 681 | 682 | // console.log("object after flatten",config); 683 | 684 | 685 | var keys = Object.keys(config); 686 | 687 | for (var i = 0; i < keys.length; i++) { 688 | if ((config.style && config.style.display === 'none') && keys[i] === 'gravity') { 689 | continue; 690 | } 691 | parseLayoutProps(type, config, keys[i]); 692 | } 693 | 694 | 695 | config.transition = [ 696 | String(config.a_duration || 0) +"ms", 697 | "all", 698 | config.transitionTimingFunction 699 | ] 700 | .filter(Boolean) 701 | .join(" "); 702 | 703 | if (config.style.transform == "") { 704 | delete config.style.transform; 705 | } 706 | if (config.hasOwnProperty("inlineAnimation")) { 707 | this_inlineAnimation(config); 708 | } 709 | 710 | return config; 711 | } --------------------------------------------------------------------------------