├── .dockerignore ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── _config.yml ├── app └── qmljsify │ ├── Qmljsify │ ├── WrapperCreator.qml │ ├── templates │ │ ├── .babelrc │ │ ├── index.js │ │ ├── package-lock.json │ │ ├── package.json │ │ └── webpack.config.js │ └── wrappercreator.js │ ├── deployment.pri │ ├── main.cpp │ ├── qmljsify.cpp │ ├── qmljsify.h │ ├── qmljsify.pri │ ├── qmljsify.pro │ ├── qmljsify.qrc │ └── qpm.json ├── docker ├── Dockerfile ├── build-docker-image.sh ├── qmljsify.alias └── wrapper.sh └── tests └── qmljsifyunittests ├── main.cpp ├── qmljsifyunittests.pro ├── qmltests └── tst_QmlTests.qml ├── qpm.json ├── samples ├── lodash.merge.js ├── lodash.merge.orig.js ├── sprintf-0.1.5.min.js └── sprintf.js ├── tests.cpp └── tests.h /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used to ignore files which are generated 2 | # ---------------------------------------------------------------------------- 3 | 4 | *~ 5 | *.autosave 6 | *.a 7 | *.core 8 | *.moc 9 | *.o 10 | *.obj 11 | *.orig 12 | *.rej 13 | *.so 14 | *.so.* 15 | *_pch.h.cpp 16 | *_resource.rc 17 | *.qm 18 | .#* 19 | *.*# 20 | core 21 | !core/ 22 | tags 23 | .DS_Store 24 | .directory 25 | *.debug 26 | Makefile* 27 | *.prl 28 | *.app 29 | moc_*.cpp 30 | ui_*.h 31 | qrc_*.cpp 32 | Thumbs.db 33 | *.res 34 | *.rc 35 | /.qmake.cache 36 | /.qmake.stash 37 | 38 | # qtcreator generated files 39 | *.pro.user* 40 | 41 | # xemacs temporary files 42 | *.flc 43 | 44 | # Vim temporary files 45 | .*.swp 46 | 47 | # Visual Studio generated files 48 | *.ib_pdb_index 49 | *.idb 50 | *.ilk 51 | *.pdb 52 | *.sln 53 | *.suo 54 | *.vcproj 55 | *vcproj.*.*.user 56 | *.ncb 57 | *.sdf 58 | *.opensdf 59 | *.vcxproj 60 | *vcxproj.* 61 | 62 | # MinGW generated files 63 | *.Debug 64 | *.Release 65 | 66 | # Python byte code 67 | *.pyc 68 | 69 | # Binaries 70 | # -------- 71 | *.dll 72 | *.exe 73 | 74 | vendor 75 | 76 | 77 | # Android 78 | # ------- 79 | *.idea 80 | *.build 81 | .gradle 82 | local.properties 83 | gradle.properties 84 | !gradle-wrapper.jar 85 | *.iml 86 | 87 | # Qt 88 | # --- 89 | qmlimport.path 90 | *.qmlc 91 | *.jsc 92 | 93 | !build-docker-images.sh 94 | **/build-* 95 | node_modules 96 | 97 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language : cpp 2 | dist: trusty 3 | env: 4 | - DISPLAY=:99.0 5 | compiler: 6 | - gcc 7 | before_install: 8 | - export GOPATH=`pwd`/gosrc 9 | - export PATH=`pwd`/gosrc/bin:$PATH 10 | - go get qpm.io/qpm 11 | - sh -e /etc/init.d/xvfb start 12 | - npm install -g webpack@3.10.0 13 | script: 14 | - git clone https://github.com/benlau/qtci.git 15 | - source qtci/path.env 16 | - qt-5.7 17 | - source qt-5.7.env 18 | - mkdir build 19 | - cd build 20 | - run-unittests ../tests/qmljsifyunittests/qmljsifyunittests.pro 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qmljsify 2 | Convert an NPM package into a QML friendly JavaScript file 3 | 4 | It is still a prototype software. Use it at your own risk. 5 | 6 | **Proven Working Packages** 7 | 8 | fecha (Lightweight version of moment) 9 | lodash(--no-minify) 10 | sprintf 11 | 12 | Installation Instruction 13 | ================= 14 | 15 | A pre-built version of qmljsify is available in Docker Hub. You may pull the docker image directly: 16 | 17 | ``` 18 | docker pull benlau/qmljsify 19 | docker tag benlau/qmljsify qmljsify 20 | alias qmljsify='docker run --rm -v $PWD:/data --user $(id -u):$(id -g) -i -t qmljsify' 21 | ``` 22 | 23 | Usage 24 | ===== 25 | 26 | ``` 27 | Usage: qmljsify [options] command package 28 | qmljsify - Download and convert an NPM package to a QML friendly JavaScript file 29 | 30 | Options: 31 | -h, --help Displays this help. 32 | --no-minify Turn off JavaScript Compression 33 | 34 | Arguments: 35 | command Command [Available Commands: convert] 36 | package NPM package to be qmljsified 37 | ``` 38 | 39 | 40 | Example: 41 | 42 | ``` 43 | qmljsify convert sprintf 44 | ``` 45 | 46 | Then it will fetch `sprintf` from NPM and create two files 47 | 48 | ``` 49 | sprintf.orig.js # A compiled and minified sprintf library 50 | sprintf.js # A Wrapper of the compiled sprintf library for QML 51 | ``` 52 | 53 | That is what `sprintf.js` looks like: 54 | 55 | ``` 56 | .pragma library 57 | Qt.include("sprintf.orig.js") 58 | var object_stringify = QML.object_stringify; 59 | var format = QML.format; 60 | var cache = QML.cache; 61 | var parse = QML.parse; 62 | var sprintf = QML.sprintf; 63 | var vsprintf = QML.vsprintf; 64 | ``` 65 | 66 | Then you could use it in your QML file: 67 | 68 | ``` 69 | import "./sprintf.js" as SPrintf 70 | 71 | // [snipped] 72 | SPrintf.sprintf("%d %d", 1 , 2); 73 | ``` 74 | 75 | Known Issues 76 | ============ 77 | 78 | 1. It doesn't work on Windows. 79 | 80 | 2. setTimeout is not wrapped. 81 | 82 | 3. It may not works for some npm package. 83 | 84 | Remarks: NPM library with only a single function is supported now (e.g. left-pad) 85 | 86 | Build Instruction 87 | ================= 88 | 89 | Prerequisite: 90 | 91 | - webpack < 4.0 92 | - npm 93 | - nodejs 94 | 95 | ``` 96 | cd app/qmljsify 97 | qpm install 98 | qmake 99 | make 100 | #Then copy qmljsify to your favor path 101 | ``` 102 | 103 | p.s Windows is not working yet. 104 | 105 | Build Instruction (Docker) 106 | ========================== 107 | 108 | 109 | ``` 110 | cd qmljsify/docker 111 | ./build-docker-images.sh 112 | alias qmljsify='docker run --rm -v $PWD:/data --user $(id -u):$(id -g) -i -t qmljsify' 113 | ``` 114 | 115 | 116 | TroubleShooting 117 | =============== 118 | 119 | 1) qml: SyntaxError: Expected token `;' 120 | 121 | ``` 122 | $ qmljsify convert lodash 123 | qml: SyntaxError: Expected token `;' 124 | ``` 125 | Try --no-minify 126 | 127 | ``` 128 | $ qmljsify convert --no-minify lodash 129 | lodash.orig.js saved 130 | lodash.js saved 131 | ``` 132 | 133 | 2) SyntaxError: Expected token `identifier` 134 | 135 | The Javascript loaded by `Qt.include` could not use "as" as a variable name. The minified Javascript may contain such kind of variable so it will raise the exception. You may try to use the `--no-minify` argument to create a non-minified QML friendly Javascript. 136 | 137 | Brainstorming 138 | ------------ 139 | 140 | Proposed features: 141 | 142 | 1. --combine - Merge orig js and wrapper js into a single file. (It could be a solution of the bug of Qt.include) 143 | 144 | 2. Handle library with only a single function 145 | 146 | 3. --function name - Set the function name of the library that only provides a single function 147 | 148 | 4. Break down the "convert" function into multiple steps. 149 | 150 | 5. --retry-minify - Try to minify for few more times unless it doesn't use any variable name with `as' 151 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /app/qmljsify/Qmljsify/WrapperCreator.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import "./wrappercreator.js" as Script 3 | 4 | QtObject { 5 | objectName: "Main" 6 | 7 | function validate(source) { 8 | return Script.validate(source); 9 | } 10 | 11 | function create(source, fileName) { 12 | return Script.create(source, fileName); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/qmljsify/Qmljsify/templates/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/react"] 3 | } 4 | -------------------------------------------------------------------------------- /app/qmljsify/Qmljsify/templates/index.js: -------------------------------------------------------------------------------- 1 | var pkg = require('%{PACKAGE}'); 2 | 3 | if (typeof pkg === "function") { 4 | module.exports = { 5 | %{FUNCTION} : pkg 6 | }; 7 | } else { 8 | module.exports = pkg; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /app/qmljsify/Qmljsify/templates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "qmljsify", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "@babel/core": "7.1.6", 8 | "@babel/preset-env": "7.1.6", 9 | "@babel/preset-react": "7.0.0", 10 | "babel-loader": "8.0.4", 11 | "babel-preset-es2015": "6.24.1", 12 | "webpack": "2.5.1", 13 | "webpack-cli": "3.3.9" 14 | }, 15 | "devDependencies": {}, 16 | "scripts": { 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "author": "", 20 | "license": "ISC" 21 | } 22 | -------------------------------------------------------------------------------- /app/qmljsify/Qmljsify/templates/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var argv = require("minimist")(process.argv.slice(2)); 3 | 4 | var config = { 5 | entry: [ 6 | "./index.js" 7 | ], 8 | devtool: false, 9 | resolve: { 10 | extensions: [".js", ".jsx"] 11 | }, 12 | output: { 13 | path: __dirname + "/dist", 14 | publicPath: "/", 15 | filename: "bundle.js", 16 | library: "QML", 17 | libraryTarget: 'umd', 18 | umdNamedDefine: true 19 | }, 20 | devServer: { 21 | contentBase: __dirname + "/build", 22 | hot: true 23 | }, 24 | node: { 25 | dns: "mock", 26 | net: "mock" 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | use: { 32 | loader:'babel-loader' 33 | }, 34 | test: /\.js$/ 35 | } 36 | ] 37 | }, 38 | plugins: [] 39 | } 40 | 41 | module.exports = function(env) { 42 | 43 | const minify = env === "minify"; 44 | 45 | if (minify) { 46 | config.plugins.push( 47 | new webpack.optimize.UglifyJsPlugin({ 48 | compress: { 49 | warnings: false 50 | } 51 | }) 52 | ); 53 | } 54 | 55 | return config; 56 | } 57 | -------------------------------------------------------------------------------- /app/qmljsify/Qmljsify/wrappercreator.js: -------------------------------------------------------------------------------- 1 | .pragma library 2 | 3 | var thiz = this; 4 | 5 | this.setTimeout = function(){ 6 | } 7 | 8 | function validate(source) { 9 | var status = Qt.include(source); 10 | 11 | if (status.status !== 0) { 12 | console.warn("Failed to load: " + source); 13 | if (status.hasOwnProperty("exception")) { 14 | console.warn(status.exception.lineNumber + ":" + status.exception); 15 | } 16 | return false; 17 | } 18 | 19 | return true; 20 | } 21 | 22 | function create(source, fileName) { 23 | var status = Qt.include(source); 24 | 25 | if (status.status !== 0) { 26 | console.warn("Failed to load: " + source); 27 | if (status.hasOwnProperty("exception")) { 28 | console.warn(status.exception.lineNumber + ":" + status.exception); 29 | } 30 | 31 | return; 32 | } 33 | 34 | var res = ".pragma library\n"; 35 | res += "Qt.include(\"" + fileName + "\")\n"; 36 | for (var i in thiz) { 37 | for (var j in thiz[i]) { 38 | res += "var " + j + " = " + i +"." + j + ";\n"; 39 | } 40 | } 41 | 42 | return res; 43 | } 44 | -------------------------------------------------------------------------------- /app/qmljsify/deployment.pri: -------------------------------------------------------------------------------- 1 | unix:!android { 2 | isEmpty(target.path) { 3 | qnx { 4 | target.path = /tmp/$${TARGET}/bin 5 | } else { 6 | target.path = /opt/$${TARGET}/bin 7 | } 8 | export(target.path) 9 | } 10 | INSTALLS += target 11 | } 12 | 13 | export(INSTALLS) 14 | 15 | mac { 16 | # Quick and dirty way to create 17 | APP=$${OUT_PWD}/$${TARGET}.app 18 | DMG_FOLDER=$${OUT_PWD}/dmg 19 | 20 | dmg.target = dmg 21 | dmg.commands = macdeployqt $${APP}; mkdir -p $${DMG_FOLDER}; cp -a $${APP} $${DMG_FOLDER}; cd $${OUT_PWD}; hdiutil create -srcfolder dmg -volname uforobot -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDZO -imagekey zlib-level=9 $${TARGET}.dmg 22 | dmg.depends = $${APP} 23 | 24 | QMAKE_EXTRA_TARGETS += dmg 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/qmljsify/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "qmljsify.h" 7 | 8 | using namespace QtShell; 9 | 10 | void create(Qmljsify& qmljsify) { 11 | qmljsify.create(); 12 | } 13 | 14 | void convert(Qmljsify& qmljsify) { 15 | qmljsify.prepare(); 16 | qmljsify.fetch(); 17 | qmljsify.build(); 18 | qmljsify.create(); 19 | } 20 | 21 | int main(int argc, char *argv[]) 22 | { 23 | QCoreApplication app(argc, argv); 24 | QCoreApplication::setApplicationName("qmljsify"); 25 | QCoreApplication::setApplicationVersion("0.1"); 26 | 27 | QCommandLineParser parser; 28 | 29 | parser.setApplicationDescription("qmljsify - Download and convert a NPM package to a QML friendly JavaScript file"); 30 | parser.addHelpOption(); 31 | parser.addPositionalArgument("command", "Command [Available Commands: convert]"); 32 | parser.addPositionalArgument("package", "NPM package to be qmljsified"); 33 | 34 | QCommandLineOption noMinifyOption(QStringList() << "no-minify", "Turn off JavaScript Compression"); 35 | parser.addOption(noMinifyOption); 36 | 37 | parser.parse(app.arguments()); 38 | 39 | QStringList args = parser.positionalArguments(); 40 | 41 | if (args.size() != 2) { 42 | parser.showHelp(0); 43 | } 44 | 45 | bool noMinify = parser.isSet(noMinifyOption); 46 | 47 | QString command = args[0]; 48 | QString package = args[1]; 49 | 50 | Qmljsify qmljsify; 51 | qmljsify.setBuildFolder(realpath_strip(pwd(), "build")); 52 | qmljsify.parsePackageString(package); 53 | qmljsify.setOutputFolder(pwd()); 54 | qmljsify.setMinifyEnabled(!noMinify); 55 | 56 | if (command == "convert") { 57 | convert(qmljsify); 58 | } else if (command == "create") { 59 | create(qmljsify); 60 | } else { 61 | qDebug() << "Unknown commmand:" << command; 62 | parser.showHelp(-1); 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /app/qmljsify/qmljsify.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "qmljsify.h" 9 | 10 | using namespace QtShell; 11 | 12 | #define TEMPLATES ":/Qmljsify/templates" 13 | 14 | static void writeToFile(QString file, QString content) { 15 | rm("-f", file); 16 | 17 | QFile output(file); 18 | output.open(QIODevice::ReadWrite); 19 | output.write(content.toLocal8Bit()); 20 | output.close(); 21 | } 22 | 23 | static QString which(QString program) { 24 | //@TODO : Fix it. It doesn't work on windows 25 | QProcess process; 26 | QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); 27 | 28 | process.setProcessEnvironment(env); 29 | 30 | process.start("/usr/bin/which", QStringList() << program); 31 | 32 | process.waitForFinished(); 33 | 34 | QString content = process.readAll(); 35 | 36 | if (process.exitStatus() == QProcess::CrashExit) { 37 | qDebug() << "which" << program << process.errorString() ; 38 | } 39 | 40 | return content.replace("\n", ""); 41 | } 42 | 43 | static void execute(QString cwd, QString command , QStringList arguments) { 44 | qDebug().noquote() << command << arguments.join(" "); 45 | QProcess process; 46 | 47 | QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); 48 | 49 | process.setProcessEnvironment(env); 50 | process.setWorkingDirectory(cwd); 51 | 52 | process.setProcessChannelMode(QProcess::MergedChannels); 53 | 54 | process.start(command , arguments); 55 | 56 | process.waitForFinished(-1); 57 | 58 | if (process.exitStatus() != QProcess::NormalExit || process.exitCode() != 0) { 59 | qDebug() << command << process.errorString(); 60 | } 61 | } 62 | 63 | static QVariantMap parse(const QString &text) { 64 | QJsonParseError error; 65 | QJsonDocument doc = QJsonDocument::fromJson(text.toUtf8(),&error); 66 | 67 | if (error.error != QJsonParseError::NoError) { 68 | qWarning() << "JSON::parse() error: "<< error.errorString(); 69 | } 70 | 71 | return doc.object().toVariantMap(); 72 | } 73 | 74 | 75 | static void prepend(QString fileName , QString content) { 76 | 77 | QString origContent = cat(fileName); 78 | 79 | QFile file(fileName); 80 | 81 | if (!file.open(QIODevice::WriteOnly)) { 82 | qWarning() << file.errorString(); 83 | return; 84 | } 85 | 86 | file.write(content.toUtf8()); 87 | file.write(origContent.toUtf8()); 88 | } 89 | 90 | Qmljsify::Qmljsify() 91 | { 92 | m_minifyEnabled = false; 93 | } 94 | 95 | QString Qmljsify::buildFolder() const 96 | { 97 | return m_buildFolder; 98 | } 99 | 100 | void Qmljsify::setBuildFolder(const QString &buildFolder) 101 | { 102 | m_buildFolder = buildFolder; 103 | } 104 | 105 | void Qmljsify::prepare() 106 | { 107 | QStringList files = QStringList{"index.js", "webpack.config.js", "package.json", ".babelrc", "package-lock.json"}; 108 | 109 | mkdir("-p", m_buildFolder); 110 | 111 | for (int i = 0 ; i < files.size() ; i++) { 112 | QString file = files[i]; 113 | QString inputPath = realpath_strip(TEMPLATES, file); 114 | QString outputPath = realpath_strip(m_buildFolder, file); 115 | QString content = cat(inputPath); 116 | 117 | content = content.replace("%{PACKAGE}", m_package); 118 | content = content.replace("%{FUNCTION}", normalizeFunctionName(m_package)); 119 | 120 | writeToFile(outputPath, content); 121 | } 122 | } 123 | 124 | void Qmljsify::fetch() 125 | { 126 | QString npm = which("npm"); 127 | 128 | QString package = m_package; 129 | if (!m_packageVersion.isEmpty()) { 130 | package = m_package + "@" + m_packageVersion; 131 | } 132 | 133 | execute(m_buildFolder, npm, QStringList() << "install" << "--no-progress" << "--save" << "--save-exact" << package); 134 | execute(m_buildFolder, npm, QStringList() << "install"); 135 | } 136 | 137 | void Qmljsify::build() 138 | { 139 | QString webpack = "./node_modules/.bin/webpack"; 140 | if (webpack.isEmpty()) { 141 | qWarning() << "webpack command not found. Please check your PATH environment variable."; 142 | } 143 | QStringList arguments; 144 | 145 | if (m_minifyEnabled) { 146 | arguments << "--env" << "minify"; 147 | } 148 | 149 | execute(m_buildFolder, webpack, arguments); 150 | } 151 | 152 | void Qmljsify::create() 153 | { 154 | QUrl url("qrc:/Qmljsify/WrapperCreator.qml"); 155 | 156 | QQmlApplicationEngine engine; 157 | engine.load(url); 158 | 159 | QObject* script = engine.rootObjects().first(); 160 | 161 | QString bundle = realpath_strip(m_buildFolder, "dist/bundle.js");; 162 | 163 | QString origJs = realpath_strip(m_outputFolder, m_package + ".orig.js"); 164 | QString js = realpath_strip(m_outputFolder, m_package + ".js"); 165 | 166 | cp(bundle, origJs); 167 | 168 | qDebug().noquote() << QtShell::basename(origJs) << "saved"; 169 | 170 | QVariant result; 171 | 172 | QMetaObject::invokeMethod(script, 173 | "create", 174 | Qt::DirectConnection, 175 | Q_RETURN_ARG(QVariant, result), 176 | Q_ARG(QVariant, QUrl::fromLocalFile(origJs).toString()), 177 | Q_ARG(QVariant, QtShell::basename(origJs))); 178 | 179 | QString output = realpath_strip(m_outputFolder, m_package + ".js"); 180 | writeToFile(output, result.toString()); 181 | 182 | qDebug().noquote() << QtShell::basename(output) << "saved"; 183 | 184 | QString version = queryPackageVersion(m_buildFolder, m_package); 185 | 186 | QString line = QString("/*%1@%2 - generated by Qmljsify.*/\n").arg(m_package).arg(version); 187 | 188 | prepend(js, line); 189 | prepend(origJs, line); 190 | } 191 | 192 | bool Qmljsify::minifyEnabled() const 193 | { 194 | return m_minifyEnabled; 195 | } 196 | 197 | void Qmljsify::setMinifyEnabled(bool minifyEnabled) 198 | { 199 | m_minifyEnabled = minifyEnabled; 200 | } 201 | 202 | QString Qmljsify::outputFolder() const 203 | { 204 | return m_outputFolder; 205 | } 206 | 207 | void Qmljsify::setOutputFolder(const QString &outputFolder) 208 | { 209 | m_outputFolder = outputFolder; 210 | } 211 | 212 | QString Qmljsify::normalizeFunctionName(const QString &package) 213 | { 214 | QStringList token = package.split(QRegExp("[.-]")); 215 | 216 | for (int i = 1 ; i < token.size();i++) { 217 | QString str = token[i]; 218 | str[0] = str[0].toUpper(); 219 | token[i] = str; 220 | } 221 | 222 | return token.join(""); 223 | } 224 | 225 | QString Qmljsify::packageVersion() const 226 | { 227 | return m_packageVersion; 228 | } 229 | 230 | void Qmljsify::setPackageVersion(const QString &packageVersion) 231 | { 232 | m_packageVersion = packageVersion; 233 | } 234 | 235 | void Qmljsify::parsePackageString(const QString &text) 236 | { 237 | QStringList token = text.split("@"); 238 | setPackage(token[0]); 239 | if (token.size() > 1) { 240 | setPackageVersion(token[1]); 241 | } 242 | } 243 | 244 | QString Qmljsify::queryPackageVersion(const QString &folder, const QString &packageName) 245 | { 246 | QString path = realpath_strip(folder, "node_modules", packageName, "package.json"); 247 | 248 | QString content = cat(path); 249 | 250 | QVariantMap map = parse(content); 251 | 252 | return map["version"].toString(); 253 | } 254 | 255 | bool Qmljsify::validate(const QString &file) 256 | { 257 | QUrl url("qrc:/Qmljsify/WrapperCreator.qml"); 258 | 259 | QQmlApplicationEngine engine; 260 | engine.load(url); 261 | 262 | QObject* script = engine.rootObjects().first(); 263 | 264 | QVariant result; 265 | 266 | QMetaObject::invokeMethod(script, 267 | "validate", 268 | Qt::DirectConnection, 269 | Q_RETURN_ARG(QVariant, result), 270 | Q_ARG(QVariant, QUrl::fromLocalFile(file).toString())); 271 | 272 | return result.toBool(); 273 | } 274 | 275 | QString Qmljsify::package() const 276 | { 277 | return m_package; 278 | } 279 | 280 | void Qmljsify::setPackage(const QString &package) 281 | { 282 | m_package = package; 283 | } 284 | -------------------------------------------------------------------------------- /app/qmljsify/qmljsify.h: -------------------------------------------------------------------------------- 1 | #ifndef QMLJSIFY_H 2 | #define QMLJSIFY_H 3 | 4 | #include 5 | 6 | class Qmljsify 7 | { 8 | public: 9 | Qmljsify(); 10 | 11 | QString buildFolder() const; 12 | 13 | void setBuildFolder(const QString &buildFolder); 14 | 15 | QString package() const; 16 | 17 | void setPackage(const QString &package); 18 | 19 | void prepare(); 20 | 21 | void fetch(); 22 | 23 | void build(); 24 | 25 | /// Create a wrapper of compiled JavaScript 26 | void create(); 27 | 28 | bool minifyEnabled() const; 29 | 30 | void setMinifyEnabled(bool minifyEnabled); 31 | 32 | QString createdJavaScriptFile() const; 33 | void setCreatedJavaScriptFile(const QString &createdJavaScriptFile); 34 | 35 | QString outputFolder() const; 36 | void setOutputFolder(const QString &outputFolder); 37 | 38 | static QString normalizeFunctionName(const QString& package); 39 | 40 | QString packageVersion() const; 41 | 42 | void setPackageVersion(const QString &packageVersion); 43 | 44 | void parsePackageString(const QString& text); 45 | 46 | static QString queryPackageVersion(const QString& folder, const QString& packageName); 47 | 48 | static bool validate(const QString& file); 49 | 50 | private: 51 | 52 | QString m_buildFolder; 53 | 54 | QString m_package; 55 | 56 | QString m_packageVersion; 57 | 58 | QString m_outputFolder; 59 | 60 | bool m_minifyEnabled; 61 | }; 62 | 63 | #endif // QMLJSIFY_H 64 | -------------------------------------------------------------------------------- /app/qmljsify/qmljsify.pri: -------------------------------------------------------------------------------- 1 | CONFIG += c++11 2 | 3 | SOURCES += \ 4 | $$PWD/qmljsify.cpp 5 | 6 | RESOURCES += $$PWD/qmljsify.qrc 7 | 8 | INCLUDEPATH += $$PWD 9 | 10 | # Additional import path used to resolve QML modules in Qt Creator's code model 11 | QML_IMPORT_PATH = $$PWD 12 | 13 | HEADERS += \ 14 | $$PWD/qmljsify.h 15 | 16 | DISTFILES += \ 17 | $$PWD/Qmljsify/templates/package.json 18 | -------------------------------------------------------------------------------- /app/qmljsify/qmljsify.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | 3 | QT += qml quick 4 | CONFIG += c++11 5 | CONFIG-=app_bundle 6 | CONFIG -= qtquickcompiler 7 | 8 | QT += quick qml 9 | 10 | include(qmljsify.pri) 11 | 12 | SOURCES += main.cpp 13 | 14 | TARGET = qmljsify 15 | 16 | ROOTDIR = $$PWD/../.. 17 | 18 | # Default rules for deployment. 19 | include(deployment.pri) 20 | include(vendor/vendor.pri) 21 | 22 | DISTFILES += qpm.json 23 | 24 | -------------------------------------------------------------------------------- /app/qmljsify/qmljsify.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Qmljsify/templates/webpack.config.js 4 | Qmljsify/templates/index.js 5 | Qmljsify/wrappercreator.js 6 | Qmljsify/WrapperCreator.qml 7 | Qmljsify/templates/package.json 8 | Qmljsify/templates/.babelrc 9 | Qmljsify/templates/package-lock.json 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/qmljsify/qpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | "com.github.benlau.qtshell@0.4.5" 4 | ] 5 | } -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10.15.3-alpine 2 | 3 | MAINTAINER Ben Lau version: 0.1 4 | 5 | VOLUME /data 6 | 7 | RUN apk add --update --no-cache bash \ 8 | alpine-sdk qt5-qtbase-dev qt5-qtbase-dev qt5-qtdeclarative qt5-qtquickcontrols2-dev go && \ 9 | rm -rf /var/cache/apk/* && \ 10 | npm install -g webpack@3.10.0 && \ 11 | go get qpm.io/qpm && cp /root/go/bin/qpm /usr/bin && rm -rf /root/go 12 | 13 | ADD app/qmljsify /src 14 | 15 | RUN export PATH=/usr/lib/qt5/bin:$PATH && cd /src && qpm install && qmake && make && cp qmljsify /usr/bin && make clean 16 | 17 | ADD docker/wrapper.sh /usr/bin/wrapper.sh 18 | RUN chmod a+x /usr/bin/wrapper.sh 19 | 20 | ENTRYPOINT ["/usr/bin/wrapper.sh"] 21 | -------------------------------------------------------------------------------- /docker/build-docker-image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker build -t qmljsify -f Dockerfile ../ 4 | -------------------------------------------------------------------------------- /docker/qmljsify.alias: -------------------------------------------------------------------------------- 1 | alias qmljsify='docker run --rm -v $PWD:/data --user $(id -u):$(id -g) -i -t qmljsify' 2 | -------------------------------------------------------------------------------- /docker/wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export XDG_RUNTIME_DIR=/tmp/xdg 4 | mkdir -p $XDG_RUNTIME_DIR 5 | export HOME=/tmp/home 6 | mkdir -p $HOME 7 | set -e 8 | cd /tmp 9 | qmljsify "$@" 10 | cp *.js /data 11 | -------------------------------------------------------------------------------- /tests/qmljsifyunittests/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "tests.h" 9 | 10 | void handleBacktrace(int sig) { 11 | void *array[100]; 12 | size_t size; 13 | 14 | // get void*'s for all entries on the stack 15 | size = backtrace(array, 100); 16 | 17 | // print out all the frames to stderr 18 | fprintf(stderr, "Error: signal %d:\n", sig); 19 | backtrace_symbols_fd(array, size, STDERR_FILENO); 20 | exit(1); 21 | } 22 | 23 | namespace AutoTestRegister { 24 | QUICK_TEST_MAIN(QuickTests) 25 | } 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | signal(SIGSEGV, handleBacktrace); 30 | 31 | QGuiApplication app(argc, argv); 32 | 33 | TestRunner runner; 34 | runner.addImportPath("qrc:///"); 35 | runner.add(); 36 | runner.add(QString(QUICK_TEST_SOURCE_DIR)); 37 | 38 | int waitTime = 100; 39 | if (app.arguments().size() != 1) { 40 | waitTime = 60000; 41 | } 42 | 43 | bool error = runner.exec(app.arguments()); 44 | 45 | if (!error) { 46 | qDebug() << "All test cases passed!"; 47 | } 48 | 49 | return error; 50 | } 51 | -------------------------------------------------------------------------------- /tests/qmljsifyunittests/qmljsifyunittests.pro: -------------------------------------------------------------------------------- 1 | QT += testlib qml 2 | 3 | TARGET = untitled 4 | CONFIG += console 5 | CONFIG -= app_bundle 6 | 7 | TEMPLATE = app 8 | 9 | SOURCES += main.cpp tests.cpp 10 | 11 | DEFINES += SRCDIR=\\\"$$PWD/\\\" 12 | ROOTDIR = $$PWD/../../ 13 | 14 | include(vendor/vendor.pri) 15 | include($$ROOTDIR/app/qmljsify/qmljsify.pri) 16 | 17 | DEFINES += QUICK_TEST_SOURCE_DIR=\\\"$$PWD/qmltests\\\" 18 | 19 | DISTFILES += qpm.json qmltests/tst_QmlTests.qml \ 20 | ../../.travis.yml \ 21 | ../../README.md \ 22 | samples/sprintf.js \ 23 | samples/sprintf.min.js \ 24 | samples/sprintf-0.1.5.min.js 25 | 26 | HEADERS += tests.h 27 | -------------------------------------------------------------------------------- /tests/qmljsifyunittests/qmltests/tst_QmlTests.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtTest 1.0 3 | import Testable 1.0 4 | import "../samples/sprintf.js" as SPrintf 5 | import "../samples/lodash.merge.js" as LodashMerge 6 | Item { 7 | id: window 8 | height: 640 9 | width: 480 10 | 11 | TestCase { 12 | name: "QmlTests" 13 | 14 | function test_sprintf() { 15 | // Test the sprintf generated by this program 16 | compare(SPrintf.sprintf("%d %d",1,2), "1 2"); 17 | } 18 | 19 | function test_lodashMerge() { 20 | var input = { 21 | value1: 1, 22 | value2: 2.0 23 | } 24 | 25 | var output = LodashMerge.lodashMerge({}, input); 26 | 27 | compare(output.value1, 1); 28 | compare(output.value2, 2.0); 29 | 30 | } 31 | 32 | 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/qmljsifyunittests/qpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "description": "", 4 | "dependencies": [ 5 | "com.github.benlau.testable@1.0.2.22", 6 | "com.github.benlau.qtshell@0.4.5", 7 | "net.efever.qmlshell@0.0.2" 8 | ], 9 | "license": "NONE", 10 | "pri_filename": "", 11 | "webpage": "" 12 | } -------------------------------------------------------------------------------- /tests/qmljsifyunittests/samples/lodash.merge.js: -------------------------------------------------------------------------------- 1 | /*lodash.merge@4.5.0 - generated by Qmljsify.*/ 2 | .pragma library 3 | Qt.include("lodash.merge.orig.js") 4 | var lodashMerge = QML.lodashMerge; 5 | -------------------------------------------------------------------------------- /tests/qmljsifyunittests/samples/lodash.merge.orig.js: -------------------------------------------------------------------------------- 1 | /*lodash.merge@4.5.0 - generated by Qmljsify.*/ 2 | (function webpackUniversalModuleDefinition(root, factory) { 3 | if(typeof exports === 'object' && typeof module === 'object') 4 | module.exports = factory(); 5 | else if(typeof define === 'function' && define.amd) 6 | define("QML", [], factory); 7 | else if(typeof exports === 'object') 8 | exports["QML"] = factory(); 9 | else 10 | root["QML"] = factory(); 11 | })(this, function() { 12 | return /******/ (function(modules) { // webpackBootstrap 13 | /******/ // The module cache 14 | /******/ var installedModules = {}; 15 | /******/ 16 | /******/ // The require function 17 | /******/ function __webpack_require__(moduleId) { 18 | /******/ 19 | /******/ // Check if module is in cache 20 | /******/ if(installedModules[moduleId]) { 21 | /******/ return installedModules[moduleId].exports; 22 | /******/ } 23 | /******/ // Create a new module (and put it into the cache) 24 | /******/ var module = installedModules[moduleId] = { 25 | /******/ i: moduleId, 26 | /******/ l: false, 27 | /******/ exports: {} 28 | /******/ }; 29 | /******/ 30 | /******/ // Execute the module function 31 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 32 | /******/ 33 | /******/ // Flag the module as loaded 34 | /******/ module.l = true; 35 | /******/ 36 | /******/ // Return the exports of the module 37 | /******/ return module.exports; 38 | /******/ } 39 | /******/ 40 | /******/ 41 | /******/ // expose the modules object (__webpack_modules__) 42 | /******/ __webpack_require__.m = modules; 43 | /******/ 44 | /******/ // expose the module cache 45 | /******/ __webpack_require__.c = installedModules; 46 | /******/ 47 | /******/ // identity function for calling harmony imports with the correct context 48 | /******/ __webpack_require__.i = function(value) { return value; }; 49 | /******/ 50 | /******/ // define getter function for harmony exports 51 | /******/ __webpack_require__.d = function(exports, name, getter) { 52 | /******/ if(!__webpack_require__.o(exports, name)) { 53 | /******/ Object.defineProperty(exports, name, { 54 | /******/ configurable: false, 55 | /******/ enumerable: true, 56 | /******/ get: getter 57 | /******/ }); 58 | /******/ } 59 | /******/ }; 60 | /******/ 61 | /******/ // getDefaultExport function for compatibility with non-harmony modules 62 | /******/ __webpack_require__.n = function(module) { 63 | /******/ var getter = module && module.__esModule ? 64 | /******/ function getDefault() { return module['default']; } : 65 | /******/ function getModuleExports() { return module; }; 66 | /******/ __webpack_require__.d(getter, 'a', getter); 67 | /******/ return getter; 68 | /******/ }; 69 | /******/ 70 | /******/ // Object.prototype.hasOwnProperty.call 71 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 72 | /******/ 73 | /******/ // __webpack_public_path__ 74 | /******/ __webpack_require__.p = "/"; 75 | /******/ 76 | /******/ // Load entry module and return exports 77 | /******/ return __webpack_require__(__webpack_require__.s = 4); 78 | /******/ }) 79 | /************************************************************************/ 80 | /******/ ([ 81 | /* 0 */ 82 | /***/ (function(module, exports, __webpack_require__) { 83 | 84 | var pkg = __webpack_require__(1); 85 | 86 | if (typeof pkg === "function") { 87 | module.exports = { 88 | lodashMerge : pkg 89 | }; 90 | } else { 91 | module.exports = pkg; 92 | } 93 | 94 | 95 | 96 | /***/ }), 97 | /* 1 */ 98 | /***/ (function(module, exports, __webpack_require__) { 99 | 100 | /* WEBPACK VAR INJECTION */(function(global, module) {/** 101 | * lodash (Custom Build) 102 | * Build: `lodash modularize exports="npm" -o ./` 103 | * Copyright jQuery Foundation and other contributors 104 | * Released under MIT license 105 | * Based on Underscore.js 1.8.3 106 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors 107 | */ 108 | 109 | /** Used as the size to enable large array optimizations. */ 110 | var LARGE_ARRAY_SIZE = 200; 111 | 112 | /** Used to stand-in for `undefined` hash values. */ 113 | var HASH_UNDEFINED = '__lodash_hash_undefined__'; 114 | 115 | /** Used as references for various `Number` constants. */ 116 | var MAX_SAFE_INTEGER = 9007199254740991; 117 | 118 | /** `Object#toString` result references. */ 119 | var argsTag = '[object Arguments]', 120 | arrayTag = '[object Array]', 121 | boolTag = '[object Boolean]', 122 | dateTag = '[object Date]', 123 | errorTag = '[object Error]', 124 | funcTag = '[object Function]', 125 | genTag = '[object GeneratorFunction]', 126 | mapTag = '[object Map]', 127 | numberTag = '[object Number]', 128 | objectTag = '[object Object]', 129 | promiseTag = '[object Promise]', 130 | regexpTag = '[object RegExp]', 131 | setTag = '[object Set]', 132 | stringTag = '[object String]', 133 | symbolTag = '[object Symbol]', 134 | weakMapTag = '[object WeakMap]'; 135 | 136 | var arrayBufferTag = '[object ArrayBuffer]', 137 | dataViewTag = '[object DataView]', 138 | float32Tag = '[object Float32Array]', 139 | float64Tag = '[object Float64Array]', 140 | int8Tag = '[object Int8Array]', 141 | int16Tag = '[object Int16Array]', 142 | int32Tag = '[object Int32Array]', 143 | uint8Tag = '[object Uint8Array]', 144 | uint8ClampedTag = '[object Uint8ClampedArray]', 145 | uint16Tag = '[object Uint16Array]', 146 | uint32Tag = '[object Uint32Array]'; 147 | 148 | /** 149 | * Used to match `RegExp` 150 | * [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns). 151 | */ 152 | var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; 153 | 154 | /** Used to match `RegExp` flags from their coerced string values. */ 155 | var reFlags = /\w*$/; 156 | 157 | /** Used to detect host constructors (Safari). */ 158 | var reIsHostCtor = /^\[object .+?Constructor\]$/; 159 | 160 | /** Used to detect unsigned integer values. */ 161 | var reIsUint = /^(?:0|[1-9]\d*)$/; 162 | 163 | /** Used to identify `toStringTag` values of typed arrays. */ 164 | var typedArrayTags = {}; 165 | typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = 166 | typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = 167 | typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = 168 | typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = 169 | typedArrayTags[uint32Tag] = true; 170 | typedArrayTags[argsTag] = typedArrayTags[arrayTag] = 171 | typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = 172 | typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = 173 | typedArrayTags[errorTag] = typedArrayTags[funcTag] = 174 | typedArrayTags[mapTag] = typedArrayTags[numberTag] = 175 | typedArrayTags[objectTag] = typedArrayTags[regexpTag] = 176 | typedArrayTags[setTag] = typedArrayTags[stringTag] = 177 | typedArrayTags[weakMapTag] = false; 178 | 179 | /** Used to identify `toStringTag` values supported by `_.clone`. */ 180 | var cloneableTags = {}; 181 | cloneableTags[argsTag] = cloneableTags[arrayTag] = 182 | cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = 183 | cloneableTags[boolTag] = cloneableTags[dateTag] = 184 | cloneableTags[float32Tag] = cloneableTags[float64Tag] = 185 | cloneableTags[int8Tag] = cloneableTags[int16Tag] = 186 | cloneableTags[int32Tag] = cloneableTags[mapTag] = 187 | cloneableTags[numberTag] = cloneableTags[objectTag] = 188 | cloneableTags[regexpTag] = cloneableTags[setTag] = 189 | cloneableTags[stringTag] = cloneableTags[symbolTag] = 190 | cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = 191 | cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; 192 | cloneableTags[errorTag] = cloneableTags[funcTag] = 193 | cloneableTags[weakMapTag] = false; 194 | 195 | /** Detect free variable `global` from Node.js. */ 196 | var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; 197 | 198 | /** Detect free variable `self`. */ 199 | var freeSelf = typeof self == 'object' && self && self.Object === Object && self; 200 | 201 | /** Used as a reference to the global object. */ 202 | var root = freeGlobal || freeSelf || Function('return this')(); 203 | 204 | /** Detect free variable `exports`. */ 205 | var freeExports = freeGlobal && typeof exports == 'object' && exports; 206 | 207 | /** Detect free variable `module`. */ 208 | var freeModule = freeExports && typeof module == 'object' && module; 209 | 210 | /** Detect the popular CommonJS extension `module.exports`. */ 211 | var moduleExports = freeModule && freeModule.exports === freeExports; 212 | 213 | /** Detect free variable `process` from Node.js. */ 214 | var freeProcess = moduleExports && freeGlobal.process; 215 | 216 | /** Used to access faster Node.js helpers. */ 217 | var nodeUtil = (function() { 218 | try { 219 | return freeProcess && freeProcess.binding('util'); 220 | } catch (e) {} 221 | }()); 222 | 223 | /* Node.js helper references. */ 224 | var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; 225 | 226 | /** 227 | * Adds the key-value `pair` to `map`. 228 | * 229 | * @private 230 | * @param {Object} map The map to modify. 231 | * @param {Array} pair The key-value pair to add. 232 | * @returns {Object} Returns `map`. 233 | */ 234 | function addMapEntry(map, pair) { 235 | // Don't return `map.set` because it's not chainable in IE 11. 236 | map.set(pair[0], pair[1]); 237 | return map; 238 | } 239 | 240 | /** 241 | * Adds `value` to `set`. 242 | * 243 | * @private 244 | * @param {Object} set The set to modify. 245 | * @param {*} value The value to add. 246 | * @returns {Object} Returns `set`. 247 | */ 248 | function addSetEntry(set, value) { 249 | // Don't return `set.add` because it's not chainable in IE 11. 250 | set.add(value); 251 | return set; 252 | } 253 | 254 | /** 255 | * A faster alternative to `Function#apply`, this function invokes `func` 256 | * with the `this` binding of `thisArg` and the arguments of `args`. 257 | * 258 | * @private 259 | * @param {Function} func The function to invoke. 260 | * @param {*} thisArg The `this` binding of `func`. 261 | * @param {Array} args The arguments to invoke `func` with. 262 | * @returns {*} Returns the result of `func`. 263 | */ 264 | function apply(func, thisArg, args) { 265 | switch (args.length) { 266 | case 0: return func.call(thisArg); 267 | case 1: return func.call(thisArg, args[0]); 268 | case 2: return func.call(thisArg, args[0], args[1]); 269 | case 3: return func.call(thisArg, args[0], args[1], args[2]); 270 | } 271 | return func.apply(thisArg, args); 272 | } 273 | 274 | /** 275 | * A specialized version of `_.forEach` for arrays without support for 276 | * iteratee shorthands. 277 | * 278 | * @private 279 | * @param {Array} [array] The array to iterate over. 280 | * @param {Function} iteratee The function invoked per iteration. 281 | * @returns {Array} Returns `array`. 282 | */ 283 | function arrayEach(array, iteratee) { 284 | var index = -1, 285 | length = array ? array.length : 0; 286 | 287 | while (++index < length) { 288 | if (iteratee(array[index], index, array) === false) { 289 | break; 290 | } 291 | } 292 | return array; 293 | } 294 | 295 | /** 296 | * Appends the elements of `values` to `array`. 297 | * 298 | * @private 299 | * @param {Array} array The array to modify. 300 | * @param {Array} values The values to append. 301 | * @returns {Array} Returns `array`. 302 | */ 303 | function arrayPush(array, values) { 304 | var index = -1, 305 | length = values.length, 306 | offset = array.length; 307 | 308 | while (++index < length) { 309 | array[offset + index] = values[index]; 310 | } 311 | return array; 312 | } 313 | 314 | /** 315 | * A specialized version of `_.reduce` for arrays without support for 316 | * iteratee shorthands. 317 | * 318 | * @private 319 | * @param {Array} [array] The array to iterate over. 320 | * @param {Function} iteratee The function invoked per iteration. 321 | * @param {*} [accumulator] The initial value. 322 | * @param {boolean} [initAccum] Specify using the first element of `array` as 323 | * the initial value. 324 | * @returns {*} Returns the accumulated value. 325 | */ 326 | function arrayReduce(array, iteratee, accumulator, initAccum) { 327 | var index = -1, 328 | length = array ? array.length : 0; 329 | 330 | if (initAccum && length) { 331 | accumulator = array[++index]; 332 | } 333 | while (++index < length) { 334 | accumulator = iteratee(accumulator, array[index], index, array); 335 | } 336 | return accumulator; 337 | } 338 | 339 | /** 340 | * The base implementation of `_.property` without support for deep paths. 341 | * 342 | * @private 343 | * @param {string} key The key of the property to get. 344 | * @returns {Function} Returns the new accessor function. 345 | */ 346 | function baseProperty(key) { 347 | return function(object) { 348 | return object == null ? undefined : object[key]; 349 | }; 350 | } 351 | 352 | /** 353 | * The base implementation of `_.times` without support for iteratee shorthands 354 | * or max array length checks. 355 | * 356 | * @private 357 | * @param {number} n The number of times to invoke `iteratee`. 358 | * @param {Function} iteratee The function invoked per iteration. 359 | * @returns {Array} Returns the array of results. 360 | */ 361 | function baseTimes(n, iteratee) { 362 | var index = -1, 363 | result = Array(n); 364 | 365 | while (++index < n) { 366 | result[index] = iteratee(index); 367 | } 368 | return result; 369 | } 370 | 371 | /** 372 | * The base implementation of `_.unary` without support for storing metadata. 373 | * 374 | * @private 375 | * @param {Function} func The function to cap arguments for. 376 | * @returns {Function} Returns the new capped function. 377 | */ 378 | function baseUnary(func) { 379 | return function(value) { 380 | return func(value); 381 | }; 382 | } 383 | 384 | /** 385 | * Gets the value at `key` of `object`. 386 | * 387 | * @private 388 | * @param {Object} [object] The object to query. 389 | * @param {string} key The key of the property to get. 390 | * @returns {*} Returns the property value. 391 | */ 392 | function getValue(object, key) { 393 | return object == null ? undefined : object[key]; 394 | } 395 | 396 | /** 397 | * Checks if `value` is a host object in IE < 9. 398 | * 399 | * @private 400 | * @param {*} value The value to check. 401 | * @returns {boolean} Returns `true` if `value` is a host object, else `false`. 402 | */ 403 | function isHostObject(value) { 404 | // Many host objects are `Object` objects that can coerce to strings 405 | // despite having improperly defined `toString` methods. 406 | var result = false; 407 | if (value != null && typeof value.toString != 'function') { 408 | try { 409 | result = !!(value + ''); 410 | } catch (e) {} 411 | } 412 | return result; 413 | } 414 | 415 | /** 416 | * Converts `iterator` to an array. 417 | * 418 | * @private 419 | * @param {Object} iterator The iterator to convert. 420 | * @returns {Array} Returns the converted array. 421 | */ 422 | function iteratorToArray(iterator) { 423 | var data, 424 | result = []; 425 | 426 | while (!(data = iterator.next()).done) { 427 | result.push(data.value); 428 | } 429 | return result; 430 | } 431 | 432 | /** 433 | * Converts `map` to its key-value pairs. 434 | * 435 | * @private 436 | * @param {Object} map The map to convert. 437 | * @returns {Array} Returns the key-value pairs. 438 | */ 439 | function mapToArray(map) { 440 | var index = -1, 441 | result = Array(map.size); 442 | 443 | map.forEach(function(value, key) { 444 | result[++index] = [key, value]; 445 | }); 446 | return result; 447 | } 448 | 449 | /** 450 | * Creates a function that invokes `func` with its first argument transformed. 451 | * 452 | * @private 453 | * @param {Function} func The function to wrap. 454 | * @param {Function} transform The argument transform. 455 | * @returns {Function} Returns the new function. 456 | */ 457 | function overArg(func, transform) { 458 | return function(arg) { 459 | return func(transform(arg)); 460 | }; 461 | } 462 | 463 | /** 464 | * Converts `set` to an array of its values. 465 | * 466 | * @private 467 | * @param {Object} set The set to convert. 468 | * @returns {Array} Returns the values. 469 | */ 470 | function setToArray(set) { 471 | var index = -1, 472 | result = Array(set.size); 473 | 474 | set.forEach(function(value) { 475 | result[++index] = value; 476 | }); 477 | return result; 478 | } 479 | 480 | /** Used for built-in method references. */ 481 | var arrayProto = Array.prototype, 482 | objectProto = Object.prototype; 483 | 484 | /** Used to detect overreaching core-js shims. */ 485 | var coreJsData = root['__core-js_shared__']; 486 | 487 | /** Used to detect methods masquerading as native. */ 488 | var maskSrcKey = (function() { 489 | var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); 490 | return uid ? ('Symbol(src)_1.' + uid) : ''; 491 | }()); 492 | 493 | /** Used to resolve the decompiled source of functions. */ 494 | var funcToString = Function.prototype.toString; 495 | 496 | /** Used to check objects for own properties. */ 497 | var hasOwnProperty = objectProto.hasOwnProperty; 498 | 499 | /** Used to infer the `Object` constructor. */ 500 | var objectCtorString = funcToString.call(Object); 501 | 502 | /** 503 | * Used to resolve the 504 | * [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) 505 | * of values. 506 | */ 507 | var objectToString = objectProto.toString; 508 | 509 | /** Used to detect if a method is native. */ 510 | var reIsNative = RegExp('^' + 511 | funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') 512 | .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' 513 | ); 514 | 515 | /** Built-in value references. */ 516 | var Buffer = moduleExports ? root.Buffer : undefined, 517 | Reflect = root.Reflect, 518 | Symbol = root.Symbol, 519 | Uint8Array = root.Uint8Array, 520 | enumerate = Reflect ? Reflect.enumerate : undefined, 521 | objectCreate = Object.create, 522 | propertyIsEnumerable = objectProto.propertyIsEnumerable, 523 | splice = arrayProto.splice; 524 | 525 | /* Built-in method references for those with the same name as other `lodash` methods. */ 526 | var nativeGetPrototype = Object.getPrototypeOf, 527 | nativeGetSymbols = Object.getOwnPropertySymbols, 528 | nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined, 529 | nativeKeys = Object.keys, 530 | nativeMax = Math.max; 531 | 532 | /* Built-in method references that are verified to be native. */ 533 | var DataView = getNative(root, 'DataView'), 534 | Map = getNative(root, 'Map'), 535 | Promise = getNative(root, 'Promise'), 536 | Set = getNative(root, 'Set'), 537 | WeakMap = getNative(root, 'WeakMap'), 538 | nativeCreate = getNative(Object, 'create'); 539 | 540 | /** Used to detect maps, sets, and weakmaps. */ 541 | var dataViewCtorString = toSource(DataView), 542 | mapCtorString = toSource(Map), 543 | promiseCtorString = toSource(Promise), 544 | setCtorString = toSource(Set), 545 | weakMapCtorString = toSource(WeakMap); 546 | 547 | /** Used to convert symbols to primitives and strings. */ 548 | var symbolProto = Symbol ? Symbol.prototype : undefined, 549 | symbolValueOf = symbolProto ? symbolProto.valueOf : undefined; 550 | 551 | /** 552 | * Creates a hash object. 553 | * 554 | * @private 555 | * @constructor 556 | * @param {Array} [entries] The key-value pairs to cache. 557 | */ 558 | function Hash(entries) { 559 | var index = -1, 560 | length = entries ? entries.length : 0; 561 | 562 | this.clear(); 563 | while (++index < length) { 564 | var entry = entries[index]; 565 | this.set(entry[0], entry[1]); 566 | } 567 | } 568 | 569 | /** 570 | * Removes all key-value entries from the hash. 571 | * 572 | * @private 573 | * @name clear 574 | * @memberOf Hash 575 | */ 576 | function hashClear() { 577 | this.__data__ = nativeCreate ? nativeCreate(null) : {}; 578 | } 579 | 580 | /** 581 | * Removes `key` and its value from the hash. 582 | * 583 | * @private 584 | * @name delete 585 | * @memberOf Hash 586 | * @param {Object} hash The hash to modify. 587 | * @param {string} key The key of the value to remove. 588 | * @returns {boolean} Returns `true` if the entry was removed, else `false`. 589 | */ 590 | function hashDelete(key) { 591 | return this.has(key) && delete this.__data__[key]; 592 | } 593 | 594 | /** 595 | * Gets the hash value for `key`. 596 | * 597 | * @private 598 | * @name get 599 | * @memberOf Hash 600 | * @param {string} key The key of the value to get. 601 | * @returns {*} Returns the entry value. 602 | */ 603 | function hashGet(key) { 604 | var data = this.__data__; 605 | if (nativeCreate) { 606 | var result = data[key]; 607 | return result === HASH_UNDEFINED ? undefined : result; 608 | } 609 | return hasOwnProperty.call(data, key) ? data[key] : undefined; 610 | } 611 | 612 | /** 613 | * Checks if a hash value for `key` exists. 614 | * 615 | * @private 616 | * @name has 617 | * @memberOf Hash 618 | * @param {string} key The key of the entry to check. 619 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. 620 | */ 621 | function hashHas(key) { 622 | var data = this.__data__; 623 | return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); 624 | } 625 | 626 | /** 627 | * Sets the hash `key` to `value`. 628 | * 629 | * @private 630 | * @name set 631 | * @memberOf Hash 632 | * @param {string} key The key of the value to set. 633 | * @param {*} value The value to set. 634 | * @returns {Object} Returns the hash instance. 635 | */ 636 | function hashSet(key, value) { 637 | var data = this.__data__; 638 | data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; 639 | return this; 640 | } 641 | 642 | // Add methods to `Hash`. 643 | Hash.prototype.clear = hashClear; 644 | Hash.prototype['delete'] = hashDelete; 645 | Hash.prototype.get = hashGet; 646 | Hash.prototype.has = hashHas; 647 | Hash.prototype.set = hashSet; 648 | 649 | /** 650 | * Creates an list cache object. 651 | * 652 | * @private 653 | * @constructor 654 | * @param {Array} [entries] The key-value pairs to cache. 655 | */ 656 | function ListCache(entries) { 657 | var index = -1, 658 | length = entries ? entries.length : 0; 659 | 660 | this.clear(); 661 | while (++index < length) { 662 | var entry = entries[index]; 663 | this.set(entry[0], entry[1]); 664 | } 665 | } 666 | 667 | /** 668 | * Removes all key-value entries from the list cache. 669 | * 670 | * @private 671 | * @name clear 672 | * @memberOf ListCache 673 | */ 674 | function listCacheClear() { 675 | this.__data__ = []; 676 | } 677 | 678 | /** 679 | * Removes `key` and its value from the list cache. 680 | * 681 | * @private 682 | * @name delete 683 | * @memberOf ListCache 684 | * @param {string} key The key of the value to remove. 685 | * @returns {boolean} Returns `true` if the entry was removed, else `false`. 686 | */ 687 | function listCacheDelete(key) { 688 | var data = this.__data__, 689 | index = assocIndexOf(data, key); 690 | 691 | if (index < 0) { 692 | return false; 693 | } 694 | var lastIndex = data.length - 1; 695 | if (index == lastIndex) { 696 | data.pop(); 697 | } else { 698 | splice.call(data, index, 1); 699 | } 700 | return true; 701 | } 702 | 703 | /** 704 | * Gets the list cache value for `key`. 705 | * 706 | * @private 707 | * @name get 708 | * @memberOf ListCache 709 | * @param {string} key The key of the value to get. 710 | * @returns {*} Returns the entry value. 711 | */ 712 | function listCacheGet(key) { 713 | var data = this.__data__, 714 | index = assocIndexOf(data, key); 715 | 716 | return index < 0 ? undefined : data[index][1]; 717 | } 718 | 719 | /** 720 | * Checks if a list cache value for `key` exists. 721 | * 722 | * @private 723 | * @name has 724 | * @memberOf ListCache 725 | * @param {string} key The key of the entry to check. 726 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. 727 | */ 728 | function listCacheHas(key) { 729 | return assocIndexOf(this.__data__, key) > -1; 730 | } 731 | 732 | /** 733 | * Sets the list cache `key` to `value`. 734 | * 735 | * @private 736 | * @name set 737 | * @memberOf ListCache 738 | * @param {string} key The key of the value to set. 739 | * @param {*} value The value to set. 740 | * @returns {Object} Returns the list cache instance. 741 | */ 742 | function listCacheSet(key, value) { 743 | var data = this.__data__, 744 | index = assocIndexOf(data, key); 745 | 746 | if (index < 0) { 747 | data.push([key, value]); 748 | } else { 749 | data[index][1] = value; 750 | } 751 | return this; 752 | } 753 | 754 | // Add methods to `ListCache`. 755 | ListCache.prototype.clear = listCacheClear; 756 | ListCache.prototype['delete'] = listCacheDelete; 757 | ListCache.prototype.get = listCacheGet; 758 | ListCache.prototype.has = listCacheHas; 759 | ListCache.prototype.set = listCacheSet; 760 | 761 | /** 762 | * Creates a map cache object to store key-value pairs. 763 | * 764 | * @private 765 | * @constructor 766 | * @param {Array} [entries] The key-value pairs to cache. 767 | */ 768 | function MapCache(entries) { 769 | var index = -1, 770 | length = entries ? entries.length : 0; 771 | 772 | this.clear(); 773 | while (++index < length) { 774 | var entry = entries[index]; 775 | this.set(entry[0], entry[1]); 776 | } 777 | } 778 | 779 | /** 780 | * Removes all key-value entries from the map. 781 | * 782 | * @private 783 | * @name clear 784 | * @memberOf MapCache 785 | */ 786 | function mapCacheClear() { 787 | this.__data__ = { 788 | 'hash': new Hash, 789 | 'map': new (Map || ListCache), 790 | 'string': new Hash 791 | }; 792 | } 793 | 794 | /** 795 | * Removes `key` and its value from the map. 796 | * 797 | * @private 798 | * @name delete 799 | * @memberOf MapCache 800 | * @param {string} key The key of the value to remove. 801 | * @returns {boolean} Returns `true` if the entry was removed, else `false`. 802 | */ 803 | function mapCacheDelete(key) { 804 | return getMapData(this, key)['delete'](key); 805 | } 806 | 807 | /** 808 | * Gets the map value for `key`. 809 | * 810 | * @private 811 | * @name get 812 | * @memberOf MapCache 813 | * @param {string} key The key of the value to get. 814 | * @returns {*} Returns the entry value. 815 | */ 816 | function mapCacheGet(key) { 817 | return getMapData(this, key).get(key); 818 | } 819 | 820 | /** 821 | * Checks if a map value for `key` exists. 822 | * 823 | * @private 824 | * @name has 825 | * @memberOf MapCache 826 | * @param {string} key The key of the entry to check. 827 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. 828 | */ 829 | function mapCacheHas(key) { 830 | return getMapData(this, key).has(key); 831 | } 832 | 833 | /** 834 | * Sets the map `key` to `value`. 835 | * 836 | * @private 837 | * @name set 838 | * @memberOf MapCache 839 | * @param {string} key The key of the value to set. 840 | * @param {*} value The value to set. 841 | * @returns {Object} Returns the map cache instance. 842 | */ 843 | function mapCacheSet(key, value) { 844 | getMapData(this, key).set(key, value); 845 | return this; 846 | } 847 | 848 | // Add methods to `MapCache`. 849 | MapCache.prototype.clear = mapCacheClear; 850 | MapCache.prototype['delete'] = mapCacheDelete; 851 | MapCache.prototype.get = mapCacheGet; 852 | MapCache.prototype.has = mapCacheHas; 853 | MapCache.prototype.set = mapCacheSet; 854 | 855 | /** 856 | * Creates a stack cache object to store key-value pairs. 857 | * 858 | * @private 859 | * @constructor 860 | * @param {Array} [entries] The key-value pairs to cache. 861 | */ 862 | function Stack(entries) { 863 | this.__data__ = new ListCache(entries); 864 | } 865 | 866 | /** 867 | * Removes all key-value entries from the stack. 868 | * 869 | * @private 870 | * @name clear 871 | * @memberOf Stack 872 | */ 873 | function stackClear() { 874 | this.__data__ = new ListCache; 875 | } 876 | 877 | /** 878 | * Removes `key` and its value from the stack. 879 | * 880 | * @private 881 | * @name delete 882 | * @memberOf Stack 883 | * @param {string} key The key of the value to remove. 884 | * @returns {boolean} Returns `true` if the entry was removed, else `false`. 885 | */ 886 | function stackDelete(key) { 887 | return this.__data__['delete'](key); 888 | } 889 | 890 | /** 891 | * Gets the stack value for `key`. 892 | * 893 | * @private 894 | * @name get 895 | * @memberOf Stack 896 | * @param {string} key The key of the value to get. 897 | * @returns {*} Returns the entry value. 898 | */ 899 | function stackGet(key) { 900 | return this.__data__.get(key); 901 | } 902 | 903 | /** 904 | * Checks if a stack value for `key` exists. 905 | * 906 | * @private 907 | * @name has 908 | * @memberOf Stack 909 | * @param {string} key The key of the entry to check. 910 | * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. 911 | */ 912 | function stackHas(key) { 913 | return this.__data__.has(key); 914 | } 915 | 916 | /** 917 | * Sets the stack `key` to `value`. 918 | * 919 | * @private 920 | * @name set 921 | * @memberOf Stack 922 | * @param {string} key The key of the value to set. 923 | * @param {*} value The value to set. 924 | * @returns {Object} Returns the stack cache instance. 925 | */ 926 | function stackSet(key, value) { 927 | var cache = this.__data__; 928 | if (cache instanceof ListCache) { 929 | var pairs = cache.__data__; 930 | if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { 931 | pairs.push([key, value]); 932 | return this; 933 | } 934 | cache = this.__data__ = new MapCache(pairs); 935 | } 936 | cache.set(key, value); 937 | return this; 938 | } 939 | 940 | // Add methods to `Stack`. 941 | Stack.prototype.clear = stackClear; 942 | Stack.prototype['delete'] = stackDelete; 943 | Stack.prototype.get = stackGet; 944 | Stack.prototype.has = stackHas; 945 | Stack.prototype.set = stackSet; 946 | 947 | /** 948 | * This function is like `assignValue` except that it doesn't assign 949 | * `undefined` values. 950 | * 951 | * @private 952 | * @param {Object} object The object to modify. 953 | * @param {string} key The key of the property to assign. 954 | * @param {*} value The value to assign. 955 | */ 956 | function assignMergeValue(object, key, value) { 957 | if ((value !== undefined && !eq(object[key], value)) || 958 | (typeof key == 'number' && value === undefined && !(key in object))) { 959 | object[key] = value; 960 | } 961 | } 962 | 963 | /** 964 | * Assigns `value` to `key` of `object` if the existing value is not equivalent 965 | * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) 966 | * for equality comparisons. 967 | * 968 | * @private 969 | * @param {Object} object The object to modify. 970 | * @param {string} key The key of the property to assign. 971 | * @param {*} value The value to assign. 972 | */ 973 | function assignValue(object, key, value) { 974 | var objValue = object[key]; 975 | if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) || 976 | (value === undefined && !(key in object))) { 977 | object[key] = value; 978 | } 979 | } 980 | 981 | /** 982 | * Gets the index at which the `key` is found in `array` of key-value pairs. 983 | * 984 | * @private 985 | * @param {Array} array The array to search. 986 | * @param {*} key The key to search for. 987 | * @returns {number} Returns the index of the matched value, else `-1`. 988 | */ 989 | function assocIndexOf(array, key) { 990 | var length = array.length; 991 | while (length--) { 992 | if (eq(array[length][0], key)) { 993 | return length; 994 | } 995 | } 996 | return -1; 997 | } 998 | 999 | /** 1000 | * The base implementation of `_.assign` without support for multiple sources 1001 | * or `customizer` functions. 1002 | * 1003 | * @private 1004 | * @param {Object} object The destination object. 1005 | * @param {Object} source The source object. 1006 | * @returns {Object} Returns `object`. 1007 | */ 1008 | function baseAssign(object, source) { 1009 | return object && copyObject(source, keys(source), object); 1010 | } 1011 | 1012 | /** 1013 | * The base implementation of `_.clone` and `_.cloneDeep` which tracks 1014 | * traversed objects. 1015 | * 1016 | * @private 1017 | * @param {*} value The value to clone. 1018 | * @param {boolean} [isDeep] Specify a deep clone. 1019 | * @param {boolean} [isFull] Specify a clone including symbols. 1020 | * @param {Function} [customizer] The function to customize cloning. 1021 | * @param {string} [key] The key of `value`. 1022 | * @param {Object} [object] The parent object of `value`. 1023 | * @param {Object} [stack] Tracks traversed objects and their clone counterparts. 1024 | * @returns {*} Returns the cloned value. 1025 | */ 1026 | function baseClone(value, isDeep, isFull, customizer, key, object, stack) { 1027 | var result; 1028 | if (customizer) { 1029 | result = object ? customizer(value, key, object, stack) : customizer(value); 1030 | } 1031 | if (result !== undefined) { 1032 | return result; 1033 | } 1034 | if (!isObject(value)) { 1035 | return value; 1036 | } 1037 | var isArr = isArray(value); 1038 | if (isArr) { 1039 | result = initCloneArray(value); 1040 | if (!isDeep) { 1041 | return copyArray(value, result); 1042 | } 1043 | } else { 1044 | var tag = getTag(value), 1045 | isFunc = tag == funcTag || tag == genTag; 1046 | 1047 | if (isBuffer(value)) { 1048 | return cloneBuffer(value, isDeep); 1049 | } 1050 | if (tag == objectTag || tag == argsTag || (isFunc && !object)) { 1051 | if (isHostObject(value)) { 1052 | return object ? value : {}; 1053 | } 1054 | result = initCloneObject(isFunc ? {} : value); 1055 | if (!isDeep) { 1056 | return copySymbols(value, baseAssign(result, value)); 1057 | } 1058 | } else { 1059 | if (!cloneableTags[tag]) { 1060 | return object ? value : {}; 1061 | } 1062 | result = initCloneByTag(value, tag, baseClone, isDeep); 1063 | } 1064 | } 1065 | // Check for circular references and return its corresponding clone. 1066 | stack || (stack = new Stack); 1067 | var stacked = stack.get(value); 1068 | if (stacked) { 1069 | return stacked; 1070 | } 1071 | stack.set(value, result); 1072 | 1073 | if (!isArr) { 1074 | var props = isFull ? getAllKeys(value) : keys(value); 1075 | } 1076 | arrayEach(props || value, function(subValue, key) { 1077 | if (props) { 1078 | key = subValue; 1079 | subValue = value[key]; 1080 | } 1081 | // Recursively populate clone (susceptible to call stack limits). 1082 | assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack)); 1083 | }); 1084 | if (!isFull) { 1085 | stack['delete'](value); 1086 | } 1087 | return result; 1088 | } 1089 | 1090 | /** 1091 | * The base implementation of `_.create` without support for assigning 1092 | * properties to the created object. 1093 | * 1094 | * @private 1095 | * @param {Object} prototype The object to inherit from. 1096 | * @returns {Object} Returns the new object. 1097 | */ 1098 | function baseCreate(proto) { 1099 | return isObject(proto) ? objectCreate(proto) : {}; 1100 | } 1101 | 1102 | /** 1103 | * The base implementation of `getAllKeys` and `getAllKeysIn` which uses 1104 | * `keysFunc` and `symbolsFunc` to get the enumerable property names and 1105 | * symbols of `object`. 1106 | * 1107 | * @private 1108 | * @param {Object} object The object to query. 1109 | * @param {Function} keysFunc The function to get the keys of `object`. 1110 | * @param {Function} symbolsFunc The function to get the symbols of `object`. 1111 | * @returns {Array} Returns the array of property names and symbols. 1112 | */ 1113 | function baseGetAllKeys(object, keysFunc, symbolsFunc) { 1114 | var result = keysFunc(object); 1115 | return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); 1116 | } 1117 | 1118 | /** 1119 | * The base implementation of `getTag`. 1120 | * 1121 | * @private 1122 | * @param {*} value The value to query. 1123 | * @returns {string} Returns the `toStringTag`. 1124 | */ 1125 | function baseGetTag(value) { 1126 | return objectToString.call(value); 1127 | } 1128 | 1129 | /** 1130 | * The base implementation of `_.has` without support for deep paths. 1131 | * 1132 | * @private 1133 | * @param {Object} [object] The object to query. 1134 | * @param {Array|string} key The key to check. 1135 | * @returns {boolean} Returns `true` if `key` exists, else `false`. 1136 | */ 1137 | function baseHas(object, key) { 1138 | // Avoid a bug in IE 10-11 where objects with a [[Prototype]] of `null`, 1139 | // that are composed entirely of index properties, return `false` for 1140 | // `hasOwnProperty` checks of them. 1141 | return object != null && 1142 | (hasOwnProperty.call(object, key) || 1143 | (typeof object == 'object' && key in object && getPrototype(object) === null)); 1144 | } 1145 | 1146 | /** 1147 | * The base implementation of `_.isNative` without bad shim checks. 1148 | * 1149 | * @private 1150 | * @param {*} value The value to check. 1151 | * @returns {boolean} Returns `true` if `value` is a native function, 1152 | * else `false`. 1153 | */ 1154 | function baseIsNative(value) { 1155 | if (!isObject(value) || isMasked(value)) { 1156 | return false; 1157 | } 1158 | var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor; 1159 | return pattern.test(toSource(value)); 1160 | } 1161 | 1162 | /** 1163 | * The base implementation of `_.isTypedArray` without Node.js optimizations. 1164 | * 1165 | * @private 1166 | * @param {*} value The value to check. 1167 | * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. 1168 | */ 1169 | function baseIsTypedArray(value) { 1170 | return isObjectLike(value) && 1171 | isLength(value.length) && !!typedArrayTags[objectToString.call(value)]; 1172 | } 1173 | 1174 | /** 1175 | * The base implementation of `_.keys` which doesn't skip the constructor 1176 | * property of prototypes or treat sparse arrays as dense. 1177 | * 1178 | * @private 1179 | * @param {Object} object The object to query. 1180 | * @returns {Array} Returns the array of property names. 1181 | */ 1182 | var baseKeys = overArg(nativeKeys, Object); 1183 | 1184 | /** 1185 | * The base implementation of `_.keysIn` which doesn't skip the constructor 1186 | * property of prototypes or treat sparse arrays as dense. 1187 | * 1188 | * @private 1189 | * @param {Object} object The object to query. 1190 | * @returns {Array} Returns the array of property names. 1191 | */ 1192 | function baseKeysIn(object) { 1193 | object = object == null ? object : Object(object); 1194 | 1195 | var result = []; 1196 | for (var key in object) { 1197 | result.push(key); 1198 | } 1199 | return result; 1200 | } 1201 | 1202 | // Fallback for IE < 9 with es6-shim. 1203 | if (enumerate && !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf')) { 1204 | baseKeysIn = function(object) { 1205 | return iteratorToArray(enumerate(object)); 1206 | }; 1207 | } 1208 | 1209 | /** 1210 | * The base implementation of `_.merge` without support for multiple sources. 1211 | * 1212 | * @private 1213 | * @param {Object} object The destination object. 1214 | * @param {Object} source The source object. 1215 | * @param {number} srcIndex The index of `source`. 1216 | * @param {Function} [customizer] The function to customize merged values. 1217 | * @param {Object} [stack] Tracks traversed source values and their merged 1218 | * counterparts. 1219 | */ 1220 | function baseMerge(object, source, srcIndex, customizer, stack) { 1221 | if (object === source) { 1222 | return; 1223 | } 1224 | if (!(isArray(source) || isTypedArray(source))) { 1225 | var props = keysIn(source); 1226 | } 1227 | arrayEach(props || source, function(srcValue, key) { 1228 | if (props) { 1229 | key = srcValue; 1230 | srcValue = source[key]; 1231 | } 1232 | if (isObject(srcValue)) { 1233 | stack || (stack = new Stack); 1234 | baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack); 1235 | } 1236 | else { 1237 | var newValue = customizer 1238 | ? customizer(object[key], srcValue, (key + ''), object, source, stack) 1239 | : undefined; 1240 | 1241 | if (newValue === undefined) { 1242 | newValue = srcValue; 1243 | } 1244 | assignMergeValue(object, key, newValue); 1245 | } 1246 | }); 1247 | } 1248 | 1249 | /** 1250 | * A specialized version of `baseMerge` for arrays and objects which performs 1251 | * deep merges and tracks traversed objects enabling objects with circular 1252 | * references to be merged. 1253 | * 1254 | * @private 1255 | * @param {Object} object The destination object. 1256 | * @param {Object} source The source object. 1257 | * @param {string} key The key of the value to merge. 1258 | * @param {number} srcIndex The index of `source`. 1259 | * @param {Function} mergeFunc The function to merge values. 1260 | * @param {Function} [customizer] The function to customize assigned values. 1261 | * @param {Object} [stack] Tracks traversed source values and their merged 1262 | * counterparts. 1263 | */ 1264 | function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) { 1265 | var objValue = object[key], 1266 | srcValue = source[key], 1267 | stacked = stack.get(srcValue); 1268 | 1269 | if (stacked) { 1270 | assignMergeValue(object, key, stacked); 1271 | return; 1272 | } 1273 | var newValue = customizer 1274 | ? customizer(objValue, srcValue, (key + ''), object, source, stack) 1275 | : undefined; 1276 | 1277 | var isCommon = newValue === undefined; 1278 | 1279 | if (isCommon) { 1280 | newValue = srcValue; 1281 | if (isArray(srcValue) || isTypedArray(srcValue)) { 1282 | if (isArray(objValue)) { 1283 | newValue = objValue; 1284 | } 1285 | else if (isArrayLikeObject(objValue)) { 1286 | newValue = copyArray(objValue); 1287 | } 1288 | else { 1289 | isCommon = false; 1290 | newValue = baseClone(srcValue, true); 1291 | } 1292 | } 1293 | else if (isPlainObject(srcValue) || isArguments(srcValue)) { 1294 | if (isArguments(objValue)) { 1295 | newValue = toPlainObject(objValue); 1296 | } 1297 | else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) { 1298 | isCommon = false; 1299 | newValue = baseClone(srcValue, true); 1300 | } 1301 | else { 1302 | newValue = objValue; 1303 | } 1304 | } 1305 | else { 1306 | isCommon = false; 1307 | } 1308 | } 1309 | if (isCommon) { 1310 | // Recursively merge objects and arrays (susceptible to call stack limits). 1311 | stack.set(srcValue, newValue); 1312 | mergeFunc(newValue, srcValue, srcIndex, customizer, stack); 1313 | stack['delete'](srcValue); 1314 | } 1315 | assignMergeValue(object, key, newValue); 1316 | } 1317 | 1318 | /** 1319 | * The base implementation of `_.rest` which doesn't validate or coerce arguments. 1320 | * 1321 | * @private 1322 | * @param {Function} func The function to apply a rest parameter to. 1323 | * @param {number} [start=func.length-1] The start position of the rest parameter. 1324 | * @returns {Function} Returns the new function. 1325 | */ 1326 | function baseRest(func, start) { 1327 | start = nativeMax(start === undefined ? (func.length - 1) : start, 0); 1328 | return function() { 1329 | var args = arguments, 1330 | index = -1, 1331 | length = nativeMax(args.length - start, 0), 1332 | array = Array(length); 1333 | 1334 | while (++index < length) { 1335 | array[index] = args[start + index]; 1336 | } 1337 | index = -1; 1338 | var otherArgs = Array(start + 1); 1339 | while (++index < start) { 1340 | otherArgs[index] = args[index]; 1341 | } 1342 | otherArgs[start] = array; 1343 | return apply(func, this, otherArgs); 1344 | }; 1345 | } 1346 | 1347 | /** 1348 | * Creates a clone of `buffer`. 1349 | * 1350 | * @private 1351 | * @param {Buffer} buffer The buffer to clone. 1352 | * @param {boolean} [isDeep] Specify a deep clone. 1353 | * @returns {Buffer} Returns the cloned buffer. 1354 | */ 1355 | function cloneBuffer(buffer, isDeep) { 1356 | if (isDeep) { 1357 | return buffer.slice(); 1358 | } 1359 | var result = new buffer.constructor(buffer.length); 1360 | buffer.copy(result); 1361 | return result; 1362 | } 1363 | 1364 | /** 1365 | * Creates a clone of `arrayBuffer`. 1366 | * 1367 | * @private 1368 | * @param {ArrayBuffer} arrayBuffer The array buffer to clone. 1369 | * @returns {ArrayBuffer} Returns the cloned array buffer. 1370 | */ 1371 | function cloneArrayBuffer(arrayBuffer) { 1372 | var result = new arrayBuffer.constructor(arrayBuffer.byteLength); 1373 | new Uint8Array(result).set(new Uint8Array(arrayBuffer)); 1374 | return result; 1375 | } 1376 | 1377 | /** 1378 | * Creates a clone of `dataView`. 1379 | * 1380 | * @private 1381 | * @param {Object} dataView The data view to clone. 1382 | * @param {boolean} [isDeep] Specify a deep clone. 1383 | * @returns {Object} Returns the cloned data view. 1384 | */ 1385 | function cloneDataView(dataView, isDeep) { 1386 | var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; 1387 | return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); 1388 | } 1389 | 1390 | /** 1391 | * Creates a clone of `map`. 1392 | * 1393 | * @private 1394 | * @param {Object} map The map to clone. 1395 | * @param {Function} cloneFunc The function to clone values. 1396 | * @param {boolean} [isDeep] Specify a deep clone. 1397 | * @returns {Object} Returns the cloned map. 1398 | */ 1399 | function cloneMap(map, isDeep, cloneFunc) { 1400 | var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map); 1401 | return arrayReduce(array, addMapEntry, new map.constructor); 1402 | } 1403 | 1404 | /** 1405 | * Creates a clone of `regexp`. 1406 | * 1407 | * @private 1408 | * @param {Object} regexp The regexp to clone. 1409 | * @returns {Object} Returns the cloned regexp. 1410 | */ 1411 | function cloneRegExp(regexp) { 1412 | var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); 1413 | result.lastIndex = regexp.lastIndex; 1414 | return result; 1415 | } 1416 | 1417 | /** 1418 | * Creates a clone of `set`. 1419 | * 1420 | * @private 1421 | * @param {Object} set The set to clone. 1422 | * @param {Function} cloneFunc The function to clone values. 1423 | * @param {boolean} [isDeep] Specify a deep clone. 1424 | * @returns {Object} Returns the cloned set. 1425 | */ 1426 | function cloneSet(set, isDeep, cloneFunc) { 1427 | var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set); 1428 | return arrayReduce(array, addSetEntry, new set.constructor); 1429 | } 1430 | 1431 | /** 1432 | * Creates a clone of the `symbol` object. 1433 | * 1434 | * @private 1435 | * @param {Object} symbol The symbol object to clone. 1436 | * @returns {Object} Returns the cloned symbol object. 1437 | */ 1438 | function cloneSymbol(symbol) { 1439 | return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; 1440 | } 1441 | 1442 | /** 1443 | * Creates a clone of `typedArray`. 1444 | * 1445 | * @private 1446 | * @param {Object} typedArray The typed array to clone. 1447 | * @param {boolean} [isDeep] Specify a deep clone. 1448 | * @returns {Object} Returns the cloned typed array. 1449 | */ 1450 | function cloneTypedArray(typedArray, isDeep) { 1451 | var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; 1452 | return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); 1453 | } 1454 | 1455 | /** 1456 | * Copies the values of `source` to `array`. 1457 | * 1458 | * @private 1459 | * @param {Array} source The array to copy values from. 1460 | * @param {Array} [array=[]] The array to copy values to. 1461 | * @returns {Array} Returns `array`. 1462 | */ 1463 | function copyArray(source, array) { 1464 | var index = -1, 1465 | length = source.length; 1466 | 1467 | array || (array = Array(length)); 1468 | while (++index < length) { 1469 | array[index] = source[index]; 1470 | } 1471 | return array; 1472 | } 1473 | 1474 | /** 1475 | * Copies properties of `source` to `object`. 1476 | * 1477 | * @private 1478 | * @param {Object} source The object to copy properties from. 1479 | * @param {Array} props The property identifiers to copy. 1480 | * @param {Object} [object={}] The object to copy properties to. 1481 | * @param {Function} [customizer] The function to customize copied values. 1482 | * @returns {Object} Returns `object`. 1483 | */ 1484 | function copyObject(source, props, object, customizer) { 1485 | object || (object = {}); 1486 | 1487 | var index = -1, 1488 | length = props.length; 1489 | 1490 | while (++index < length) { 1491 | var key = props[index]; 1492 | 1493 | var newValue = customizer 1494 | ? customizer(object[key], source[key], key, object, source) 1495 | : undefined; 1496 | 1497 | assignValue(object, key, newValue === undefined ? source[key] : newValue); 1498 | } 1499 | return object; 1500 | } 1501 | 1502 | /** 1503 | * Copies own symbol properties of `source` to `object`. 1504 | * 1505 | * @private 1506 | * @param {Object} source The object to copy symbols from. 1507 | * @param {Object} [object={}] The object to copy symbols to. 1508 | * @returns {Object} Returns `object`. 1509 | */ 1510 | function copySymbols(source, object) { 1511 | return copyObject(source, getSymbols(source), object); 1512 | } 1513 | 1514 | /** 1515 | * Creates a function like `_.assign`. 1516 | * 1517 | * @private 1518 | * @param {Function} assigner The function to assign values. 1519 | * @returns {Function} Returns the new assigner function. 1520 | */ 1521 | function createAssigner(assigner) { 1522 | return baseRest(function(object, sources) { 1523 | var index = -1, 1524 | length = sources.length, 1525 | customizer = length > 1 ? sources[length - 1] : undefined, 1526 | guard = length > 2 ? sources[2] : undefined; 1527 | 1528 | customizer = (assigner.length > 3 && typeof customizer == 'function') 1529 | ? (length--, customizer) 1530 | : undefined; 1531 | 1532 | if (guard && isIterateeCall(sources[0], sources[1], guard)) { 1533 | customizer = length < 3 ? undefined : customizer; 1534 | length = 1; 1535 | } 1536 | object = Object(object); 1537 | while (++index < length) { 1538 | var source = sources[index]; 1539 | if (source) { 1540 | assigner(object, source, index, customizer); 1541 | } 1542 | } 1543 | return object; 1544 | }); 1545 | } 1546 | 1547 | /** 1548 | * Creates an array of own enumerable property names and symbols of `object`. 1549 | * 1550 | * @private 1551 | * @param {Object} object The object to query. 1552 | * @returns {Array} Returns the array of property names and symbols. 1553 | */ 1554 | function getAllKeys(object) { 1555 | return baseGetAllKeys(object, keys, getSymbols); 1556 | } 1557 | 1558 | /** 1559 | * Gets the "length" property value of `object`. 1560 | * 1561 | * **Note:** This function is used to avoid a 1562 | * [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) that affects 1563 | * Safari on at least iOS 8.1-8.3 ARM64. 1564 | * 1565 | * @private 1566 | * @param {Object} object The object to query. 1567 | * @returns {*} Returns the "length" value. 1568 | */ 1569 | var getLength = baseProperty('length'); 1570 | 1571 | /** 1572 | * Gets the data for `map`. 1573 | * 1574 | * @private 1575 | * @param {Object} map The map to query. 1576 | * @param {string} key The reference key. 1577 | * @returns {*} Returns the map data. 1578 | */ 1579 | function getMapData(map, key) { 1580 | var data = map.__data__; 1581 | return isKeyable(key) 1582 | ? data[typeof key == 'string' ? 'string' : 'hash'] 1583 | : data.map; 1584 | } 1585 | 1586 | /** 1587 | * Gets the native function at `key` of `object`. 1588 | * 1589 | * @private 1590 | * @param {Object} object The object to query. 1591 | * @param {string} key The key of the method to get. 1592 | * @returns {*} Returns the function if it's native, else `undefined`. 1593 | */ 1594 | function getNative(object, key) { 1595 | var value = getValue(object, key); 1596 | return baseIsNative(value) ? value : undefined; 1597 | } 1598 | 1599 | /** 1600 | * Gets the `[[Prototype]]` of `value`. 1601 | * 1602 | * @private 1603 | * @param {*} value The value to query. 1604 | * @returns {null|Object} Returns the `[[Prototype]]`. 1605 | */ 1606 | var getPrototype = overArg(nativeGetPrototype, Object); 1607 | 1608 | /** 1609 | * Creates an array of the own enumerable symbol properties of `object`. 1610 | * 1611 | * @private 1612 | * @param {Object} object The object to query. 1613 | * @returns {Array} Returns the array of symbols. 1614 | */ 1615 | var getSymbols = nativeGetSymbols ? overArg(nativeGetSymbols, Object) : stubArray; 1616 | 1617 | /** 1618 | * Gets the `toStringTag` of `value`. 1619 | * 1620 | * @private 1621 | * @param {*} value The value to query. 1622 | * @returns {string} Returns the `toStringTag`. 1623 | */ 1624 | var getTag = baseGetTag; 1625 | 1626 | // Fallback for data views, maps, sets, and weak maps in IE 11, 1627 | // for data views in Edge, and promises in Node.js. 1628 | if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || 1629 | (Map && getTag(new Map) != mapTag) || 1630 | (Promise && getTag(Promise.resolve()) != promiseTag) || 1631 | (Set && getTag(new Set) != setTag) || 1632 | (WeakMap && getTag(new WeakMap) != weakMapTag)) { 1633 | getTag = function(value) { 1634 | var result = objectToString.call(value), 1635 | Ctor = result == objectTag ? value.constructor : undefined, 1636 | ctorString = Ctor ? toSource(Ctor) : undefined; 1637 | 1638 | if (ctorString) { 1639 | switch (ctorString) { 1640 | case dataViewCtorString: return dataViewTag; 1641 | case mapCtorString: return mapTag; 1642 | case promiseCtorString: return promiseTag; 1643 | case setCtorString: return setTag; 1644 | case weakMapCtorString: return weakMapTag; 1645 | } 1646 | } 1647 | return result; 1648 | }; 1649 | } 1650 | 1651 | /** 1652 | * Initializes an array clone. 1653 | * 1654 | * @private 1655 | * @param {Array} array The array to clone. 1656 | * @returns {Array} Returns the initialized clone. 1657 | */ 1658 | function initCloneArray(array) { 1659 | var length = array.length, 1660 | result = array.constructor(length); 1661 | 1662 | // Add properties assigned by `RegExp#exec`. 1663 | if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { 1664 | result.index = array.index; 1665 | result.input = array.input; 1666 | } 1667 | return result; 1668 | } 1669 | 1670 | /** 1671 | * Initializes an object clone. 1672 | * 1673 | * @private 1674 | * @param {Object} object The object to clone. 1675 | * @returns {Object} Returns the initialized clone. 1676 | */ 1677 | function initCloneObject(object) { 1678 | return (typeof object.constructor == 'function' && !isPrototype(object)) 1679 | ? baseCreate(getPrototype(object)) 1680 | : {}; 1681 | } 1682 | 1683 | /** 1684 | * Initializes an object clone based on its `toStringTag`. 1685 | * 1686 | * **Note:** This function only supports cloning values with tags of 1687 | * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. 1688 | * 1689 | * @private 1690 | * @param {Object} object The object to clone. 1691 | * @param {string} tag The `toStringTag` of the object to clone. 1692 | * @param {Function} cloneFunc The function to clone values. 1693 | * @param {boolean} [isDeep] Specify a deep clone. 1694 | * @returns {Object} Returns the initialized clone. 1695 | */ 1696 | function initCloneByTag(object, tag, cloneFunc, isDeep) { 1697 | var Ctor = object.constructor; 1698 | switch (tag) { 1699 | case arrayBufferTag: 1700 | return cloneArrayBuffer(object); 1701 | 1702 | case boolTag: 1703 | case dateTag: 1704 | return new Ctor(+object); 1705 | 1706 | case dataViewTag: 1707 | return cloneDataView(object, isDeep); 1708 | 1709 | case float32Tag: case float64Tag: 1710 | case int8Tag: case int16Tag: case int32Tag: 1711 | case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: 1712 | return cloneTypedArray(object, isDeep); 1713 | 1714 | case mapTag: 1715 | return cloneMap(object, isDeep, cloneFunc); 1716 | 1717 | case numberTag: 1718 | case stringTag: 1719 | return new Ctor(object); 1720 | 1721 | case regexpTag: 1722 | return cloneRegExp(object); 1723 | 1724 | case setTag: 1725 | return cloneSet(object, isDeep, cloneFunc); 1726 | 1727 | case symbolTag: 1728 | return cloneSymbol(object); 1729 | } 1730 | } 1731 | 1732 | /** 1733 | * Creates an array of index keys for `object` values of arrays, 1734 | * `arguments` objects, and strings, otherwise `null` is returned. 1735 | * 1736 | * @private 1737 | * @param {Object} object The object to query. 1738 | * @returns {Array|null} Returns index keys, else `null`. 1739 | */ 1740 | function indexKeys(object) { 1741 | var length = object ? object.length : undefined; 1742 | if (isLength(length) && 1743 | (isArray(object) || isString(object) || isArguments(object))) { 1744 | return baseTimes(length, String); 1745 | } 1746 | return null; 1747 | } 1748 | 1749 | /** 1750 | * Checks if `value` is a valid array-like index. 1751 | * 1752 | * @private 1753 | * @param {*} value The value to check. 1754 | * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. 1755 | * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. 1756 | */ 1757 | function isIndex(value, length) { 1758 | length = length == null ? MAX_SAFE_INTEGER : length; 1759 | return !!length && 1760 | (typeof value == 'number' || reIsUint.test(value)) && 1761 | (value > -1 && value % 1 == 0 && value < length); 1762 | } 1763 | 1764 | /** 1765 | * Checks if the given arguments are from an iteratee call. 1766 | * 1767 | * @private 1768 | * @param {*} value The potential iteratee value argument. 1769 | * @param {*} index The potential iteratee index or key argument. 1770 | * @param {*} object The potential iteratee object argument. 1771 | * @returns {boolean} Returns `true` if the arguments are from an iteratee call, 1772 | * else `false`. 1773 | */ 1774 | function isIterateeCall(value, index, object) { 1775 | if (!isObject(object)) { 1776 | return false; 1777 | } 1778 | var type = typeof index; 1779 | if (type == 'number' 1780 | ? (isArrayLike(object) && isIndex(index, object.length)) 1781 | : (type == 'string' && index in object) 1782 | ) { 1783 | return eq(object[index], value); 1784 | } 1785 | return false; 1786 | } 1787 | 1788 | /** 1789 | * Checks if `value` is suitable for use as unique object key. 1790 | * 1791 | * @private 1792 | * @param {*} value The value to check. 1793 | * @returns {boolean} Returns `true` if `value` is suitable, else `false`. 1794 | */ 1795 | function isKeyable(value) { 1796 | var type = typeof value; 1797 | return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') 1798 | ? (value !== '__proto__') 1799 | : (value === null); 1800 | } 1801 | 1802 | /** 1803 | * Checks if `func` has its source masked. 1804 | * 1805 | * @private 1806 | * @param {Function} func The function to check. 1807 | * @returns {boolean} Returns `true` if `func` is masked, else `false`. 1808 | */ 1809 | function isMasked(func) { 1810 | return !!maskSrcKey && (maskSrcKey in func); 1811 | } 1812 | 1813 | /** 1814 | * Checks if `value` is likely a prototype object. 1815 | * 1816 | * @private 1817 | * @param {*} value The value to check. 1818 | * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. 1819 | */ 1820 | function isPrototype(value) { 1821 | var Ctor = value && value.constructor, 1822 | proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; 1823 | 1824 | return value === proto; 1825 | } 1826 | 1827 | /** 1828 | * Converts `func` to its source code. 1829 | * 1830 | * @private 1831 | * @param {Function} func The function to process. 1832 | * @returns {string} Returns the source code. 1833 | */ 1834 | function toSource(func) { 1835 | if (func != null) { 1836 | try { 1837 | return funcToString.call(func); 1838 | } catch (e) {} 1839 | try { 1840 | return (func + ''); 1841 | } catch (e) {} 1842 | } 1843 | return ''; 1844 | } 1845 | 1846 | /** 1847 | * Performs a 1848 | * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) 1849 | * comparison between two values to determine if they are equivalent. 1850 | * 1851 | * @static 1852 | * @memberOf _ 1853 | * @since 4.0.0 1854 | * @category Lang 1855 | * @param {*} value The value to compare. 1856 | * @param {*} other The other value to compare. 1857 | * @returns {boolean} Returns `true` if the values are equivalent, else `false`. 1858 | * @example 1859 | * 1860 | * var object = { 'a': 1 }; 1861 | * var other = { 'a': 1 }; 1862 | * 1863 | * _.eq(object, object); 1864 | * // => true 1865 | * 1866 | * _.eq(object, other); 1867 | * // => false 1868 | * 1869 | * _.eq('a', 'a'); 1870 | * // => true 1871 | * 1872 | * _.eq('a', Object('a')); 1873 | * // => false 1874 | * 1875 | * _.eq(NaN, NaN); 1876 | * // => true 1877 | */ 1878 | function eq(value, other) { 1879 | return value === other || (value !== value && other !== other); 1880 | } 1881 | 1882 | /** 1883 | * Checks if `value` is likely an `arguments` object. 1884 | * 1885 | * @static 1886 | * @memberOf _ 1887 | * @since 0.1.0 1888 | * @category Lang 1889 | * @param {*} value The value to check. 1890 | * @returns {boolean} Returns `true` if `value` is an `arguments` object, 1891 | * else `false`. 1892 | * @example 1893 | * 1894 | * _.isArguments(function() { return arguments; }()); 1895 | * // => true 1896 | * 1897 | * _.isArguments([1, 2, 3]); 1898 | * // => false 1899 | */ 1900 | function isArguments(value) { 1901 | // Safari 8.1 incorrectly makes `arguments.callee` enumerable in strict mode. 1902 | return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') && 1903 | (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag); 1904 | } 1905 | 1906 | /** 1907 | * Checks if `value` is classified as an `Array` object. 1908 | * 1909 | * @static 1910 | * @memberOf _ 1911 | * @since 0.1.0 1912 | * @category Lang 1913 | * @param {*} value The value to check. 1914 | * @returns {boolean} Returns `true` if `value` is an array, else `false`. 1915 | * @example 1916 | * 1917 | * _.isArray([1, 2, 3]); 1918 | * // => true 1919 | * 1920 | * _.isArray(document.body.children); 1921 | * // => false 1922 | * 1923 | * _.isArray('abc'); 1924 | * // => false 1925 | * 1926 | * _.isArray(_.noop); 1927 | * // => false 1928 | */ 1929 | var isArray = Array.isArray; 1930 | 1931 | /** 1932 | * Checks if `value` is array-like. A value is considered array-like if it's 1933 | * not a function and has a `value.length` that's an integer greater than or 1934 | * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. 1935 | * 1936 | * @static 1937 | * @memberOf _ 1938 | * @since 4.0.0 1939 | * @category Lang 1940 | * @param {*} value The value to check. 1941 | * @returns {boolean} Returns `true` if `value` is array-like, else `false`. 1942 | * @example 1943 | * 1944 | * _.isArrayLike([1, 2, 3]); 1945 | * // => true 1946 | * 1947 | * _.isArrayLike(document.body.children); 1948 | * // => true 1949 | * 1950 | * _.isArrayLike('abc'); 1951 | * // => true 1952 | * 1953 | * _.isArrayLike(_.noop); 1954 | * // => false 1955 | */ 1956 | function isArrayLike(value) { 1957 | return value != null && isLength(getLength(value)) && !isFunction(value); 1958 | } 1959 | 1960 | /** 1961 | * This method is like `_.isArrayLike` except that it also checks if `value` 1962 | * is an object. 1963 | * 1964 | * @static 1965 | * @memberOf _ 1966 | * @since 4.0.0 1967 | * @category Lang 1968 | * @param {*} value The value to check. 1969 | * @returns {boolean} Returns `true` if `value` is an array-like object, 1970 | * else `false`. 1971 | * @example 1972 | * 1973 | * _.isArrayLikeObject([1, 2, 3]); 1974 | * // => true 1975 | * 1976 | * _.isArrayLikeObject(document.body.children); 1977 | * // => true 1978 | * 1979 | * _.isArrayLikeObject('abc'); 1980 | * // => false 1981 | * 1982 | * _.isArrayLikeObject(_.noop); 1983 | * // => false 1984 | */ 1985 | function isArrayLikeObject(value) { 1986 | return isObjectLike(value) && isArrayLike(value); 1987 | } 1988 | 1989 | /** 1990 | * Checks if `value` is a buffer. 1991 | * 1992 | * @static 1993 | * @memberOf _ 1994 | * @since 4.3.0 1995 | * @category Lang 1996 | * @param {*} value The value to check. 1997 | * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. 1998 | * @example 1999 | * 2000 | * _.isBuffer(new Buffer(2)); 2001 | * // => true 2002 | * 2003 | * _.isBuffer(new Uint8Array(2)); 2004 | * // => false 2005 | */ 2006 | var isBuffer = nativeIsBuffer || stubFalse; 2007 | 2008 | /** 2009 | * Checks if `value` is classified as a `Function` object. 2010 | * 2011 | * @static 2012 | * @memberOf _ 2013 | * @since 0.1.0 2014 | * @category Lang 2015 | * @param {*} value The value to check. 2016 | * @returns {boolean} Returns `true` if `value` is a function, else `false`. 2017 | * @example 2018 | * 2019 | * _.isFunction(_); 2020 | * // => true 2021 | * 2022 | * _.isFunction(/abc/); 2023 | * // => false 2024 | */ 2025 | function isFunction(value) { 2026 | // The use of `Object#toString` avoids issues with the `typeof` operator 2027 | // in Safari 8 which returns 'object' for typed array and weak map constructors, 2028 | // and PhantomJS 1.9 which returns 'function' for `NodeList` instances. 2029 | var tag = isObject(value) ? objectToString.call(value) : ''; 2030 | return tag == funcTag || tag == genTag; 2031 | } 2032 | 2033 | /** 2034 | * Checks if `value` is a valid array-like length. 2035 | * 2036 | * **Note:** This function is loosely based on 2037 | * [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength). 2038 | * 2039 | * @static 2040 | * @memberOf _ 2041 | * @since 4.0.0 2042 | * @category Lang 2043 | * @param {*} value The value to check. 2044 | * @returns {boolean} Returns `true` if `value` is a valid length, 2045 | * else `false`. 2046 | * @example 2047 | * 2048 | * _.isLength(3); 2049 | * // => true 2050 | * 2051 | * _.isLength(Number.MIN_VALUE); 2052 | * // => false 2053 | * 2054 | * _.isLength(Infinity); 2055 | * // => false 2056 | * 2057 | * _.isLength('3'); 2058 | * // => false 2059 | */ 2060 | function isLength(value) { 2061 | return typeof value == 'number' && 2062 | value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; 2063 | } 2064 | 2065 | /** 2066 | * Checks if `value` is the 2067 | * [language type](http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-types) 2068 | * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) 2069 | * 2070 | * @static 2071 | * @memberOf _ 2072 | * @since 0.1.0 2073 | * @category Lang 2074 | * @param {*} value The value to check. 2075 | * @returns {boolean} Returns `true` if `value` is an object, else `false`. 2076 | * @example 2077 | * 2078 | * _.isObject({}); 2079 | * // => true 2080 | * 2081 | * _.isObject([1, 2, 3]); 2082 | * // => true 2083 | * 2084 | * _.isObject(_.noop); 2085 | * // => true 2086 | * 2087 | * _.isObject(null); 2088 | * // => false 2089 | */ 2090 | function isObject(value) { 2091 | var type = typeof value; 2092 | return !!value && (type == 'object' || type == 'function'); 2093 | } 2094 | 2095 | /** 2096 | * Checks if `value` is object-like. A value is object-like if it's not `null` 2097 | * and has a `typeof` result of "object". 2098 | * 2099 | * @static 2100 | * @memberOf _ 2101 | * @since 4.0.0 2102 | * @category Lang 2103 | * @param {*} value The value to check. 2104 | * @returns {boolean} Returns `true` if `value` is object-like, else `false`. 2105 | * @example 2106 | * 2107 | * _.isObjectLike({}); 2108 | * // => true 2109 | * 2110 | * _.isObjectLike([1, 2, 3]); 2111 | * // => true 2112 | * 2113 | * _.isObjectLike(_.noop); 2114 | * // => false 2115 | * 2116 | * _.isObjectLike(null); 2117 | * // => false 2118 | */ 2119 | function isObjectLike(value) { 2120 | return !!value && typeof value == 'object'; 2121 | } 2122 | 2123 | /** 2124 | * Checks if `value` is a plain object, that is, an object created by the 2125 | * `Object` constructor or one with a `[[Prototype]]` of `null`. 2126 | * 2127 | * @static 2128 | * @memberOf _ 2129 | * @since 0.8.0 2130 | * @category Lang 2131 | * @param {*} value The value to check. 2132 | * @returns {boolean} Returns `true` if `value` is a plain object, 2133 | * else `false`. 2134 | * @example 2135 | * 2136 | * function Foo() { 2137 | * this.a = 1; 2138 | * } 2139 | * 2140 | * _.isPlainObject(new Foo); 2141 | * // => false 2142 | * 2143 | * _.isPlainObject([1, 2, 3]); 2144 | * // => false 2145 | * 2146 | * _.isPlainObject({ 'x': 0, 'y': 0 }); 2147 | * // => true 2148 | * 2149 | * _.isPlainObject(Object.create(null)); 2150 | * // => true 2151 | */ 2152 | function isPlainObject(value) { 2153 | if (!isObjectLike(value) || 2154 | objectToString.call(value) != objectTag || isHostObject(value)) { 2155 | return false; 2156 | } 2157 | var proto = getPrototype(value); 2158 | if (proto === null) { 2159 | return true; 2160 | } 2161 | var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; 2162 | return (typeof Ctor == 'function' && 2163 | Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString); 2164 | } 2165 | 2166 | /** 2167 | * Checks if `value` is classified as a `String` primitive or object. 2168 | * 2169 | * @static 2170 | * @since 0.1.0 2171 | * @memberOf _ 2172 | * @category Lang 2173 | * @param {*} value The value to check. 2174 | * @returns {boolean} Returns `true` if `value` is a string, else `false`. 2175 | * @example 2176 | * 2177 | * _.isString('abc'); 2178 | * // => true 2179 | * 2180 | * _.isString(1); 2181 | * // => false 2182 | */ 2183 | function isString(value) { 2184 | return typeof value == 'string' || 2185 | (!isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag); 2186 | } 2187 | 2188 | /** 2189 | * Checks if `value` is classified as a typed array. 2190 | * 2191 | * @static 2192 | * @memberOf _ 2193 | * @since 3.0.0 2194 | * @category Lang 2195 | * @param {*} value The value to check. 2196 | * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. 2197 | * @example 2198 | * 2199 | * _.isTypedArray(new Uint8Array); 2200 | * // => true 2201 | * 2202 | * _.isTypedArray([]); 2203 | * // => false 2204 | */ 2205 | var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; 2206 | 2207 | /** 2208 | * Converts `value` to a plain object flattening inherited enumerable string 2209 | * keyed properties of `value` to own properties of the plain object. 2210 | * 2211 | * @static 2212 | * @memberOf _ 2213 | * @since 3.0.0 2214 | * @category Lang 2215 | * @param {*} value The value to convert. 2216 | * @returns {Object} Returns the converted plain object. 2217 | * @example 2218 | * 2219 | * function Foo() { 2220 | * this.b = 2; 2221 | * } 2222 | * 2223 | * Foo.prototype.c = 3; 2224 | * 2225 | * _.assign({ 'a': 1 }, new Foo); 2226 | * // => { 'a': 1, 'b': 2 } 2227 | * 2228 | * _.assign({ 'a': 1 }, _.toPlainObject(new Foo)); 2229 | * // => { 'a': 1, 'b': 2, 'c': 3 } 2230 | */ 2231 | function toPlainObject(value) { 2232 | return copyObject(value, keysIn(value)); 2233 | } 2234 | 2235 | /** 2236 | * Creates an array of the own enumerable property names of `object`. 2237 | * 2238 | * **Note:** Non-object values are coerced to objects. See the 2239 | * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys) 2240 | * for more details. 2241 | * 2242 | * @static 2243 | * @since 0.1.0 2244 | * @memberOf _ 2245 | * @category Object 2246 | * @param {Object} object The object to query. 2247 | * @returns {Array} Returns the array of property names. 2248 | * @example 2249 | * 2250 | * function Foo() { 2251 | * this.a = 1; 2252 | * this.b = 2; 2253 | * } 2254 | * 2255 | * Foo.prototype.c = 3; 2256 | * 2257 | * _.keys(new Foo); 2258 | * // => ['a', 'b'] (iteration order is not guaranteed) 2259 | * 2260 | * _.keys('hi'); 2261 | * // => ['0', '1'] 2262 | */ 2263 | function keys(object) { 2264 | var isProto = isPrototype(object); 2265 | if (!(isProto || isArrayLike(object))) { 2266 | return baseKeys(object); 2267 | } 2268 | var indexes = indexKeys(object), 2269 | skipIndexes = !!indexes, 2270 | result = indexes || [], 2271 | length = result.length; 2272 | 2273 | for (var key in object) { 2274 | if (baseHas(object, key) && 2275 | !(skipIndexes && (key == 'length' || isIndex(key, length))) && 2276 | !(isProto && key == 'constructor')) { 2277 | result.push(key); 2278 | } 2279 | } 2280 | return result; 2281 | } 2282 | 2283 | /** 2284 | * Creates an array of the own and inherited enumerable property names of `object`. 2285 | * 2286 | * **Note:** Non-object values are coerced to objects. 2287 | * 2288 | * @static 2289 | * @memberOf _ 2290 | * @since 3.0.0 2291 | * @category Object 2292 | * @param {Object} object The object to query. 2293 | * @returns {Array} Returns the array of property names. 2294 | * @example 2295 | * 2296 | * function Foo() { 2297 | * this.a = 1; 2298 | * this.b = 2; 2299 | * } 2300 | * 2301 | * Foo.prototype.c = 3; 2302 | * 2303 | * _.keysIn(new Foo); 2304 | * // => ['a', 'b', 'c'] (iteration order is not guaranteed) 2305 | */ 2306 | function keysIn(object) { 2307 | var index = -1, 2308 | isProto = isPrototype(object), 2309 | props = baseKeysIn(object), 2310 | propsLength = props.length, 2311 | indexes = indexKeys(object), 2312 | skipIndexes = !!indexes, 2313 | result = indexes || [], 2314 | length = result.length; 2315 | 2316 | while (++index < propsLength) { 2317 | var key = props[index]; 2318 | if (!(skipIndexes && (key == 'length' || isIndex(key, length))) && 2319 | !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { 2320 | result.push(key); 2321 | } 2322 | } 2323 | return result; 2324 | } 2325 | 2326 | /** 2327 | * This method is like `_.assign` except that it recursively merges own and 2328 | * inherited enumerable string keyed properties of source objects into the 2329 | * destination object. Source properties that resolve to `undefined` are 2330 | * skipped if a destination value exists. Array and plain object properties 2331 | * are merged recursively. Other objects and value types are overridden by 2332 | * assignment. Source objects are applied from left to right. Subsequent 2333 | * sources overwrite property assignments of previous sources. 2334 | * 2335 | * **Note:** This method mutates `object`. 2336 | * 2337 | * @static 2338 | * @memberOf _ 2339 | * @since 0.5.0 2340 | * @category Object 2341 | * @param {Object} object The destination object. 2342 | * @param {...Object} [sources] The source objects. 2343 | * @returns {Object} Returns `object`. 2344 | * @example 2345 | * 2346 | * var object = { 2347 | * 'a': [{ 'b': 2 }, { 'd': 4 }] 2348 | * }; 2349 | * 2350 | * var other = { 2351 | * 'a': [{ 'c': 3 }, { 'e': 5 }] 2352 | * }; 2353 | * 2354 | * _.merge(object, other); 2355 | * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } 2356 | */ 2357 | var merge = createAssigner(function(object, source, srcIndex) { 2358 | baseMerge(object, source, srcIndex); 2359 | }); 2360 | 2361 | /** 2362 | * This method returns a new empty array. 2363 | * 2364 | * @static 2365 | * @memberOf _ 2366 | * @since 4.13.0 2367 | * @category Util 2368 | * @returns {Array} Returns the new empty array. 2369 | * @example 2370 | * 2371 | * var arrays = _.times(2, _.stubArray); 2372 | * 2373 | * console.log(arrays); 2374 | * // => [[], []] 2375 | * 2376 | * console.log(arrays[0] === arrays[1]); 2377 | * // => false 2378 | */ 2379 | function stubArray() { 2380 | return []; 2381 | } 2382 | 2383 | /** 2384 | * This method returns `false`. 2385 | * 2386 | * @static 2387 | * @memberOf _ 2388 | * @since 4.13.0 2389 | * @category Util 2390 | * @returns {boolean} Returns `false`. 2391 | * @example 2392 | * 2393 | * _.times(2, _.stubFalse); 2394 | * // => [false, false] 2395 | */ 2396 | function stubFalse() { 2397 | return false; 2398 | } 2399 | 2400 | module.exports = merge; 2401 | 2402 | /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(2), __webpack_require__(3)(module))) 2403 | 2404 | /***/ }), 2405 | /* 2 */ 2406 | /***/ (function(module, exports) { 2407 | 2408 | var g; 2409 | 2410 | // This works in non-strict mode 2411 | g = (function() { 2412 | return this; 2413 | })(); 2414 | 2415 | try { 2416 | // This works if eval is allowed (see CSP) 2417 | g = g || Function("return this")() || (1,eval)("this"); 2418 | } catch(e) { 2419 | // This works if the window reference is available 2420 | if(typeof window === "object") 2421 | g = window; 2422 | } 2423 | 2424 | // g can still be undefined, but nothing to do about it... 2425 | // We return undefined, instead of nothing here, so it's 2426 | // easier to handle this case. if(!global) { ...} 2427 | 2428 | module.exports = g; 2429 | 2430 | 2431 | /***/ }), 2432 | /* 3 */ 2433 | /***/ (function(module, exports) { 2434 | 2435 | module.exports = function(module) { 2436 | if(!module.webpackPolyfill) { 2437 | module.deprecate = function() {}; 2438 | module.paths = []; 2439 | // module.parent = undefined by default 2440 | if(!module.children) module.children = []; 2441 | Object.defineProperty(module, "loaded", { 2442 | enumerable: true, 2443 | get: function() { 2444 | return module.l; 2445 | } 2446 | }); 2447 | Object.defineProperty(module, "id", { 2448 | enumerable: true, 2449 | get: function() { 2450 | return module.i; 2451 | } 2452 | }); 2453 | module.webpackPolyfill = 1; 2454 | } 2455 | return module; 2456 | }; 2457 | 2458 | 2459 | /***/ }), 2460 | /* 4 */ 2461 | /***/ (function(module, exports, __webpack_require__) { 2462 | 2463 | module.exports = __webpack_require__(0); 2464 | 2465 | 2466 | /***/ }) 2467 | /******/ ]); 2468 | }); -------------------------------------------------------------------------------- /tests/qmljsifyunittests/samples/sprintf-0.1.5.min.js: -------------------------------------------------------------------------------- 1 | !function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define("QML",[],r):"object"==typeof exports?exports.QML=r():e.QML=r()}(this,function(){return function(e){function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}var t={};return r.m=e,r.c=t,r.i=function(e){return e},r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:n})},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},r.p="/",r(r.s=2)}([function(e,r,t){var n=t(1);e.exports=n},function(e,r){var t=function(){function e(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}function r(e,r){for(var t=[];r>0;t[--r]=e);return t.join("")}var n=function(){return n.cache.hasOwnProperty(arguments[0])||(n.cache[arguments[0]]=n.parse(arguments[0])),n.format.call(null,n.cache[arguments[0]],arguments)};return n.object_stringify=function(e,r,t,o){var i="";if(null!=e)switch(typeof e){case"function":return"[Function"+(e.name?": "+e.name:"")+"]";case"object":if(e instanceof Error)return"["+e.toString()+"]";if(r>=t)return"[Object]";if(o&&(o=o.slice(0),o.push(e)),null!=e.length){i+="[";var s=[];for(var a in e)o&&o.indexOf(e[a])>=0?s.push("[Circular]"):s.push(n.object_stringify(e[a],r+1,t,o));i+=s.join(", ")+"]"}else{if("getMonth"in e)return"Date("+e+")";i+="{";var s=[];for(var u in e)e.hasOwnProperty(u)&&(o&&o.indexOf(e[u])>=0?s.push(u+": [Circular]"):s.push(u+": "+n.object_stringify(e[u],r+1,t,o)));i+=s.join(", ")+"}"}return i;case"string":return'"'+e+'"'}return""+e},n.format=function(o,i){var s,a,u,f,c,p,l,h=1,b=o.length,x="",d=[];for(a=0;a=0?"+"+s:s,p=f[4]?"0"==f[4]?"0":f[4].charAt(1):" ",l=f[6]-String(s).length,c=f[6]?r(p,l):"",d.push(f[5]?s+c:c+s)}return d.join("")},n.cache={},n.parse=function(e){for(var r=e,t=[],n=[],o=0;r;){if(null!==(t=/^[^\x25]+/.exec(r)))n.push(t[0]);else if(null!==(t=/^\x25{2}/.exec(r)))n.push("%");else{if(null===(t=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosOuxX])/.exec(r)))throw new Error("[sprintf] "+r);if(t[2]){o|=1;var i=[],s=t[2],a=[];if(null===(a=/^([a-z_][a-z_\d]*)/i.exec(s)))throw new Error("[sprintf] "+s);for(i.push(a[1]);""!==(s=s.substring(a[0].length));)if(null!==(a=/^\.([a-z_][a-z_\d]*)/i.exec(s)))i.push(a[1]);else{if(null===(a=/^\[(\d+)\]/.exec(s)))throw new Error("[sprintf] "+s);i.push(a[1])}t[2]=i}else o|=2;if(3===o)throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported");n.push(t)}r=r.substring(t[0].length)}return n},n}(),n=function(e,r){var n=r.slice();return n.unshift(e),t.apply(null,n)};e.exports=t,t.sprintf=t,t.vsprintf=n},function(e,r,t){e.exports=t(0)}])}); -------------------------------------------------------------------------------- /tests/qmljsifyunittests/samples/sprintf.js: -------------------------------------------------------------------------------- 1 | .pragma library 2 | Qt.include("sprintf-0.1.5.min.js") 3 | var object_stringify = QML.object_stringify; 4 | var format = QML.format; 5 | var cache = QML.cache; 6 | var parse = QML.parse; 7 | var sprintf = QML.sprintf; 8 | var vsprintf = QML.vsprintf; 9 | -------------------------------------------------------------------------------- /tests/qmljsifyunittests/tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "tests.h" 8 | #include "qmljsify.h" 9 | 10 | using namespace QtShell; 11 | 12 | void printException(QJSValue value) 13 | { 14 | if (value.isError()) { 15 | QString message = QString("%1:%2: %3: %4") 16 | .arg(value.property("fileName").toString()) 17 | .arg(value.property("lineNumber").toString()) 18 | .arg(value.property("name").toString()) 19 | .arg(value.property("message").toString()); 20 | qWarning() << message; 21 | } 22 | } 23 | 24 | Tests::Tests(QObject *parent) : QObject(parent) 25 | { 26 | auto ref = [=]() { 27 | QTest::qExec(this, 0, 0); // Autotest detect available test cases of a QObject by looking for "QTest::qExec" in source code 28 | }; 29 | Q_UNUSED(ref); 30 | } 31 | 32 | void Tests::test_normalizeFunctionName() 33 | { 34 | QCOMPARE(Qmljsify::normalizeFunctionName("left-pad"), QString("leftPad")); 35 | QCOMPARE(Qmljsify::normalizeFunctionName("lodash.merge"), QString("lodashMerge")); 36 | } 37 | 38 | void Tests::test_prepare() 39 | { 40 | Qmljsify jsify; 41 | 42 | QString buildFolder = realpath_strip(pwd(), "build"); 43 | jsify.setBuildFolder(buildFolder); 44 | jsify.setPackage("lodash"); 45 | 46 | jsify.prepare(); 47 | 48 | QString index = realpath_strip(buildFolder, "index.js"); 49 | QString webpack = realpath_strip(buildFolder, "webpack.config.js"); 50 | 51 | QVERIFY(QFile::exists(index)); 52 | QVERIFY(QFile::exists(webpack)); 53 | 54 | QString content = cat(index); 55 | QVERIFY(content.indexOf("lodash") >= 0); 56 | } 57 | 58 | void Tests::test_create() 59 | { 60 | Qmljsify jsify; 61 | 62 | QString buildFolder = realpath_strip(pwd(), "build"); 63 | QString outputFolder = pwd(); 64 | 65 | QString origJs = realpath_strip(pwd() , "sprintf.orig.js"); 66 | 67 | QString package = "sprintf"; 68 | jsify.setMinifyEnabled(true); 69 | jsify.setOutputFolder(outputFolder); 70 | 71 | jsify.setBuildFolder(buildFolder); 72 | jsify.setPackage(package); 73 | 74 | jsify.prepare(); 75 | 76 | jsify.fetch(); 77 | 78 | jsify.build(); 79 | 80 | jsify.create(); 81 | 82 | QVERIFY(QFile::exists(origJs)); 83 | QVERIFY(cat(origJs).size() > 0); 84 | } 85 | 86 | void Tests::test_leftpad() 87 | { 88 | Qmljsify jsify; 89 | 90 | QString buildFolder = realpath_strip(pwd(), "build"); 91 | QString outputFolder = pwd(); 92 | 93 | QString js = realpath_strip(pwd() , "left-pad.js"); 94 | 95 | QString package = "left-pad"; 96 | 97 | jsify.setMinifyEnabled(false); 98 | jsify.setOutputFolder(outputFolder); 99 | 100 | jsify.setBuildFolder(buildFolder); 101 | jsify.setPackage(package); 102 | 103 | jsify.prepare(); 104 | 105 | jsify.fetch(); 106 | 107 | jsify.build(); 108 | 109 | jsify.create(); 110 | 111 | QVERIFY(cat(js).indexOf("var leftPad") >= 0); 112 | } 113 | 114 | void Tests::test_lodashMerge() 115 | { 116 | Qmljsify jsify; 117 | 118 | QString buildFolder = realpath_strip(pwd(), "build"); 119 | QString outputFolder = pwd(); 120 | 121 | QString origJs = realpath_strip(pwd() , "lodash.merge.orig.js"); 122 | QString js = realpath_strip(pwd() , "lodash.merge.js"); 123 | 124 | QString package = "lodash.merge@4.5.0"; 125 | 126 | jsify.setMinifyEnabled(false); 127 | jsify.setOutputFolder(outputFolder); 128 | 129 | jsify.setBuildFolder(buildFolder); 130 | jsify.parsePackageString(package); 131 | 132 | QCOMPARE(jsify.package(), QString("lodash.merge")); 133 | QCOMPARE(jsify.packageVersion(), QString("4.5.0")); 134 | 135 | jsify.prepare(); 136 | 137 | jsify.fetch(); 138 | 139 | jsify.build(); 140 | 141 | QVERIFY(jsify.validate(realpath_strip(buildFolder, "dist/bundle.js"))); 142 | 143 | jsify.create(); 144 | 145 | QVERIFY(cat(js).indexOf("var lodashMerge") >= 0); 146 | QVERIFY(cat(js).indexOf("4.5.0") >= 0); 147 | 148 | QVERIFY(cat(origJs).indexOf("4.5.0") >= 0); 149 | 150 | QCOMPARE(Qmljsify::queryPackageVersion(buildFolder, "lodash.merge") , QString("4.5.0")); 151 | 152 | cp("-v", origJs, realpath_strip(SRCDIR, "samples/")); 153 | cp("-v", js, realpath_strip(SRCDIR, "samples/")); 154 | } 155 | 156 | 157 | -------------------------------------------------------------------------------- /tests/qmljsifyunittests/tests.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class Tests : public QObject 5 | { 6 | Q_OBJECT 7 | public: 8 | explicit Tests(QObject *parent = 0); 9 | 10 | private slots: 11 | 12 | void test_normalizeFunctionName(); 13 | 14 | void test_prepare(); 15 | 16 | void test_create(); 17 | 18 | void test_leftpad(); 19 | 20 | void test_lodashMerge(); 21 | 22 | }; 23 | 24 | --------------------------------------------------------------------------------