Compare commits
No commits in common. "master" and "gh-pages" have entirely different histories.
@ -1,2 +0,0 @@
|
||||
src/vendor/
|
||||
node_modules/
|
67
.eslintrc
@ -1,67 +0,0 @@
|
||||
{
|
||||
"parser": "babel-eslint",
|
||||
"env": { "es6": true },
|
||||
"plugins": [ "html", "json", "react", "jsx" ],
|
||||
"extends": "eslint:recommended",
|
||||
"rules": {
|
||||
"comma-style": [ 2, "last" ],
|
||||
"comma-dangle": [ 2, "never" ],
|
||||
"curly": 0,
|
||||
"no-bitwise": 0,
|
||||
"no-console": 0,
|
||||
"no-control-regex": 0,
|
||||
"no-empty": 0,
|
||||
"no-trailing-spaces": 2,
|
||||
"no-use-before-define": [ 1, {
|
||||
"functions":false, "classes":true, "variables":false
|
||||
}],
|
||||
"no-useless-escape": 0,
|
||||
"semi": [ 2, "always" ],
|
||||
|
||||
"react/display-name": 0,
|
||||
"react/jsx-boolean-value": 2,
|
||||
"react/jsx-closing-bracket-location": 0,
|
||||
"react/jsx-curly-spacing": [2, "never"],
|
||||
"react/jsx-indent-props": 0,
|
||||
"react/jsx-max-props-per-line": 0,
|
||||
"react/jsx-no-duplicate-props": 2,
|
||||
"react/jsx-no-literals": 0,
|
||||
"react/jsx-no-undef": 2,
|
||||
"react/jsx-quotes": 0,
|
||||
"react/jsx-sort-prop-types": 0,
|
||||
"react/jsx-sort-props": 0,
|
||||
"react/jsx-uses-react": 2,
|
||||
"react/jsx-uses-vars": 2,
|
||||
"react/no-danger": 0,
|
||||
"react/no-did-mount-set-state": 0,
|
||||
"react/no-did-update-set-state": 2,
|
||||
"react/no-direct-mutation-state": 2,
|
||||
"react/no-multi-comp": 0,
|
||||
"react/no-set-state": 0,
|
||||
"react/no-unknown-property": 2,
|
||||
"react/react-in-jsx-scope": 2,
|
||||
"react/require-extension": 0,
|
||||
"react/self-closing-comp": 2,
|
||||
"react/jsx-wrap-multilines": 2,
|
||||
"react/sort-comp": [2, {
|
||||
"order": [
|
||||
"mixins",
|
||||
"displayName",
|
||||
"defaultProps",
|
||||
"constructor",
|
||||
"getDefaultProps",
|
||||
"getInitialState",
|
||||
"getChildContext",
|
||||
"componentWillMount",
|
||||
"componentDidMount",
|
||||
"componentWillReceiveProps",
|
||||
"shouldComponentUpdate",
|
||||
"componentWillUpdate",
|
||||
"componentDidUpdate",
|
||||
"componentWillUnmount",
|
||||
"everything-else",
|
||||
"render"
|
||||
]
|
||||
}]
|
||||
}
|
||||
}
|
10
.gitignore
vendored
@ -1,8 +1,6 @@
|
||||
/coverage
|
||||
/dist
|
||||
/node_modules
|
||||
npm-debug.log*
|
||||
package-lock.json
|
||||
dist/
|
||||
node_modules/
|
||||
yarn.lock
|
||||
*.foo
|
||||
package-lock.json
|
||||
*.map
|
||||
*.foo
|
||||
|
9
.vscode/settings.json
vendored
@ -1,9 +0,0 @@
|
||||
{
|
||||
"search.exclude": {
|
||||
"node_modules/*": true,
|
||||
"dist/*": true,
|
||||
"src/vendor/*": true,
|
||||
"*.map": true,
|
||||
"*.foo": true
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
## Prerequisites
|
||||
|
||||
[Node.js](http://nodejs.org/) >= v4 must be installed.
|
||||
|
||||
## Installation
|
||||
|
||||
- Running `npm install` in the app's root directory will install everything you need for development.
|
||||
|
||||
## Development Server
|
||||
|
||||
- `npm start` will run the app's development server at [http://localhost:3000](http://localhost:3000) with hot module reloading.
|
||||
|
||||
## Running Tests
|
||||
|
||||
- `npm test` will run the tests once.
|
||||
|
||||
- `npm run test:coverage` will run the tests and produce a coverage report in `coverage/`.
|
||||
|
||||
- `npm run test:watch` will run the tests on every change.
|
||||
|
||||
## Building
|
||||
|
||||
- `npm run build` creates a production build by default.
|
||||
|
||||
To create a development build, set the `NODE_ENV` environment variable to `development` while running this command.
|
||||
|
||||
- `npm run clean` will delete built resources.
|
Before Width: | Height: | Size: 731 KiB After Width: | Height: | Size: 731 KiB |
Before Width: | Height: | Size: 788 KiB After Width: | Height: | Size: 788 KiB |
Before Width: | Height: | Size: 1011 KiB After Width: | Height: | Size: 1011 KiB |
Before Width: | Height: | Size: 811 KiB After Width: | Height: | Size: 811 KiB |
Before Width: | Height: | Size: 777 KiB After Width: | Height: | Size: 777 KiB |
201
LICENSE
@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (C) 2017-present SheetJS LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
18
Makefile
@ -1,12 +1,12 @@
|
||||
.PHONY: dist
|
||||
dist:
|
||||
rm -f dist/*.{js,css,html,map}
|
||||
npm run build
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
npm run lint
|
||||
.PHONY: init
|
||||
init:
|
||||
git rm -f *.gif logo.{png,svg} app.*.{js,css} fontawesome-webfont.* index.html styles.css vendor.*.{css,js} || echo
|
||||
cp dist/* .
|
||||
sed -i.foo 's#href="/#href="#g; s#src="/#src="#g' index.html
|
||||
sed -i.foo 's#url(/font#url(font#g' *.css
|
||||
git add *.gif logo.{png,svg} app.*.{js,css} fontawesome-webfont.* index.html styles.css vendor.*.{css,js}
|
||||
@make clean
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.map *.foo
|
||||
rm -f *.foo *.map
|
||||
|
Before Width: | Height: | Size: 683 KiB After Width: | Height: | Size: 683 KiB |
@ -1,5 +0,0 @@
|
||||
# cfb-editor
|
||||
|
||||
Archive (ZIP/CFB/MAD) Editor
|
||||
|
||||
See <https://oss.sheetjs.com/cfb-editor/> for a live instance
|
Before Width: | Height: | Size: 751 KiB After Width: | Height: | Size: 751 KiB |
Before Width: | Height: | Size: 947 KiB After Width: | Height: | Size: 947 KiB |
Before Width: | Height: | Size: 727 KiB After Width: | Height: | Size: 727 KiB |
Before Width: | Height: | Size: 847 KiB After Width: | Height: | Size: 847 KiB |
Before Width: | Height: | Size: 775 KiB After Width: | Height: | Size: 775 KiB |
14
app.b9a31e1c.js
Normal file
172
app.bdcabfb2.css
Normal file
@ -0,0 +1,172 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
html, body, #app {
|
||||
height: 100%;
|
||||
}
|
||||
html {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: auto;
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
.Outer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.Bottom {
|
||||
height: calc(100% - 55px);
|
||||
}
|
||||
.RightPane {
|
||||
height: calc(100vh - 55px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
.ToolBar {
|
||||
height: 55px;
|
||||
line-height: 55px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
padding: 0px 20px;
|
||||
/* display: flex;
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex: 0 0 55px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid #000000;
|
||||
width: 100%;*/
|
||||
}
|
||||
|
||||
.ToolBar {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
-webkit-box-pack: justify;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.Logo {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.TitleIcons {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.TitleIcons > div {
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
.TitleFile {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.TitleName {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-pack: justify;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.AlignLeft {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.AlignRight {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.AlignCenter {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
a { color: #0000EE; }
|
||||
|
||||
.Spinner {
|
||||
height: 30px;
|
||||
}
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
.tree {
|
||||
padding: 0px 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
white-space: pre;
|
||||
height: calc(100vh - 55px);
|
||||
}
|
||||
.tree ul {
|
||||
padding: 0px;
|
||||
}
|
||||
.tree li {
|
||||
list-style-type:none;
|
||||
margin:0;
|
||||
position:relative;
|
||||
font-size: 12px;
|
||||
}
|
||||
.tree a {
|
||||
padding:10px 5px 0 0px;
|
||||
display:block;
|
||||
}
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
.conpainer {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
padding: 0px 20px;
|
||||
}
|
||||
|
||||
.flexrow {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: normal;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
-webkit-box-pack: justify;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.minor {
|
||||
font-size: 10px;
|
||||
color: #868e96;
|
||||
}
|
||||
|
||||
.Title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.Subtitle {
|
||||
font-style: italic;
|
||||
}
|
||||
.helpview {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=app.bdcabfb2.css.map*/
|
BIN
fontawesome-webfont.674f50d2.eot
Normal file
2671
fontawesome-webfont.912ec66d.svg
Normal file
After Width: | Height: | Size: 434 KiB |
BIN
fontawesome-webfont.af7ae505.woff2
Normal file
BIN
fontawesome-webfont.b06871f2.ttf
Normal file
BIN
fontawesome-webfont.fee66e71.woff
Normal file
34
index.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- cfb-editor (C) 2017-present SheetJS http://sheetjs.com -->
|
||||
<!-- vim: set ts=2: -->
|
||||
<html lang="en" style="height: 100%">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
||||
<meta name='apple-mobile-web-app-capable' content='yes'>
|
||||
<meta name='apple-mobile-web-app-status-bar-style' content='black'>
|
||||
<title>CFB Editor</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
|
||||
<link rel="stylesheet" href="https://cdn.rawgit.com/tyleruebele/details-shim/ba9df5f3/details-shim.min.css" />
|
||||
<link href="styles.css" rel="stylesheet"/>
|
||||
<link rel="shortcut icon" type="image/png" href="logo.png"/>
|
||||
<link href="vendor.5cab5569.css" rel="stylesheet"><link href="app.bdcabfb2.css" rel="stylesheet"></head>
|
||||
<body class="Body">
|
||||
<script src="https://cdn.rawgit.com/tyleruebele/details-shim/ba9df5f3/details-shim.min.js"></script>
|
||||
<div id="app" class="root"></div>
|
||||
<script type="text/javascript">
|
||||
/* eslint-env browser */
|
||||
/* eslint-disable-next-line */
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-36810333-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
<script>!function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c<i.length;c++)f=i[c],o[f]&&s.push(o[f][0]),o[f]=0;for(n in l)Object.prototype.hasOwnProperty.call(l,n)&&(e[n]=l[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,a||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var l=t[i];0!==o[l]&&(n=!1)}n&&(u.splice(r--,1),e=f(f.s=t[0]))}return e}var n={},o={2:0},u=[];function f(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.m=e,f.c=n,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,r){if(1&r&&(e=f(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)f.d(t,n,function(r){return e[r]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var i=window.webpackJsonp=window.webpackJsonp||[],l=i.push.bind(i);i.push=r,i=i.slice();for(var a=0;a<i.length;a++)r(i[a]);var p=l;t()}([]);</script>
|
||||
<script type="text/javascript" src="vendor.c8b76703.js"></script><script type="text/javascript" src="app.b9a31e1c.js"></script></body>
|
||||
</html>
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@ -1,13 +0,0 @@
|
||||
module.exports = {
|
||||
type: 'react-app',
|
||||
webpack: {
|
||||
extra: {
|
||||
module: {
|
||||
rules: [
|
||||
// {test: /\.html$/, loader: 'html-loader'},
|
||||
{test: /\.md$/, use: 'raw-loader'}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
42
package.json
@ -1,42 +0,0 @@
|
||||
{
|
||||
"name": "cfb-editor",
|
||||
"version": "0.1.2",
|
||||
"description": "CFB / ZIP editor",
|
||||
"private": true,
|
||||
"homepage": "https://sheetjs.com/cfb-editor",
|
||||
"scripts": {
|
||||
"build": "nwb build-react-app",
|
||||
"clean": "nwb clean-app",
|
||||
"start": "nwb serve-react-app",
|
||||
"lint": "eslint --ext js,jsx,html src/",
|
||||
"test:coverage": "nwb test-react --coverage",
|
||||
"test:watch": "nwb test-react --server"
|
||||
},
|
||||
"dependencies": {
|
||||
"cfb": "^1.2.0",
|
||||
"crc-32": "^1.2.0",
|
||||
"file-saver": "^1.3.3",
|
||||
"printj": "^1.1.2",
|
||||
"raw-loader": "^0.5.1",
|
||||
"react": "^16.0.0",
|
||||
"react-dom": "^16.0.0",
|
||||
"react-easy-state": "^3.0.1",
|
||||
"react-fa": "^5.0.0",
|
||||
"react-markdown": "^2.5.0",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-spinkit": "^3.0.0",
|
||||
"react-split-pane": "^0.1.66",
|
||||
"react-syntax-highlighter": "^6.1.1",
|
||||
"react-tippy": "^1.2.2",
|
||||
"sweetalert": "^2.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nwb": "0.23.x"
|
||||
},
|
||||
"author": "sheetjs",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/SheetJSDev/cfb-editor.git"
|
||||
}
|
||||
}
|
2
runtime.d956614e.js
Normal file
@ -0,0 +1,2 @@
|
||||
!function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],c=0,s=[];c<i.length;c++)f=i[c],o[f]&&s.push(o[f][0]),o[f]=0;for(n in l)Object.prototype.hasOwnProperty.call(l,n)&&(e[n]=l[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,a||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var l=t[i];0!==o[l]&&(n=!1)}n&&(u.splice(r--,1),e=f(f.s=t[0]))}return e}var n={},o={2:0},u=[];function f(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.m=e,f.c=n,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,r){if(1&r&&(e=f(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)f.d(t,n,function(r){return e[r]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var i=window.webpackJsonp=window.webpackJsonp||[],l=i.push.bind(i);i.push=r,i=i.slice();for(var a=0;a<i.length;a++)r(i[a]);var p=l;t()}([]);
|
||||
//# sourceMappingURL=runtime.d956614e.js.map
|
23
src/App.js
@ -1,23 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
import React, {Component} from 'react';
|
||||
import { HashRouter as Router, Route, Redirect, Switch } from 'react-router-dom';
|
||||
import SheetJSApp from './SheetJSApp';
|
||||
import HelpView from './views/HelpView';
|
||||
|
||||
class App extends Component {
|
||||
render() { return (
|
||||
<Router basename="/cfb-editor">
|
||||
<Switch>
|
||||
<Route path="/view/:idx/:mode" component={SheetJSApp} />
|
||||
<Route path="/view/:idx" component={SheetJSApp} />
|
||||
<Route exact path="/" component={SheetJSApp} />
|
||||
<Route exact path="/help" component={HelpView} />
|
||||
<Route render={() => (
|
||||
<Redirect to={`/`}/>
|
||||
)}/>
|
||||
</Switch>
|
||||
</Router>
|
||||
); }
|
||||
}
|
||||
|
||||
export default App;
|
@ -1,14 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
.Outer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.Bottom {
|
||||
height: calc(100% - 55px);
|
||||
}
|
||||
.RightPane {
|
||||
height: calc(100vh - 55px);
|
||||
overflow-y: auto;
|
||||
}
|
@ -1,168 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env browser */
|
||||
import React, { Component } from 'react';
|
||||
import { easyComp } from 'react-easy-state';
|
||||
import { /*BrowserRouter as Router,*/ Switch, Route, Redirect } from 'react-router-dom';
|
||||
import swal from 'sweetalert';
|
||||
import SplitPane from 'react-split-pane';
|
||||
import './SheetJSApp.css';
|
||||
|
||||
import store from './state';
|
||||
import ToolBar from './views/ToolBar';
|
||||
import TreeList from './views/TreeList';
|
||||
import FileView from './views/FileView';
|
||||
import ManifestView from './views/ManifestView';
|
||||
import InitView from './views/InitView';
|
||||
import DragDropDiv from './components/DragDropDiv';
|
||||
//import { fix_string, unfix_string } from './utils/misc';
|
||||
import { MAX_SIZE } from './consts';
|
||||
|
||||
const renamePopup = (n) => swal("Enter the new file name", {content:{element:'input', attributes:{placeholder:n}}});
|
||||
|
||||
const _ul = (evt) => {
|
||||
if(!store.isDirty()) return;
|
||||
const msg = `Recent changes have not been saved`;
|
||||
swal(msg, {timer:1000});
|
||||
return evt.returnValue = msg;
|
||||
};
|
||||
|
||||
class SheetJSApp extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.hfoe = this.handleFileOrEntry.bind(this);
|
||||
this.flipViz = this.flip_viz.bind(this);
|
||||
this.force = this.force.bind(this);
|
||||
this.state = { viz: true };
|
||||
}
|
||||
componentDidMount() { window.removeEventListener("beforeunload", _ul); window.addEventListener("beforeunload", _ul, true); }
|
||||
|
||||
flip_viz() { this.setState({viz: !this.state.viz}); this.forceUpdate(); }
|
||||
force() { if(this.toolbar) this.toolbar.force(); }
|
||||
erase() { if(!store.isDirty()) return store.reset();
|
||||
swal(`Are you sure you want to close ${store.fname}?`, {buttons:['No', 'Yes']}).then(v => { if(v) store.reset(); });
|
||||
}
|
||||
renameFile() { renamePopup(store.fname).then(v => { if(v) store.setName(v); }); }
|
||||
handleFile(file/*:File|FileList*/) {
|
||||
if(!(file instanceof File)) return swal("Please drop only one file");
|
||||
return this.handleOneFile(file);
|
||||
}
|
||||
handleOneFile(file/*:File*/) {
|
||||
const doit = () => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const bstr = e.target.result;
|
||||
try {
|
||||
store.setBStr(bstr, (e) => { swal("Error:" + (e.message||e)); });
|
||||
store.setName(file.name);
|
||||
} catch(e) { swal("Error:" + (e.message||e)); }
|
||||
};
|
||||
reader.readAsBinaryString(file);
|
||||
};
|
||||
|
||||
let msg = [];
|
||||
if(file.size > MAX_SIZE) msg.push("File size " + file.size + " exceeds limit.");
|
||||
if(msg.length > 0) {
|
||||
msg.push("Operations may be slow. Shall we proceed?");
|
||||
const domelt = document.createElement("div");
|
||||
domelt.innerHTML = msg.join("<br/>\n");
|
||||
swal({content: domelt, buttons:['No', 'Yes']}).then(v => { if(v) doit(); });
|
||||
} else doit();
|
||||
}
|
||||
handleEntry(idx/*:number*/, file/*:File*/) {
|
||||
const ReadFileArrayBuffer = (file/*:File*/, cb, opts) => {
|
||||
/* Boilerplate to set up FileReader */
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
/* Parse data */
|
||||
const ab = e.target.result;
|
||||
cb(null, ab);
|
||||
};
|
||||
if(opts) {
|
||||
if(opts.size > 0 && file.size > opts.size) return cb(new Error("File size " + file.size + " exceeds limit"), null);
|
||||
}
|
||||
reader.readAsArrayBuffer(file);
|
||||
};
|
||||
|
||||
ReadFileArrayBuffer(file, (err, ab) => {
|
||||
if(err) return swal("Error: " + err.message);
|
||||
store.setContentAB(idx, ab);
|
||||
}, { size: MAX_SIZE });
|
||||
}
|
||||
handleFileOrEntry(file/*:File|FileList*/) {
|
||||
if(store.isEmpty()) return this.handleFile(file);
|
||||
if(!(file instanceof File)) return swal("Please drop only one file");
|
||||
const idx = (!this.props.match || this.props.match.params.idx == null) ? -1 : this.props.match.params.idx;
|
||||
const opts = { buttons: { open: `Open`, add: "Add File" } };
|
||||
const fname = store.getFixedName(idx);
|
||||
if(idx > -1) opts.buttons.update = `Update contents`;
|
||||
swal(`${store.fname} ${fname && "(" + fname + ")"} is open. What do you want to do with ${file.name}?`, opts).then(v => {
|
||||
switch(v) {
|
||||
case 'open': {
|
||||
if(!store.isDirty()) { store.reset(); return this.handleFile(file); }
|
||||
swal(`There are unsaved changes. Are you sure you want to close ${store.fname}?`, {buttons:['No', 'Yes']}).then(v => { if(!v) return; store.reset(); this.handleFile(file); });
|
||||
} break;
|
||||
case 'add': {
|
||||
let fname = "";
|
||||
if(!store.find(file.name)) {
|
||||
fname = store.addNewFile(file.name);
|
||||
} else {
|
||||
fname = store.addNewFile();
|
||||
swal(`File ${file.name} exists! New file: ${fname}`);
|
||||
}
|
||||
const id = store.getIdByName(fname);
|
||||
this.handleEntry(id, file);
|
||||
} break;
|
||||
case 'update': this.handleEntry(idx, file); break;
|
||||
}
|
||||
});
|
||||
}
|
||||
newEntry() { const fname = store.addNewFile(); swal("New File: " + fname); }
|
||||
exportFile() { store.exportBStr(store.fname || "SheetJS.cfb"); }
|
||||
deleteEntry(idx) {
|
||||
swal(`Are you sure you want to delete ${store.getFixedName(idx)}`, {buttons:['No', 'Yes']}).then(v => {
|
||||
switch(v) {
|
||||
case true: store.delFileById(idx); break;
|
||||
default: return;
|
||||
}
|
||||
});
|
||||
}
|
||||
renameEntry(idx) { renamePopup(store.getFixedPath(idx)).then(v => { if(v) store.renFileById(idx,v); }); }
|
||||
|
||||
render() { return (
|
||||
<div className="Outer">
|
||||
<DragDropDiv handleFile={this.hfoe}>
|
||||
<ToolBar idx={this.props.match.params.idx} erase={this.erase} exportFile={this.exportFile} handleFile={this.hfoe} viz={this.state.viz} flipViz={this.flipViz} ref={(tb) => { this.toolbar = tb; }}/>
|
||||
</DragDropDiv>
|
||||
<div className="Bottom"><SplitPane split="vertical" minSize={50} maxSize={250}
|
||||
defaultSize={window.matchMedia && !window.matchMedia("(min-width: 650px)").matches ? 50 : 250}
|
||||
style={{height: 'calc(100% - 55px)'}}
|
||||
pane1Style={{display:this.state.viz ? "block" : "none"}}>
|
||||
{this.state.viz && ( <DragDropDiv handleFile={this.hfoe}>
|
||||
<TreeList idx={this.props.match.params.idx} fname={store.fname} exportFile={this.exportFile} erase={this.erase} renameFile={this.renameFile} addFile={this.newEntry} />
|
||||
</DragDropDiv>)}
|
||||
<DragDropDiv handleFile={this.hfoe}><div className="RightPane">
|
||||
{!store.isEmpty() ? (
|
||||
<Switch>
|
||||
<Route path="/view">
|
||||
<FileView idx={this.props.match.params.idx||0} mode={this.props.match.params.mode} deleteEntry={this.deleteEntry} renameEntry={this.renameEntry} handleFile={this.handleEntry} viz={this.state.viz} force={this.force} />
|
||||
</Route>
|
||||
<Route exact path="/">
|
||||
<ManifestView exportFile={this.exportFile} erase={this.erase} renameFile={this.renameFile} addFile={this.newEntry} />
|
||||
</Route>
|
||||
<Route><Redirect to="/" /></Route>
|
||||
</Switch>
|
||||
) : (
|
||||
<Switch>
|
||||
<Route exact path="/">
|
||||
<InitView handleFile={this.handleFile} />
|
||||
</Route>
|
||||
<Route><Redirect to="/" /></Route>
|
||||
</Switch>
|
||||
)}
|
||||
</div></DragDropDiv>
|
||||
</SplitPane></div>
|
||||
</div>
|
||||
); }
|
||||
}
|
||||
|
||||
export default easyComp(SheetJSApp);
|
@ -1,29 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
/*
|
||||
Simple HTML5 file input wrapper
|
||||
usage: <DataInput handleFile={callback} />
|
||||
handleFile(file:File):void;
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
|
||||
class DataInput extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
handleChange(e) {
|
||||
const files = e.target.files;
|
||||
if(files && files[0]) this.props.handleFile(files[0]);
|
||||
}
|
||||
render() { return (
|
||||
<form className="form-inline">
|
||||
<div className="form-group">
|
||||
{this.props.title && `${this.props.title}: `}
|
||||
<input type="file" className="form-control" id="file" onChange={this.handleChange} />
|
||||
</div>
|
||||
</form>
|
||||
); }
|
||||
}
|
||||
//<input type="file" className="form-control" id="file" accept={this.props.fmts || "*"} onChange={this.handleChange} />
|
||||
|
||||
export default DataInput;
|
@ -1,16 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
/*
|
||||
Simple Marker when a file is dirty
|
||||
usage: <DirtyBit isDirty={isdirty} />
|
||||
isDirty:boolean
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import Tooltip from './Tooltip';
|
||||
|
||||
class DirtyBit extends Component {
|
||||
constructor(props) { super(props); }
|
||||
render() { return this.props.isDirty ? ( <Tooltip title="File has unsaved changes" position="bottom">**</Tooltip> ) : ""; }
|
||||
}
|
||||
|
||||
export default DirtyBit;
|
@ -1,23 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
/*
|
||||
Simple HTML5 file drag-and-drop wrapper
|
||||
usage: <DragDropDiv handleFile={handleFile}>...</DragDropFile>
|
||||
handleFile(file:File):void;
|
||||
*/
|
||||
import React, { Component } from 'react';
|
||||
|
||||
class DragDropDiv extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.onDrop = this.onDrop.bind(this);
|
||||
}
|
||||
suppress(evt) { evt.stopPropagation(); evt.preventDefault(); }
|
||||
onDrop(evt) { evt.stopPropagation(); evt.preventDefault();
|
||||
const files = evt.dataTransfer.files;
|
||||
if(!files || files.length == 0) return;
|
||||
this.props.handleFile((files.length == 1) ? files[0] : files);
|
||||
}
|
||||
render() { return ( <div className="DDD" onDrop={this.onDrop} onDragEnter={this.suppress} onDragOver={this.suppress}>{this.props.children}</div> ); }
|
||||
}
|
||||
|
||||
export default DragDropDiv;
|
@ -1,6 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
/* This is a light-weight wrapper to hide the css import for react-tippy */
|
||||
import 'react-tippy/dist/tippy.css';
|
||||
import { Tooltip } from 'react-tippy';
|
||||
|
||||
export default Tooltip;
|
@ -1,12 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
export const THRESH = 24*16;
|
||||
export const MAX_SIZE = 5<<20;
|
||||
|
||||
export const SheetJSFT = [
|
||||
"application/zip",
|
||||
"application/octet-stream"
|
||||
].join(",") + [
|
||||
"mht", "mhtml",
|
||||
"zip", "xlsx", "xlsb", "xlsm", "ods",
|
||||
"cfb", "xls", "qpw", "wb3", "ppt", "doc"
|
||||
].map(function(x) { return "." + x; }).join(",");
|
@ -1,21 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
html, body, #app {
|
||||
height: 100%;
|
||||
}
|
||||
html {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow: auto;
|
||||
background-color: #ffffff;
|
||||
color: #000000;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- cfb-editor (C) 2017-present SheetJS http://sheetjs.com -->
|
||||
<!-- vim: set ts=2: -->
|
||||
<html lang="en" style="height: 100%">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
||||
<meta name='apple-mobile-web-app-capable' content='yes'>
|
||||
<meta name='apple-mobile-web-app-status-bar-style' content='black'>
|
||||
<title>CFB Editor</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
|
||||
<link rel="stylesheet" href="https://cdn.rawgit.com/tyleruebele/details-shim/ba9df5f3/details-shim.min.css" />
|
||||
<link href="styles.css" rel="stylesheet"/>
|
||||
<link rel="shortcut icon" type="image/png" href="logo.png"/>
|
||||
</head>
|
||||
<body class="Body">
|
||||
<script src="https://cdn.rawgit.com/tyleruebele/details-shim/ba9df5f3/details-shim.min.js"></script>
|
||||
<div id="app" class="root"></div>
|
||||
<script type="text/javascript">
|
||||
/* eslint-env browser */
|
||||
/* eslint-disable-next-line */
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-36810333-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
10
src/index.js
@ -1,10 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env browser */
|
||||
import './index.css';
|
||||
|
||||
import React from 'react';
|
||||
import {render} from 'react-dom';
|
||||
|
||||
import App from './App';
|
||||
|
||||
render(<App/>, document.querySelector('#app'));
|
171
src/state.js
@ -1,171 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env browser */
|
||||
import { easyStore } from 'react-easy-state';
|
||||
|
||||
import * as CFB from 'cfb';
|
||||
import * as JSZIP from './vendor/jszip';
|
||||
import { SaveBArray, SaveBString } from './utils/SaveData';
|
||||
import { fix_string, unfix_string, ab2a, s2a, a2s } from './utils/misc';
|
||||
|
||||
export default easyStore({
|
||||
fname: "",
|
||||
file: null,
|
||||
dirty: false,
|
||||
loading: false,
|
||||
|
||||
isDirty() { return this.file && this.dirty; },
|
||||
|
||||
setLoading(x) { return this.loading = x; },
|
||||
getLoading() { return this.loading; },
|
||||
|
||||
/* Type tests */
|
||||
isEmpty() { return !this.file; },
|
||||
isCFB() { return this.file && this.file.FullPaths; },
|
||||
isZIP() { return this.file && !this.isCFB(); },
|
||||
getType() { return this.isCFB() ? "CFB" : this.isZIP() ? "ZIP" : "???"; },
|
||||
|
||||
/* File-level Accessors */
|
||||
getFileList() { return this.keys().map((x, i) => [x, this.getFileEntryById(i), i]).filter(x => this.isCFB() ? x[1].type == 2 : !x[1].dir); },
|
||||
getNextName(id) { return !id ? this.getNextName(1) : this.find(`SheetJS${id}`) ? this.getNextName(id+1) : `SheetJS${id}`; },
|
||||
getRootName() { return !this.isCFB() ? "" : this.getFileNameById(0); },
|
||||
getCLSID() { return !this.isCFB() ? "" : this.getFileEntryById(0).clsid; },
|
||||
|
||||
/* File-level Mutators */
|
||||
setName(name) { this.fname = name; },
|
||||
setBStr(bstr, cb) {
|
||||
this.loading = true;
|
||||
setTimeout(() => {try {
|
||||
switch(bstr.slice(0,4)) {
|
||||
case "MIME":
|
||||
case "\xD0\xCF\x11\xE0": this.type = "CFB"; this.file = CFB.read(bstr, {type: "binary"}); break;
|
||||
case "PK\x03\x04": case "PK\x05\x06": this.type = "ZIP"; this.file = new JSZIP(bstr, {base64: false}); break;
|
||||
default: throw new Error(`Invalid file (magic ${bstr.slice(0,4).split("").map(x => x.charCodeAt(0).toString(16).padStart(2,"0"))})`);
|
||||
}
|
||||
this.loading = false;
|
||||
} catch(e) { this.loading = false; if(cb) cb(e); } }, 100); },
|
||||
addNewFile(name) {
|
||||
const fname = name || this.getNextName(1);
|
||||
if(this.isCFB()) CFB.utils.cfb_add(this.file, fname, [0x37, 0x32, 0x36, 0x32]);
|
||||
else if(this.isZIP()) this.file.file(fname, "7262");
|
||||
const fi = this.find(fname);
|
||||
this.dirty = true;
|
||||
return this.isCFB() ? this.getFileNameById(this.entries().indexOf(fi)) : fname;
|
||||
},
|
||||
|
||||
/* Entry-level Accessors */
|
||||
getFileNameById(id) { return this.keys()[id] || ""; },
|
||||
getFileEntryById(id) { return this.entries()[id] || null; },
|
||||
getFixedName(id) { return this.isEmpty() ? "" : fix_string(this.getFileNameById(id)); },
|
||||
getFixedPath(id) { return this.isEmpty() ? ""
|
||||
: this.isCFB() ? fix_string(this.getFileNameById(id)).replace(/^[^/]*/,"")
|
||||
: fix_string(this.getFileNameById(id)); },
|
||||
getFileTime(id) { return (this.isEmpty() || !this.getFileEntryById(id)) ? new Date(NaN) : (this.getFileTimeEntry(id) || this.getFileTimeEntry(0) || new Date(1980,0,1)); },
|
||||
|
||||
/* Entry-level Mutators */
|
||||
setContentAB(id, ab) {
|
||||
const payload = ab2a(ab), fn = this.getFileNameById(id);
|
||||
if(this.isCFB()) {
|
||||
CFB.utils.prep_blob(payload);
|
||||
CFB.utils.cfb_add(this.file, fn, ab2a(payload));
|
||||
} else if(this.isZIP()) {
|
||||
this.file.file(fn, payload.map(x => String.fromCharCode(x)).join(""), {binary:"true"});
|
||||
}
|
||||
this.dirty = true;
|
||||
},
|
||||
renFileById(id, name) {
|
||||
const oldfn = this.getFileNameById(id), newfn = unfix_string(name).replace(/^\//,"");
|
||||
if(this.isCFB()) {
|
||||
CFB.utils.cfb_mov(this.file, oldfn, this.getFileNameById(0) + newfn);
|
||||
CFB.utils.cfb_gc(this.file);
|
||||
} else if(this.isZIP()) {
|
||||
this.file.file(newfn, a2s(this.getContentById(id)), {binary: true});
|
||||
this.file.remove(oldfn);
|
||||
}
|
||||
this.dirty = true;
|
||||
},
|
||||
delFileById(id) {
|
||||
const fn = this.getFileNameById(id);
|
||||
if(this.isCFB()) CFB.utils.cfb_del(this.file, fn);
|
||||
if(this.isZIP()) this.file.remove(fn);
|
||||
this.dirty = true;
|
||||
},
|
||||
|
||||
/* Download data */
|
||||
exportBStrById(id) {
|
||||
this.loading = true;
|
||||
setTimeout(() => {try {
|
||||
const FI = this.getFileEntryById(id);
|
||||
SaveBArray(this.getContentByEntry(FI), FI.name);
|
||||
this.loading = false;
|
||||
} catch(e) { this.loading = false; throw e; } }, 100);
|
||||
},
|
||||
exportBStr(/*name*/) {
|
||||
this.loading = true;
|
||||
setTimeout(() => {try {
|
||||
this.dirty = false;
|
||||
let o;
|
||||
var fType = /\.mht(?:ml)?$/.test(this.fname||"") ? "mad" : "";
|
||||
if(this.isCFB()) o = SaveBString(CFB.write(this.file, {type:"binary", fileType: fType}), this.fname || "SheetJS.cfb");
|
||||
if(this.isZIP()) o = SaveBString(this.file.generate({type:"string", compression:"DEFLATE"}), this.fname || "SheetJS.zip");
|
||||
this.loading = false;
|
||||
return o;
|
||||
} catch(e) { this.loading = false; throw e; } }, 100); },
|
||||
|
||||
/* Initialization */
|
||||
newFile(name) {
|
||||
this.fname = name || "sheetjs.cfb";
|
||||
if(this.fname.match(/\.zip$/)) {
|
||||
const out = new JSZIP();
|
||||
out.file("Sh33tJ5", "7262");
|
||||
this.file = out;
|
||||
} else this.file = CFB.utils.cfb_new();
|
||||
this.dirty = true;
|
||||
},
|
||||
reset() { this.file = null; this.fname = ""; this.dirty = false; },
|
||||
|
||||
/* Utils */
|
||||
getContentById(id) { return this.getContentByEntry(this.getFileEntryById(id)); },
|
||||
getContentByEntry(fi) {
|
||||
if(!fi) return [];
|
||||
if(fi._data) return typeof fi._data == "string" ? s2a(fi._data) : ab2a(fi._data.getContent());
|
||||
return fi.content || []; },
|
||||
getContentSliceByEntry(fi, s, e) {
|
||||
return !fi ? []
|
||||
: fi.content ? fi.content.slice(s, e)
|
||||
: this.getContentByEntry(fi).slice(s, e);
|
||||
},
|
||||
getSizeByEntry(fi) {
|
||||
if(this.isCFB() && (!fi.content || !fi.content.length)) return 0;
|
||||
return fi && (fi.size || fi._data && (fi._data.uncompressedSize) || fi._data.length) || 0;
|
||||
},
|
||||
getTextByEntry(fi) {
|
||||
if(!fi || (!fi._data && !fi.content)) return "";
|
||||
if(fi._data) return (typeof fi._data == "string") ? fi._data : a2s(fi._data.getContent());
|
||||
return a2s(fi.content);
|
||||
},
|
||||
getFileTimeEntry(id) {
|
||||
const FI = this.getFileEntryById(id);
|
||||
return !FI ? new Date(NaN) : (FI.ct || FI.mt || FI.date || id != 0 && this.getFileTime(0) || new Date(1980,0,1));
|
||||
},
|
||||
find(path) { return this.isEmpty() ? null
|
||||
: this.isCFB() ? CFB.find(this.file, path)
|
||||
: this.file.filter((rp, f) => (path == rp || path == f.name))[0]; },
|
||||
keys() { return this.isEmpty() ? []
|
||||
: this.isCFB() ? this.file.FullPaths
|
||||
: Object.keys(this.file.files).filter(x => !this.file.files[x].dir);
|
||||
},
|
||||
entries() { return this.isEmpty() ? []
|
||||
: this.isCFB() ? this.file.FileIndex
|
||||
: this.keys().map(x => this.file.files[x]);
|
||||
},
|
||||
getIdByName(name) {
|
||||
if(this.isEmpty()) return -1;
|
||||
if(this.isZIP()) {
|
||||
const entries = Object.keys(this.file.files).filter(x => !this.file.files[x].dir);
|
||||
return entries.indexOf(name);
|
||||
}
|
||||
return this.entries().indexOf(this.find(name));
|
||||
},
|
||||
|
||||
Sheet: "JS"
|
||||
});
|
@ -1,27 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env browser */
|
||||
/* Save workbook on client side */
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
/* see Browser download file example in docs */
|
||||
const s2ab = (s/*:string*/) => {
|
||||
const buf = new ArrayBuffer(s.length);
|
||||
const view = new Uint8Array(buf);
|
||||
for (let i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
|
||||
return buf;
|
||||
};
|
||||
|
||||
const a2ab = (a/*:Array<number>*/) => {
|
||||
const o = new ArrayBuffer(a.length);
|
||||
const view = new Uint8Array(o);
|
||||
for (let i = 0; i!=a.length; ++i) view[i] = a[i];
|
||||
return o;
|
||||
};
|
||||
|
||||
export const SaveBString = (str, fname = "sheetjs.out") => {
|
||||
saveAs(new Blob([s2ab(str)],{type:"application/octet-stream"}), fname || "sheetjs.out");
|
||||
};
|
||||
|
||||
export const SaveBArray = (arr, fname = "sheetjs.out") => {
|
||||
saveAs(new Blob([a2ab(arr)],{type:"application/octet-stream"}), fname || "sheetjs.out");
|
||||
};
|
@ -1,62 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
import { sprintf, vsprintf } from 'printj';
|
||||
|
||||
const X = "%02hhx", Y = X + X + " ";
|
||||
const FMT = [...Array.from({length:16}).map((x,i) =>
|
||||
Y.repeat(i>>1) + (i%2 ? X:" ") + " " + " ".repeat(7 - (i >> 1)) + "|" + "%c".repeat(i) + " ".repeat(16-i) + "|\n"
|
||||
), Y.repeat(8) + "|" + "%c".repeat(16) + "|\n"];
|
||||
export const line = ([x,d]) => {
|
||||
if(!d || !d.length) return "";
|
||||
if(!FMT[d.length]) return "wtf";
|
||||
return vsprintf("%04x: " + FMT[d.length], [x, ...d, ...d.map(x => String.fromCharCode(x).replace(/[^\x20-\x7E]/g,"."))]);
|
||||
};
|
||||
|
||||
export const fix_string = (x) => x.replace(/[\u0000-\u001f]/g, ($$) => sprintf("\\u%04X", $$.charCodeAt(0)));
|
||||
|
||||
export const unfix_string = (x) => x.replace(/\\u(\d{4})/g, ($$, $1) => String.fromCharCode(parseInt($1, 16)));
|
||||
|
||||
export const format_date = (date/*:Date*/) => sprintf("%02u-%02u-%02u %02u:%02u", date.getUTCMonth()+1, date.getUTCDate(), date.getUTCFullYear()%100, date.getUTCHours(), date.getUTCMinutes());
|
||||
|
||||
export const ab2a = (ab) => {
|
||||
const o = [], u = new Uint8Array(ab);
|
||||
for(let i = 0; i < u.length; ++i) o[i] = u[i];
|
||||
return o;
|
||||
};
|
||||
|
||||
export const s2a = x => x.split("").map(x => x.charCodeAt(0));
|
||||
|
||||
export const a2s = x => {
|
||||
const a = new Array(x.length);
|
||||
for(let i = 0; i < x.length; ++i) a.push(String.fromCharCode(x[i]));
|
||||
return a.join("");
|
||||
};
|
||||
|
||||
const decode_msi_char = (x) => {
|
||||
switch(true) {
|
||||
case x<10: return x + 48;
|
||||
case x<36: return x + 55;
|
||||
case x<62: return x + 61;
|
||||
case x==62: return 46;
|
||||
}
|
||||
return 95;
|
||||
};
|
||||
|
||||
export const decode_msi_name = (instr) => {
|
||||
var ch = instr.charCodeAt(0);
|
||||
var idx = 0;
|
||||
var out = [];
|
||||
if(ch == 0x4840) { ++idx; /*out.push("*");*/ }
|
||||
while ((ch = instr.charCodeAt(idx++))) {
|
||||
if(ch >= 0x3800 && ch < 0x4840 ) {
|
||||
if(ch >= 0x4800) {
|
||||
ch = decode_msi_char(ch-0x4800);
|
||||
} else {
|
||||
ch -= 0x3800;
|
||||
out.push(String.fromCharCode(decode_msi_char(ch & 0x3f)));
|
||||
ch = decode_msi_char( (ch>>6) & 0x3f);
|
||||
}
|
||||
}
|
||||
out.push(String.fromCharCode(ch));
|
||||
}
|
||||
return out.join("");
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
var xlmlregex = /<(\/?)([^\s?>!/:]*:|)([^\s?>:/]+)[^>]*>/mg;
|
||||
export default function formatXml(xml) {
|
||||
var indent = 0, last = "";
|
||||
var formatted = xml.replace(xlmlregex, function($$, $1, $2, $3) {
|
||||
var old = indent;
|
||||
if($1 == "/") { --indent; --old; }
|
||||
else if($$.charAt($$.length - 2) == "/");
|
||||
else ++indent;
|
||||
var pad = ($3 == last && $1 == "/") ? "" : "\n" + " ".repeat(old);
|
||||
last = $3;
|
||||
return pad + $$;
|
||||
});
|
||||
return formatted.replace(/\n+\n</mg, "\n<");
|
||||
}
|
8988
src/vendor/jszip.js
vendored
@ -1,122 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env browser */
|
||||
import React, { PureComponent } from 'react';
|
||||
import { easyComp } from 'react-easy-state';
|
||||
import { Link, Redirect } from 'react-router-dom';
|
||||
import { Icon } from 'react-fa';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import { arduinoLight } from 'react-syntax-highlighter/styles/hljs';
|
||||
import * as Spinner from 'react-spinkit';
|
||||
import './MainPane.css';
|
||||
|
||||
import { sprintf } from 'printj';
|
||||
import { buf } from 'crc-32';
|
||||
|
||||
import store from '../state';
|
||||
import DataInput from '../components/DataInput';
|
||||
import { line, fix_string, decode_msi_name } from '../utils/misc';
|
||||
import { THRESH } from '../consts';
|
||||
import xml from '../utils/xml';
|
||||
|
||||
/* TODO: load through state so screen doesn't block on render */
|
||||
class FileView extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {loading: true, FN:"", _size:0, _crc32:"", show_text:false, indexData:[]};
|
||||
["export", "delete", "rename", "handle", "stload", "common"].forEach(n => { this[n] = this[n].bind(this); });
|
||||
}
|
||||
componentWillMount() { this.common(this.props, true); }
|
||||
componentWillReceiveProps(nextProps) { this.common(nextProps, false); return true; }
|
||||
export() { store.exportBStrById(this.props.idx); }
|
||||
delete() { this.props.deleteEntry(this.props.idx); }
|
||||
rename() { this.props.renameEntry(this.props.idx); }
|
||||
handle(f) { this.props.handleFile(this.props.idx, f); }
|
||||
stload() { }
|
||||
common(props/*, mnt*/) {
|
||||
if(!this.state.loading) { this.setState({loading:true}); }
|
||||
//const doit = mnt || this.props.idx != props.idx || this.props.mode != props.mode;
|
||||
setTimeout(() => {
|
||||
const FP = store.getFileNameById(props.idx), FI = store.getFileEntryById(props.idx);
|
||||
if(!FP || !FI || store.isCFB() && FI.type != 2) return this.setState({loading:false});
|
||||
const FN = fix_string(FI.name);
|
||||
|
||||
const content = store.getContentByEntry(FI);
|
||||
const _size = store.getSizeByEntry(FI);
|
||||
const _crc32 = sprintf("%0.8X", buf(content));
|
||||
|
||||
const show_text = props.mode == "text";
|
||||
const T = show_text ? store.getTextByEntry(FI) : "";
|
||||
const doxml = T && (T.slice(0,6) == "<?xml " || T.slice(0,100).match(/^<\w*(:\w+)?[\s/>]/));
|
||||
|
||||
let show_link = props.mode || "text"; if(show_link == "full") show_link = "text";
|
||||
if(!props.mode || props.mode == "full") {
|
||||
const hdr = store.getContentSliceByEntry(FI,0,16);
|
||||
if(hdr[0] == 0xFF && hdr[1] == 0xD8 && hdr[2] == 0xFF) show_link = "imag";
|
||||
if(hdr[0] == 0x89 && hdr[1] == 0x50 && hdr[2] == 0x4E) show_link = "imag";
|
||||
if(hdr[0] == 0x47 && hdr[1] == 0x49 && hdr[2] == 0x46 && hdr[3] == 0x38) show_link = "imag";
|
||||
}
|
||||
|
||||
const show_imag = this.props.mode == "imag", I = show_imag ? "data:image/jpeg;base64," + btoa(store.getTextByEntry(FI)) : "";
|
||||
|
||||
const Target = this.props && this.props.mode ? store.getSizeByEntry(FI) : Math.min(store.getSizeByEntry(FI),THRESH);
|
||||
const indices = Array.from({length:(Target)+15>>4}).map((x,i) => 16*i);
|
||||
const indexData = (T || I) ? [] : indices.map(x => line([x, store.getContentSliceByEntry(FI, x,x+16)]));
|
||||
this.setState({ loading:false, FN, _size, _crc32, show_text, T, doxml, show_link, show_imag, I, indices, indexData });
|
||||
this.forceUpdate();
|
||||
}, 0);
|
||||
}
|
||||
render() {
|
||||
const FP = store.getFileNameById(this.props.idx), FI = store.getFileEntryById(this.props.idx);
|
||||
if(!FP || !FI || store.isCFB() && FI.type != 2) return ( <Redirect to={`/`} /> );
|
||||
|
||||
const _FP = fix_string(FP);
|
||||
|
||||
/* indices, indices.map(x => line([x, store.getContentSliceByEntry(FI, x,x+16)])) */
|
||||
const { FN, _size, _crc32, show_text, T, doxml, show_link, show_imag, I, indices, indexData } = this.state;
|
||||
const partial = _size > THRESH && !this.props.mode;
|
||||
|
||||
const show_link_txt = { "text": "Text", "imag": "Image" }[show_link];
|
||||
const out = (
|
||||
<div className="conpainer">
|
||||
<div><b>File Name:</b> {FN} {FN.charCodeAt(0) >= 0x3800 && FN.charCodeAt(0) <= 0x4840 && `MSI Name: ${decode_msi_name(FN)}`}</div>
|
||||
<div className="minor">{FN != FI.name ? `File name has non-display characters, which are rendered as "\\u" followed by character code` : `\u00A0`}</div>
|
||||
<div className="minor"> </div>
|
||||
<div className="flexrow">
|
||||
<div className="col-xs-4"><a onClick={this.export}><Icon name="download" fixedWidth /> Export Entry</a></div>
|
||||
<div className="col-xs-4"><a onClick={this.delete}><Icon name="chain-broken" fixedWidth /> Delete Entry</a></div>
|
||||
<div className="col-xs-4"><a onClick={this.rename}><Icon name="i-cursor" fixedWidth /> Rename Entry</a></div>
|
||||
</div>
|
||||
<br />
|
||||
<DataInput handleFile={this.handle} title={`Replace file contents`} />
|
||||
<div className="minor">You can also drag and drop a file.</div><br/>
|
||||
<details>
|
||||
<summary><b>Entry Metadata</b> (click to show)</summary>
|
||||
<b>Full Path:</b> {_FP}<br />
|
||||
<b>Size:</b> {_size} bytes<br />
|
||||
<b>CRC32:</b> {_crc32}<br />
|
||||
</details>
|
||||
<br />
|
||||
<b>
|
||||
{partial ? `Showing First ${THRESH} Bytes` : "Showing File Contents"}
|
||||
{partial && ( <Link to={`/view/${this.props.idx}/full`}> (Full Contents) </Link> )}
|
||||
{show_link && show_link != this.props.mode && ( <Link to={`/view/${this.props.idx}/${show_link}`} onClick={this.stload}> (View as {show_link_txt}) </Link> )}
|
||||
</b>
|
||||
{this.state.loading ? (
|
||||
<Spinner name="line-scale-pulse-out" fadeIn="none" />
|
||||
) : show_text ? (
|
||||
<SyntaxHighlighter style={arduinoLight} customStyle={{width:`calc(100vw - ${this.props.viz ? 300 : 50}px)`}}>{doxml ? xml(T) : T}</SyntaxHighlighter>
|
||||
) : show_imag ? (
|
||||
<div>
|
||||
<img src={I} />
|
||||
</div>
|
||||
) : !indices.length ? (
|
||||
<pre>(file is empty)</pre>
|
||||
) : (
|
||||
<pre>{indexData}</pre>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
export default easyComp(FileView);
|
@ -1,4 +0,0 @@
|
||||
.helpview {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
import React, { Component } from 'react';
|
||||
import { easyComp } from 'react-easy-state';
|
||||
import { Link } from 'react-router-dom';
|
||||
//import { Icon } from 'react-fa';
|
||||
import mdcontent from './HelpView.md';
|
||||
import Markdown from 'react-markdown';
|
||||
import './HelpView.css';
|
||||
|
||||
/* see https://github.com/rexxars/react-markdown/issues/29 */
|
||||
function RouterLink(props) { return ( props.href.match(/^\//)
|
||||
? <Link to={props.href}>{props.children}</Link>
|
||||
: <a href={props.href}>{props.children}</a>
|
||||
); }
|
||||
|
||||
class HelpView extends Component {
|
||||
constructor(props) { super(props); }
|
||||
render() { return ( <Markdown className="helpview" source={mdcontent} renderers={{Link: RouterLink}} /> );}
|
||||
}
|
||||
|
||||
export default easyComp(HelpView);
|
@ -1,153 +0,0 @@
|
||||
## CFB Editor
|
||||
|
||||
[(click here to go back to the main screen)](/)
|
||||
|
||||
The CFB Editor can edit CFB and ZIP archives in your web browser! No data is
|
||||
sent to any remote server. All operations are performed locally, so your data
|
||||
is safe. It is powered by web browser magic.
|
||||
|
||||
### Loading data
|
||||
|
||||
The editor can read an existing file or generate a new file:
|
||||
|
||||
**Reading an Existing File**
|
||||
|
||||
From the main screen, click "Choose File" and select a file. Alternatively,
|
||||
you can click and drag a file from your device into the box.
|
||||
<details open>
|
||||
<summary><b>Show Animation</b></summary>
|
||||
<a href="ReadFile.gif"><img src="ReadFile.gif" style="max-width:100%" /></a>
|
||||
</details>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
**Creating a new file**
|
||||
|
||||
Click on the relevant links from the main screen.
|
||||
|
||||
<details>
|
||||
<summary><b>Show Animation</b></summary>
|
||||
<a href="NewFile.gif"><img src="NewFile.gif" style="max-width:100%" /></a>
|
||||
</details>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
**Closing a file to start from scratch**
|
||||
|
||||
If a file is open, click on the <i class="fa fa-fw fa-close"></i> Close archive
|
||||
links to close the file and go back to the initial view.
|
||||
|
||||
<details>
|
||||
<summary><b>Show Animation</b></summary>
|
||||
<a href="CloseFile.gif"><img src="CloseFile.gif" style="max-width:100%" /></a>
|
||||
</details>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
### Saving data
|
||||
|
||||
The editor can download updated archives or individual files to your device:
|
||||
|
||||
**Download an updated archive**
|
||||
|
||||
Click on the <i class="fa fa-fw fa-floppy-o"></i> Download Archive links
|
||||
|
||||
<details>
|
||||
<summary><b>Show Animation</b></summary>
|
||||
<a href="DownloadFile.gif"><img src="DownloadFile.gif" style="max-width:100%" /></a>
|
||||
</details>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
**Download a file within the archive**
|
||||
|
||||
When a file is loaded, click on the name of the file in the sidebar to show the
|
||||
file view. Click on the <i class="fa fa-fw fa-download"></i> Export Entry
|
||||
link to download the viewed file.
|
||||
|
||||
<details>
|
||||
<summary><b>Show Animation</b></summary>
|
||||
<a href="DownloadEntry.gif"><img src="DownloadEntry.gif" style="max-width:100%" /></a>
|
||||
</details>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
### Modifying files in the archive
|
||||
|
||||
The editor can add, delete, and rename files:
|
||||
|
||||
**Rename the archive**
|
||||
|
||||
Click on the <i class="fa fa-fw fa-i-cursor"></i> Rename archive link in the
|
||||
sidebar or the archive view.
|
||||
|
||||
<details>
|
||||
<summary><b>Show Animation</b></summary>
|
||||
<a href="RenameFile.gif"><img src="RenameFile.gif" style="max-width:100%" /></a>
|
||||
</details>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
**Rename a file within the archive**
|
||||
|
||||
When a file is loaded, click on the name of the file in the sidebar to show the
|
||||
file view. Click on the <i class="fa fa-fw fa-i-cursor"></i> Rename link in
|
||||
the file view to rename.
|
||||
|
||||
<details>
|
||||
<summary><b>Show Animation</b></summary>
|
||||
<a href="RenameEntry.gif"><img src="RenameEntry.gif" style="max-width:100%" /></a>
|
||||
</details>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
**Delete a file from the archive**
|
||||
|
||||
When a file is loaded, click on the name of the file in the sidebar to show the
|
||||
file view. Click on the <i class="fa fa-fw fa-chain-broken"></i> Rename link
|
||||
in the file view to rename.
|
||||
|
||||
<details>
|
||||
<summary><b>Show Animation</b></summary>
|
||||
<a href="DeleteFile.gif"><img src="DeleteFile.gif" style="max-width:100%" /></a>
|
||||
</details>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
**Updating data for a file**
|
||||
|
||||
When a file is loaded, click on the name of the file in the sidebar to show the
|
||||
file view. Click on the "Choose file" button to upload a new file that will
|
||||
replace the existing data.
|
||||
|
||||
<details>
|
||||
<summary><b>Show Animation</b></summary>
|
||||
<a href="UpdateFile.gif"><img src="UpdateFile.gif" style="max-width:100%" /></a>
|
||||
</details>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
**Add a file to the archive**
|
||||
|
||||
Click on the <i class="fa fa-fw fa-plus"></i> Add file link in the sidebar or
|
||||
the archive view. By default, the file will have a generated name and dummy
|
||||
content. Follow the instructions to rename the file and update the content.
|
||||
|
||||
<details>
|
||||
<summary><b>Show Animation</b></summary>
|
||||
<a href="DragFile.gif"><img src="DragFile.gif" style="max-width:100%" /></a>
|
||||
</details>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
[(C) 2017-present SheetJS LLC](http://sheetjs.com)
|
@ -1,13 +0,0 @@
|
||||
.minor {
|
||||
font-size: 10px;
|
||||
color: #868e96;
|
||||
}
|
||||
|
||||
.Title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.Subtitle {
|
||||
font-style: italic;
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env browser */
|
||||
import React, { Component } from 'react';
|
||||
import { easyComp } from 'react-easy-state';
|
||||
import { Link, Redirect } from 'react-router-dom';
|
||||
import './InitView.css';
|
||||
|
||||
import store from '../state';
|
||||
import DataInput from '../components/DataInput';
|
||||
import { SheetJSFT } from '../consts';
|
||||
|
||||
class InitView extends Component {
|
||||
constructor(props) { super(props); }
|
||||
newCFB() { store.newFile("SheetJS.cfb"); }
|
||||
newZIP() { store.newFile("SheetJS.zip"); }
|
||||
render() { if(!store.isEmpty()) return ( <Redirect to={`/`}/> );
|
||||
return (
|
||||
<div className="conpainer">
|
||||
<br/>
|
||||
<div className="Title">Welcome to CFB Editor</div>
|
||||
<div className="Subtitle">View and Edit archives with ease</div><br /><br />
|
||||
<DataInput handleFile={this.props.handleFile} title={`Read a file on your device`} fmts={SheetJSFT} />
|
||||
<div className="minor">Your data never leaves your device</div>
|
||||
<div>You can also drag and drop a file into the window</div>
|
||||
<br />
|
||||
<a onClick={this.newZIP}>Click here to make a new ZIP archive</a><br />
|
||||
<br />
|
||||
<a onClick={this.newCFB}>Click here to make a new CFB archive</a><br />
|
||||
<br />
|
||||
<details>
|
||||
<summary><b>Download Sample Files</b> (click to show)</summary>
|
||||
<a href="http://oss.sheetjs.com/test_files/pivot_table_test.xls" >CFB: pivot_table_test.xls</a><br /><br />
|
||||
<a href="http://oss.sheetjs.com/test_files/pivot_table_test.xlsb" >ZIP: pivot_table_test.xlsb</a><br />
|
||||
</details>
|
||||
<br />
|
||||
<Link to={`/help`}>Show Help</Link><br />
|
||||
<br />
|
||||
{ window.matchMedia && !window.matchMedia("(min-width: 800px)").matches ? ( <div className="minor">(some items may not fit, consider using a wider screen)</div> ) : ( <br /> )}
|
||||
<br />
|
||||
<br />
|
||||
<div className="minor"><a className="minor" href="http://sheetjs.com">Copyright (C) 2017-present SheetJS LLC</a></div>
|
||||
</div>
|
||||
); }
|
||||
}
|
||||
|
||||
export default easyComp(InitView);
|
@ -1,13 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
.conpainer {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
padding: 0px 20px;
|
||||
}
|
||||
|
||||
.flexrow {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
import React, { Component } from 'react';
|
||||
import { easyComp } from 'react-easy-state';
|
||||
import { Link, Redirect } from 'react-router-dom';
|
||||
import { Icon } from 'react-fa';
|
||||
import './MainPane.css';
|
||||
|
||||
import { sprintf } from 'printj';
|
||||
|
||||
import store from '../state';
|
||||
import { format_date, fix_string, decode_msi_name } from '../utils/misc';
|
||||
|
||||
class ManifestView extends Component {
|
||||
constructor(props) { super(props); }
|
||||
render() {
|
||||
if(store.isEmpty()) return ( <Redirect to={`/`} /> );
|
||||
return (
|
||||
<div className="conpainer">
|
||||
<div><b>Archive Name:</b> {store.fname}</div>
|
||||
<div className="minor"> </div>
|
||||
<div className="minor"> </div>
|
||||
<div className="flexrow">
|
||||
<div className="col-xs-3"><a onClick={this.props.exportFile}><Icon name="floppy-o" fixedWidth /> Download</a></div>
|
||||
<div className="col-xs-3"><a onClick={this.props.renameFile}><Icon name="i-cursor" fixedWidth /> Rename</a></div>
|
||||
<div className="col-xs-3"><a onClick={this.props.addFile}><Icon name="plus" fixedWidth /> Add File</a></div>
|
||||
<div className="col-xs-3"><a onClick={this.props.erase}><Icon name="close" fixedWidth /> Close</a></div>
|
||||
</div>
|
||||
<div className="minor">Entries can be renamed or deleted from the file view.</div>
|
||||
<div className="minor">Entries can be added by dragging and dropping files from your computer.</div>
|
||||
<br />
|
||||
{store.isCFB() && (<details>
|
||||
<summary><b>Metadata</b> (click to show)</summary>
|
||||
<b>Root Name:</b> {store.getRootName()}<br />
|
||||
<b>CLSID:</b> {store.getCLSID()}<br /><br />
|
||||
</details>)}
|
||||
<br/>
|
||||
<b>File Manifest</b>
|
||||
<pre>
|
||||
{" Length Date Time Name\n"}
|
||||
{" -------- ---- ---- ----\n"}
|
||||
{store.getFileList().map(([fp, fi, i]) => ( <span key={i.toString()}>
|
||||
{sprintf("%9lu %s ", store.getSizeByEntry(fi), format_date(store.getFileTime(i)))}
|
||||
{fp.replace(/.*\//g,"").charCodeAt(0) >= 0x3800 && fp.replace(/.*\//g,"").charCodeAt(0) <= 0x4840 ? (
|
||||
<Link to={`/view/${i}`}>{(`${fix_string(fp)} (${decode_msi_name(fp.replace(/.*\//g,""))})`)}</Link>
|
||||
) : (
|
||||
<Link to={`/view/${i}`}>{fix_string(fp)}</Link>
|
||||
)}
|
||||
{"\n"}
|
||||
</span> ))}
|
||||
</pre>
|
||||
</div>
|
||||
); }
|
||||
}
|
||||
// <h3>{store.fname} {store.getType()}</h3>
|
||||
export default easyComp(ManifestView);
|
@ -1,66 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
.ToolBar {
|
||||
height: 55px;
|
||||
line-height: 55px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
padding: 0px 20px;
|
||||
/* display: flex;
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex: 0 0 55px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid #000000;
|
||||
width: 100%;*/
|
||||
}
|
||||
|
||||
.ToolBar {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.Logo {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.TitleIcons {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.TitleIcons > div {
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
.TitleFile {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.TitleName {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.AlignLeft {
|
||||
float:left;
|
||||
}
|
||||
|
||||
.AlignRight {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.AlignCenter {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
a { color: #0000EE; }
|
||||
|
||||
.Spinner {
|
||||
height: 30px;
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
import React, { Component } from 'react';
|
||||
import { easyComp } from 'react-easy-state';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Icon } from 'react-fa';
|
||||
import * as Spinner from 'react-spinkit';
|
||||
import './ToolBar.css';
|
||||
|
||||
import store from '../state';
|
||||
import Tooltip from '../components/Tooltip';
|
||||
import DirtyBit from '../components/DirtyBit';
|
||||
|
||||
class ToolBar extends Component {
|
||||
constructor(props) { super(props); this.flipViz = this.flipViz.bind(this); this.force = this.force.bind(this); }
|
||||
flipViz() { this.props.flipViz(); this.forceUpdate(); }
|
||||
force() { this.forceUpdate(); }
|
||||
render() { return (
|
||||
<div className="ToolBar">
|
||||
<div className="AlignLeft"><div className="TitleName">
|
||||
CFB Editor
|
||||
<Tooltip title={(this.props.viz ? "Hide" : "Show") + " Sidebar"} position="bottom">
|
||||
<a onClick={this.flipViz}><Icon name={"caret-" + (this.props.viz ? "left" : "right")} fixedWidth /></a>
|
||||
</Tooltip>
|
||||
{store.getLoading() && (<Spinner name="line-scale-pulse-out" fadeIn="none" />)}
|
||||
</div></div>
|
||||
<div className="AlignCenter"><div className="TitleFile">
|
||||
{store.fname ? this.props.idx ? (
|
||||
<Link to={`/`}>{`<${store.fname}>`}</Link>
|
||||
) :
|
||||
`<${store.fname}>`
|
||||
: null}
|
||||
<DirtyBit isDirty={store.isDirty()} />
|
||||
</div></div>
|
||||
<div className="AlignRight"><div className="TitleIcons">
|
||||
{!store.isEmpty() && (
|
||||
<Tooltip title="Download archive" position="bottom">
|
||||
<a onClick={this.props.exportFile}><Icon name="floppy-o" fixedWidth /></a>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!store.isEmpty() && (
|
||||
<Tooltip title="Close archive" position="bottom">
|
||||
<a onClick={this.props.erase}><Icon name="close" fixedWidth /></a>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip title="Show Help" position="bottom">
|
||||
<Link to={`/help`}><Icon name="question" fixedWidth /></Link>
|
||||
</Tooltip>
|
||||
<Tooltip title="About Us" position="bottom">
|
||||
<a href="http://sheetjs.com"><img className="Logo" src="logo.png" height="24px" width="24px"/></a>
|
||||
</Tooltip>
|
||||
</div></div>
|
||||
</div>
|
||||
); }
|
||||
}
|
||||
// {this.props.idx && `>> ${store.getFixedPath(this.props.idx)}`}
|
||||
export default easyComp(ToolBar);
|
@ -1,21 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
.tree {
|
||||
padding: 0px 20px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
white-space: pre;
|
||||
height: calc(100vh - 55px);
|
||||
}
|
||||
.tree ul {
|
||||
padding: 0px;
|
||||
}
|
||||
.tree li {
|
||||
list-style-type:none;
|
||||
margin:0;
|
||||
position:relative;
|
||||
font-size: 12px;
|
||||
}
|
||||
.tree a {
|
||||
padding:10px 5px 0 0px;
|
||||
display:block;
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */
|
||||
import React, { Component } from 'react';
|
||||
import { easyComp } from 'react-easy-state';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Icon } from 'react-fa';
|
||||
import './TreeList.css';
|
||||
|
||||
import store from '../state';
|
||||
import { fix_string } from '../utils/misc';
|
||||
import Tooltip from '../components/Tooltip';
|
||||
import { decode_msi_name } from '../utils/misc';
|
||||
|
||||
class TreeList extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.exportFile = this.exportFile.bind(this);
|
||||
this.renameFile = this.renameFile.bind(this);
|
||||
this.erase = this.erase.bind(this);
|
||||
}
|
||||
exportFile() { this.props.exportFile(); }
|
||||
renameFile() { this.props.renameFile(); }
|
||||
erase() { this.props.erase(); }
|
||||
render() { if(store.isEmpty()) return (<div className="tree"> </div>);
|
||||
return (
|
||||
<div className="tree">
|
||||
|
||||
<b>{`Files in ${store.fname}`}</b><br />
|
||||
<ul>
|
||||
<li><Link to={`/`}><Icon name="list" fixedWidth /> <b>Show Manifest</b></Link></li>
|
||||
{store.getFileList().map(([f,,i]) => (
|
||||
<li key={i.toString()}>
|
||||
<Link to={`/view/${i}`}>
|
||||
<Icon name={i == this.props.idx ? "file" : "file-text-o"} fixedWidth />
|
||||
{f.replace(/[^/]*\//, "").charCodeAt(0) < 0x3800 || f.replace(/[^/]*\//, "").charCodeAt(0) > 0x4840 ?
|
||||
fix_string((store.isCFB() ? f.replace(/[^/]*\//, " ") : f) + (i == this.props.idx ? " >>": ""))
|
||||
: (
|
||||
<Tooltip title={`MSI: ${decode_msi_name(f.replace(/[^/]*\//, ""))}`} position="bottom">
|
||||
{fix_string((store.isCFB() ? f.replace(/[^/]*\//, " ") : f) + (i == this.props.idx ? " >>": ""))}
|
||||
</Tooltip>
|
||||
)}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<b>Archive Operations</b><br />
|
||||
<ul>
|
||||
<li><a onClick={this.exportFile}><Icon name="floppy-o" fixedWidth /> <b>Download Archive</b></a></li>
|
||||
<li><a onClick={this.renameFile}><Icon name="i-cursor" fixedWidth /> <b>Rename Archive</b></a></li>
|
||||
<li><a onClick={this.props.addFile}><Icon name="plus" fixedWidth /> <b>Add file to Archive</b></a></li>
|
||||
<li><b>(or drag and drop a file here)</b></li>
|
||||
<li><a onClick={this.erase}><Icon name="close" fixedWidth /> <b>Close Archive</b></a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default easyComp(TreeList);
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"allowJs": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"src/vendor/*",
|
||||
"node_modules",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|