tag1
This commit is contained in:
39
src/App.vue
Normal file
39
src/App.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial,
|
||||
"PingFang SC", "Hiragino Sans GB", "Heiti SC", "WenQuanYi Micro Hei",
|
||||
sans-serif;
|
||||
background-color: #dddddd;
|
||||
}
|
||||
|
||||
.monoFt {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
|
||||
#nprogress .bar {
|
||||
background: red !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
40
src/api/lock.js
Normal file
40
src/api/lock.js
Normal file
@@ -0,0 +1,40 @@
|
||||
// import Jquery from "jquery";
|
||||
import {aesEncrypt, md5} from "@/libs/secret";
|
||||
|
||||
// export function getSecretKey(key, password) {
|
||||
// console.log("getSecretKey");
|
||||
// let secretKey = '';
|
||||
// Jquery.ajax({
|
||||
// method: 'POST',
|
||||
// url: '/note/' + key + "/secretKey",
|
||||
// async: false,
|
||||
// contentType: 'application/json',
|
||||
// dataTeyp: 'json',
|
||||
// data: JSON.stringify({'password': password}),
|
||||
// success: function (data) {
|
||||
// secretKey = data;
|
||||
// },
|
||||
// error: function () {
|
||||
// alert('服务器链接异常 ');
|
||||
// },
|
||||
// });
|
||||
// return secretKey;
|
||||
// }
|
||||
|
||||
export function getStoreKey(key) {
|
||||
return md5(aesEncrypt(key, key));
|
||||
}
|
||||
|
||||
export function getSecretKey(cipher, password) {
|
||||
if(!password){
|
||||
password = '';
|
||||
}
|
||||
return md5(cipher + password);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
79
src/api/note.js
Normal file
79
src/api/note.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import axios from "axios";
|
||||
import Jquery from "jquery";
|
||||
import {getStoreKey} from "@/api/lock";
|
||||
import storage from "@/libs/storage";
|
||||
|
||||
export function saveNote(noteForm) {
|
||||
|
||||
let storeKey = getStoreKey(noteForm.key);
|
||||
let storeText = storage.local.getText(storeKey + '.text');
|
||||
let note = {
|
||||
"lock": storeText.substring(0, 1),
|
||||
"text": storeText.substring(35)
|
||||
}
|
||||
|
||||
console.log("n"+ storeText)
|
||||
|
||||
|
||||
return axios({
|
||||
url: '/note/' + noteForm.key,
|
||||
method: 'post',
|
||||
data: note
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// export function getSecretKey(key, password) {
|
||||
// console.log("getSecretKey");
|
||||
// let secretKey = '';
|
||||
// Jquery.ajax({
|
||||
// method: 'POST',
|
||||
// url: '/note/' + key + "/secretKey",
|
||||
// async: false,
|
||||
// contentType: 'application/json',
|
||||
// dataTeyp: 'json',
|
||||
// data: JSON.stringify({'password': password}),
|
||||
// success: function (data) {
|
||||
// secretKey = data;
|
||||
// },
|
||||
// error: function () {
|
||||
// alert('服务器链接异常 ');
|
||||
// },
|
||||
// });
|
||||
// return secretKey;
|
||||
// }
|
||||
|
||||
|
||||
export function getNoteMeta(key) {
|
||||
console.log("getNoteMeta");
|
||||
let noteMeta = {};
|
||||
Jquery.ajax({
|
||||
method: 'GET',
|
||||
url: '/note/' + key + "/noteMeta",
|
||||
async: false,
|
||||
success: function (data) {
|
||||
noteMeta = eval(data); //eval("(" + data + ")");
|
||||
},
|
||||
error: function () {
|
||||
noteMeta = null;
|
||||
},
|
||||
});
|
||||
return noteMeta;
|
||||
}
|
||||
|
||||
export function getKeyMeta() {
|
||||
console.log("getKeyMeta");
|
||||
let keyMeta = {};
|
||||
Jquery.ajax({
|
||||
method: 'GET',
|
||||
url: '/note/keyMeta',
|
||||
async: false,
|
||||
success: function (data) {
|
||||
keyMeta = eval(data); //eval("(" + data + ")");
|
||||
},
|
||||
error: function () {
|
||||
keyMeta = null;
|
||||
},
|
||||
});
|
||||
return keyMeta;
|
||||
}
|
||||
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
44
src/libs/noteStorage.js
Normal file
44
src/libs/noteStorage.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import {zip, aesEncrypt} from '../libs/secret'
|
||||
import storage from "@/libs/storage";
|
||||
import {getSecretKey} from "@/api/lock";
|
||||
import escapeHtml from "escape-html";
|
||||
|
||||
export function setStoreText(text, secret, password) {
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
password = "";
|
||||
}
|
||||
|
||||
text = "FLAGNOTE#" + text;
|
||||
|
||||
let secretKey = getSecretKey(secret.cipher, password);
|
||||
let storeText = aesEncrypt(text, secretKey);
|
||||
storeText = zip(storeText);
|
||||
|
||||
console.log("sssssssss" + secret.cipher)
|
||||
console.log("sssssssss" + password)
|
||||
console.log("sssssssss" + secretKey)
|
||||
console.log("s" + storeText)
|
||||
|
||||
let lock = '0';
|
||||
if (password) {
|
||||
lock = '1';
|
||||
}
|
||||
|
||||
storage.local.setText(secret.storeKey + '.text', lock + '|' + secret.cipher + '|' + storeText);
|
||||
}
|
||||
|
||||
|
||||
export function getEscapeText(text) {
|
||||
let textEscape = escapeHtml(text);
|
||||
textEscape = textEscape.replaceAll(" ", " ");
|
||||
textEscape = textEscape.replaceAll("\r\n", "<br/>");
|
||||
textEscape = textEscape.replaceAll("\r", "<br/>");
|
||||
textEscape = textEscape.replaceAll("\n", "<br/>");
|
||||
textEscape = textEscape.replaceAll("\t", " ");
|
||||
|
||||
return textEscape;
|
||||
}
|
||||
57
src/libs/secret.js
Normal file
57
src/libs/secret.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import CryptoJS from 'crypto-js'
|
||||
import pako from 'pako'
|
||||
|
||||
/**
|
||||
* @word 要加密的内容
|
||||
* @keyWord String 服务器随机返回的关键字
|
||||
* */
|
||||
|
||||
//加密
|
||||
export function md5(word, keyWord = 'F1agn0te') {
|
||||
let srcWords = CryptoJS.enc.Utf8.parse(word + '_' + keyWord);
|
||||
let encrypted = CryptoJS.MD5(srcWords);
|
||||
return encrypted.toString();
|
||||
}
|
||||
|
||||
//加密
|
||||
export function aesEncrypt(word, keyWord = 'F1agn0te') {
|
||||
let key = CryptoJS.enc.Utf8.parse(keyWord);
|
||||
let srcWords = CryptoJS.enc.Utf8.parse(word);
|
||||
let encrypted = CryptoJS.AES.encrypt(srcWords, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7});
|
||||
return encrypted.toString();
|
||||
}
|
||||
|
||||
//解密
|
||||
export function aesDecrypt(word, keyWord = 'F1agn0te') {
|
||||
let key = CryptoJS.enc.Utf8.parse(keyWord);
|
||||
let decrypt = CryptoJS.AES.decrypt(word, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7});
|
||||
return CryptoJS.enc.Utf8.stringify(decrypt).toString()
|
||||
}
|
||||
|
||||
export function encode(text) {
|
||||
return btoa(encodeURIComponent(text))
|
||||
}
|
||||
|
||||
export function decode(text) {
|
||||
return decodeURIComponent(atob(text))
|
||||
}
|
||||
|
||||
export function unzip(text) {
|
||||
let charData = text.split(',').map(function (x) {
|
||||
return parseInt(x)
|
||||
});
|
||||
let binData = new Uint8Array(charData);
|
||||
let data = pako.ungzip(binData);
|
||||
//text = String.fromCharCode.apply(null, new Uint8Array(data));
|
||||
text = new Uint8Array(data).reduce(function (data, byte) {
|
||||
return data + String.fromCharCode(byte);
|
||||
}, '');
|
||||
return text;
|
||||
}
|
||||
|
||||
export function zip(text) {
|
||||
text = pako.gzip(text, {to: 'string'});
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
46
src/libs/storage.js
Normal file
46
src/libs/storage.js
Normal file
@@ -0,0 +1,46 @@
|
||||
class Storage {
|
||||
constructor() {
|
||||
this.session = new Session();
|
||||
this.local = new Local();
|
||||
}
|
||||
}
|
||||
|
||||
class Session {
|
||||
|
||||
setObject(key, value) {
|
||||
sessionStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
getObject(key) {
|
||||
return JSON.parse(sessionStorage.getItem(key));
|
||||
}
|
||||
|
||||
setText(key, value) {
|
||||
sessionStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
getText(key) {
|
||||
return sessionStorage.getItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
class Local {
|
||||
setObject(key, value) {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
getObject(key) {
|
||||
return JSON.parse(localStorage.getItem(key));
|
||||
}
|
||||
|
||||
setText(key, value) {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
getText(key) {
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
const storage = new Storage();
|
||||
export default storage;
|
||||
41
src/main.js
Normal file
41
src/main.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import axios from 'axios'
|
||||
import VueAxios from 'vue-axios'
|
||||
import './plugins/iview.js'
|
||||
|
||||
//import NProgress from 'nprogress';
|
||||
//import 'nprogress/nprogress.css';
|
||||
|
||||
//顶部页面加载条
|
||||
|
||||
// NProgress.configure({
|
||||
// easing: 'ease',
|
||||
// speed: 500,
|
||||
// showSpinner: false,
|
||||
// trickleSpeed: 200,
|
||||
// minimum: 0.3
|
||||
// })
|
||||
|
||||
// //路由监听
|
||||
// router.beforeEach((to, from, next) => {
|
||||
// NProgress.start();
|
||||
// next();
|
||||
// });
|
||||
|
||||
// //路由跳转结束
|
||||
// router.afterEach(() => {
|
||||
// NProgress.done()
|
||||
// })
|
||||
|
||||
Vue.use(VueAxios, axios)
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
|
||||
|
||||
6
src/plugins/iview.js
Normal file
6
src/plugins/iview.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import Vue from 'vue'
|
||||
import ViewUI from 'view-design'
|
||||
|
||||
Vue.use(ViewUI)
|
||||
|
||||
import 'view-design/dist/styles/iview.css'
|
||||
201
src/plugins/tln/LICENSE
Normal file
201
src/plugins/tln/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
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 2018 Matheus Avellar
|
||||
|
||||
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.
|
||||
25
src/plugins/tln/README.md
Normal file
25
src/plugins/tln/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Textarea with Line Numbers
|
||||
|
||||
<p align="center"> <i>I'm definitely not very good at naming projects</i> </p>
|
||||
|
||||
---
|
||||
|
||||
This is a simple script to add dynamic line numbers to a textarea. By adding
|
||||
`tln.js` and `tln.css` (or their corresponding minified versions) to an HTML
|
||||
file and then calling the `TLN.append_line_numbers( id )` function, the
|
||||
`<textarea>` with the specified ID will be *numberified™*.
|
||||
|
||||
When and if you get sick of those pesky numbers on the side of your textarea,
|
||||
you can call `TLN.remove_line_numbers( id )` and they'll poof out of existence,
|
||||
as if they were never there.
|
||||
|
||||
You can check the [demo](https://matheusavellar.github.io/textarea-line-numbers/demo.html),
|
||||
or this sample screenshot of what it looks like (with some additional CSS
|
||||
styling):
|
||||
|
||||
<p align="center">
|
||||
<img alt="Screenshot of sample code in which each line is numbered accordingly" src="http://i.imgur.com/imEKehF.png"/>
|
||||
</p>
|
||||
|
||||
**Note:** Example code and file header ("`file.bc`") seen in screenshot are sold
|
||||
separately and may or may not be available at your local convenience store.
|
||||
36
src/plugins/tln/demo.html
Normal file
36
src/plugins/tln/demo.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title> Textarea with line numbers </title>
|
||||
<style type="text/css">
|
||||
/* none of this truly matters, except maybe the wrapper size */
|
||||
body{text-align:center}
|
||||
h1,button{margin:25px;}
|
||||
label,input{margin:0;vertical-align:middle;}
|
||||
#wrapper{border:1px solid red;position:relative;
|
||||
height:500px;width:500px;margin:15px auto}
|
||||
#wrapper::before {
|
||||
content:"wrapper";text-transform: uppercase;left:0px;
|
||||
position:absolute;top:-18px;font-size:16px;color:red; }
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="./tln.min.css"/>
|
||||
<script type="text/javascript" src="./tln.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1> Example </h1>
|
||||
<button onclick="TLN.append_line_numbers('test')"> NUMBERIFY! </button>
|
||||
<button onclick="TLN.remove_line_numbers('test')"> UN-NUMBERIFY! </button>
|
||||
<div id="wrapper">
|
||||
<textarea id="test" class="banana-cake"></textarea>
|
||||
</div>
|
||||
<label>
|
||||
Width:
|
||||
<input oninput="wrapper.style.width=this.value+'px';" type="range" value="500" min="100" max="700" />
|
||||
</label>
|
||||
<label>
|
||||
Height:
|
||||
<input oninput="wrapper.style.height=this.value+'px';" type="range" value="500" min="100" max="700" />
|
||||
</label>
|
||||
</body>
|
||||
</html>
|
||||
48
src/plugins/tln/tln.css
Normal file
48
src/plugins/tln/tln.css
Normal file
@@ -0,0 +1,48 @@
|
||||
.tln-active, .tln-wrapper, .tln-line {
|
||||
margin: 0;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
outline: 0;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
list-style: none;
|
||||
}
|
||||
.tln-active {
|
||||
display: inline-block;
|
||||
padding: 0.625em;
|
||||
width: calc(100% - 3em);
|
||||
height: 100%;
|
||||
font-size: 1em;
|
||||
line-height: 1.5;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
word-break: break-all;
|
||||
border: 1px solid #aeaeae;
|
||||
background-color: #fff;
|
||||
resize: none;
|
||||
overflow-wrap: normal;
|
||||
overflow-x: auto;
|
||||
white-space: pre;
|
||||
}
|
||||
.tln-wrapper {
|
||||
width: 3em;
|
||||
padding: 0.6875em 0.3125em 2.1875em;
|
||||
height: 100%;
|
||||
word-break: break-all;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
counter-reset: line;
|
||||
}
|
||||
.tln-line {
|
||||
width: 100%;
|
||||
display: block;
|
||||
text-align: right;
|
||||
line-height: 1.5;
|
||||
font-size: 1em;
|
||||
color: #aeaeae;
|
||||
}
|
||||
.tln-line::before {
|
||||
counter-increment: line;
|
||||
content: counter(line);
|
||||
font-size: 1em;
|
||||
user-select: none;
|
||||
}
|
||||
138
src/plugins/tln/tln.js
Normal file
138
src/plugins/tln/tln.js
Normal file
@@ -0,0 +1,138 @@
|
||||
const TLN = {
|
||||
eventList: {},
|
||||
update_line_numbers: function(ta, el) {
|
||||
// Let's check if there are more or less lines than before
|
||||
const line_count = ta.value.split("\n").length;
|
||||
const child_count = el.children.length;
|
||||
let difference = line_count - child_count;
|
||||
// If there is any positive difference, we need to add more line numbers
|
||||
if(difference > 0) {
|
||||
// Create a fragment to work with so we only have to update DOM once
|
||||
const frag = document.createDocumentFragment();
|
||||
// For each new line we need to add,
|
||||
while(difference > 0) {
|
||||
// Create a <span>, add TLN class name, append to fragment and
|
||||
// update difference
|
||||
const line_number = document.createElement("span");
|
||||
line_number.className = "tln-line";
|
||||
frag.appendChild(line_number);
|
||||
difference--;
|
||||
}
|
||||
// Append fragment (with <span> children) to our wrapper element
|
||||
el.appendChild(frag);
|
||||
}
|
||||
// If, however, there's negative difference, we need to remove line numbers
|
||||
while(difference < 0) {
|
||||
// Simple stuff, remove last child and update difference
|
||||
el.removeChild(el.lastChild);
|
||||
difference++;
|
||||
}
|
||||
},
|
||||
append_line_numbers: function(id) {
|
||||
// Get reference to desired <textarea>
|
||||
const ta = document.getElementById(id);
|
||||
// If getting reference to element fails, warn and leave
|
||||
if(ta == null) {
|
||||
return console.warn("[tln.js] Couldn't find textarea of id '"+id+"'");
|
||||
}
|
||||
// If <textarea> already has TLN active, warn and leave
|
||||
if(ta.className.indexOf("tln-active") != -1) {
|
||||
return console.warn("[tln.js] textarea of id '"+id+"' is already numbered");
|
||||
}
|
||||
// Otherwise, we're safe to add the class name and clear inline styles
|
||||
ta.classList.add("tln-active");
|
||||
ta.style = {};
|
||||
|
||||
// Create line numbers wrapper, insert it before <textarea>
|
||||
const el = document.createElement("div");
|
||||
el.className = "tln-wrapper";
|
||||
ta.parentNode.insertBefore(el, ta);
|
||||
// Call update to actually insert line numbers to the wrapper
|
||||
TLN.update_line_numbers(ta, el);
|
||||
// Initialize event listeners list for this element ID, so we can remove
|
||||
// them later if needed
|
||||
TLN.eventList[id] = [];
|
||||
|
||||
// Constant list of input event names so we can iterate
|
||||
const __change_evts = [
|
||||
"propertychange", "input", "keydown", "keyup"
|
||||
];
|
||||
// Default handler for input events
|
||||
const __change_hdlr = function(ta, el) {
|
||||
return function(e) {
|
||||
// If pressed key is Left Arrow (when cursor is on the first character),
|
||||
// or if it's Enter/Home, then we set horizontal scroll to 0
|
||||
// Check for .keyCode, .which, .code and .key, because the web is a mess
|
||||
// [Ref] stackoverflow.com/a/4471635/4824627
|
||||
if((+ta.scrollLeft==10 && (e.keyCode==37||e.which==37
|
||||
||e.code=="ArrowLeft"||e.key=="ArrowLeft"))
|
||||
|| e.keyCode==36||e.which==36||e.code=="Home"||e.key=="Home"
|
||||
|| e.keyCode==13||e.which==13||e.code=="Enter"||e.key=="Enter"
|
||||
|| e.code=="NumpadEnter")
|
||||
ta.scrollLeft = 0;
|
||||
// Whether we scrolled or not, let's check for any line count updates
|
||||
TLN.update_line_numbers(ta, el);
|
||||
}
|
||||
}(ta, el);
|
||||
|
||||
// Finally, iterate through those event names, and add listeners to
|
||||
// <textarea> and to events list
|
||||
/// TODO: Performance gurus: is this suboptimal? Should we only add a few
|
||||
/// listeners? I feel the update method is optimal enough for this to not
|
||||
/// impact too much things.
|
||||
for(let i = __change_evts.length - 1; i >= 0; i--) {
|
||||
ta.addEventListener(__change_evts[i], __change_hdlr);
|
||||
TLN.eventList[id].push({
|
||||
evt: __change_evts[i],
|
||||
hdlr: __change_hdlr
|
||||
});
|
||||
}
|
||||
|
||||
// Constant list of scroll event names so we can iterate
|
||||
const __scroll_evts = [ "change", "mousewheel", "scroll" ];
|
||||
// Default handler for scroll events (pretty self explanatory)
|
||||
const __scroll_hdlr = function(ta, el) {
|
||||
return function() { el.scrollTop = ta.scrollTop; }
|
||||
}(ta, el);
|
||||
// Just like before, iterate and add listeners to <textarea> and to list
|
||||
/// TODO: Also just like before: performance?
|
||||
for(let i = __scroll_evts.length - 1; i >= 0; i--) {
|
||||
ta.addEventListener(__scroll_evts[i], __scroll_hdlr);
|
||||
TLN.eventList[id].push({
|
||||
evt: __scroll_evts[i],
|
||||
hdlr: __scroll_hdlr
|
||||
});
|
||||
}
|
||||
},
|
||||
remove_line_numbers: function(id) {
|
||||
// Get reference to <textarea>
|
||||
const ta = document.getElementById(id);
|
||||
// If getting reference to element fails, warn and leave
|
||||
if(ta == null) {
|
||||
return console.warn("[tln.js] Couldn't find textarea of id '"+id+"'");
|
||||
}
|
||||
// If <textarea> already doesn't have TLN active, warn and leave
|
||||
if(ta.className.indexOf("tln-active") == -1) {
|
||||
return console.warn("[tln.js] textarea of id '"+id+"' isn't numbered");
|
||||
}
|
||||
// Otherwise, remove class name
|
||||
ta.classList.remove("tln-active");
|
||||
|
||||
// Remove previous sibling if it's our wrapper (otherwise, I guess 'woops'?)
|
||||
const __wrapper_chck = ta.previousSibling;
|
||||
if(__wrapper_chck.className == "tln-wrapper")
|
||||
__wrapper_chck.remove();
|
||||
|
||||
// If somehow there's no event listeners list, we can leave
|
||||
if(!TLN.eventList[id]) return;
|
||||
// Otherwise iterate through listeners list and remove each one
|
||||
for(let i = TLN.eventList[id].length - 1; i >= 0; i--) {
|
||||
const evt = TLN.eventList[id][i];
|
||||
ta.removeEventListener(evt.evt, evt.hdlr);
|
||||
}
|
||||
// Finally, delete the listeners list for that ID
|
||||
delete TLN.eventList[id];
|
||||
}
|
||||
}
|
||||
|
||||
export default TLN;
|
||||
1
src/plugins/tln/tln.min.css
vendored
Normal file
1
src/plugins/tln/tln.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.tln-active,.tln-wrapper,.tln-line{margin:0;border:0;padding:0;outline:0;box-sizing:border-box;vertical-align:middle;list-style:none}.tln-active{display:inline-block;padding:.625em;width:calc(100% - 3em);height:100%;word-break:break-all;border:1px solid #aeaeae;background-color:#fff;resize:none;overflow-wrap:normal;overflow-x:auto;white-space:pre;font:1em/1.5 "Roboto Mono",monospace}.tln-wrapper{width:3em;padding:.6875em .3125em 2.1875em;height:100%;word-break:break-all;overflow:hidden;display:inline-block;counter-reset:line}.tln-line{width:100%;display:block;text-align:right;line-height:1.5;font-size:1em;color:#aeaeae}.tln-line::before{counter-increment:line;content:counter(line);font-size:1em;user-select:none}
|
||||
1
src/plugins/tln/tln.min.js
vendored
Normal file
1
src/plugins/tln/tln.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
const TLN={eventList:{},update_line_numbers:function(e,t){let n=e.value.split("\n").length-t.children.length;if(n>0){const e=document.createDocumentFragment();for(;n>0;){const t=document.createElement("span");t.className="tln-line",e.appendChild(t),n--}t.appendChild(e)}for(;n<0;)t.removeChild(t.lastChild),n++},append_line_numbers:function(e){const t=document.getElementById(e);if(null==t)return console.warn("[tln.js] Couldn't find textarea of id '"+e+"'");if(-1!=t.className.indexOf("tln-active"))return console.warn("[tln.js] textarea of id '"+e+"' is already numbered");t.classList.add("tln-active"),t.style={};const n=document.createElement("div");n.className="tln-wrapper",t.parentNode.insertBefore(n,t),TLN.update_line_numbers(t,n),TLN.eventList[e]=[];const l=["propertychange","input","keydown","keyup"],o=function(e,t){return function(n){(10!=+e.scrollLeft||37!=n.keyCode&&37!=n.which&&"ArrowLeft"!=n.code&&"ArrowLeft"!=n.key)&&36!=n.keyCode&&36!=n.which&&"Home"!=n.code&&"Home"!=n.key&&13!=n.keyCode&&13!=n.which&&"Enter"!=n.code&&"Enter"!=n.key&&"NumpadEnter"!=n.code||(e.scrollLeft=0),TLN.update_line_numbers(e,t)}}(t,n);for(let n=l.length-1;n>=0;n--)t.addEventListener(l[n],o),TLN.eventList[e].push({evt:l[n],hdlr:o});const r=["change","mousewheel","scroll"],s=function(e,t){return function(){t.scrollTop=e.scrollTop}}(t,n);for(let n=r.length-1;n>=0;n--)t.addEventListener(r[n],s),TLN.eventList[e].push({evt:r[n],hdlr:s})},remove_line_numbers:function(e){const t=document.getElementById(e);if(null==t)return console.warn("[tln.js] Couldn't find textarea of id '"+e+"'");if(-1==t.className.indexOf("tln-active"))return console.warn("[tln.js] textarea of id '"+e+"' isn't numbered");t.classList.remove("tln-active");const n=t.previousSibling;if("tln-wrapper"==n.className&&n.remove(),TLN.eventList[e]){for(let n=TLN.eventList[e].length-1;n>=0;n--){const l=TLN.eventList[e][n];t.removeEventListener(l.evt,l.hdlr)}delete TLN.eventList[e]}}};
|
||||
80
src/router/index.js
Normal file
80
src/router/index.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
import EditNote from '@/views/EditNote.vue'
|
||||
import ViewNote from '@/views/ViewNote.vue'
|
||||
import ErrorView from '@/views/ErrorView.vue'
|
||||
import {getKeyMeta, getNoteMeta} from "@/api/note";
|
||||
import {getStoreKey} from "@/api/lock";
|
||||
import storage from "@/libs/storage";
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
let keyMeta = null;
|
||||
|
||||
function getNoteView() {
|
||||
let path = location.pathname;
|
||||
if (null != keyMeta) {
|
||||
path = "/" + keyMeta.key;
|
||||
}
|
||||
|
||||
let reg = /^\/[abcdefhikmnopqstuvwxyz23456789]{16}$/
|
||||
if (!reg.test(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let key = path.substr(1, path.length - 1);
|
||||
|
||||
let storeKey = getStoreKey(key);
|
||||
if (keyMeta) {
|
||||
storage.session.setObject(storeKey + '.keyMeta', keyMeta);
|
||||
return EditNote;
|
||||
}
|
||||
|
||||
let noteMeta = null;
|
||||
noteMeta = getNoteMeta(key);
|
||||
if (!noteMeta || !noteMeta.key) {
|
||||
return EditNote;
|
||||
}
|
||||
|
||||
storage.session.setObject(storeKey + '.noteMeta', noteMeta);
|
||||
return ViewNote;
|
||||
}
|
||||
|
||||
|
||||
function getHomeRedirect() {
|
||||
let path = location.pathname;
|
||||
if (path != '/') {
|
||||
return;
|
||||
}
|
||||
let km = getKeyMeta();
|
||||
km.isNew = true;
|
||||
//sessionStorage.setItem(km.getKey() + '.keyMeta', km);
|
||||
keyMeta = km;
|
||||
return '/' + keyMeta.key;
|
||||
}
|
||||
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
redirect: getHomeRedirect(),
|
||||
},
|
||||
{
|
||||
path: '/error',
|
||||
name: 'error',
|
||||
component: ErrorView,
|
||||
},
|
||||
{
|
||||
path: '/:name([abcdefhikmnopqstuvwxyz23456789]{16})',
|
||||
name: 'note',
|
||||
component: getNoteView(),
|
||||
}
|
||||
]
|
||||
|
||||
const router = new VueRouter({
|
||||
routes,
|
||||
mode: 'history'
|
||||
})
|
||||
|
||||
export default router
|
||||
478
src/views/EditNote.vue
Normal file
478
src/views/EditNote.vue
Normal file
@@ -0,0 +1,478 @@
|
||||
<style scoped>
|
||||
.layout {
|
||||
height: 100%;
|
||||
background: #dddddd;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #dddddd;
|
||||
}
|
||||
|
||||
.ivu-layout-header {
|
||||
line-height: normal;
|
||||
height: auto;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.content {
|
||||
background: #dddddd;
|
||||
}
|
||||
|
||||
.ivu-layout-content {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.layout-footer-center {
|
||||
background: #dddddd;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ivu-card-bordered {
|
||||
border: 0px solid #dcdee2;
|
||||
border-color: #e8eaec;
|
||||
}
|
||||
|
||||
.noteUrl {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
font-family: Merriweather;
|
||||
|
||||
}
|
||||
|
||||
.noteKey {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
font-family: "Bitstream Vera Sans Mono", Consolas, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei"
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
#noteText {
|
||||
color: black;
|
||||
padding: 10px;
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
background: white;
|
||||
min-width: 200px;
|
||||
border-radius: 0px;
|
||||
overflow-y: auto;
|
||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
|
||||
min-height: 400px;
|
||||
border: 0px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
#noteText::selection {
|
||||
background: firebrick;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#noteText::-moz-selection {
|
||||
background: firebrick;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<template>
|
||||
<div class="layout">
|
||||
<Layout>
|
||||
<Affix :offset-top="0">
|
||||
|
||||
|
||||
<Header class="header">
|
||||
<div>
|
||||
<Row>
|
||||
<Col :xs="{ span: 24, offset: 0 }" :sm="{ span: 22, offset: 1 }" :md="{ span: 20, offset: 2 }"
|
||||
:lg="{ span: 18, offset: 3 }" :xl="{ span: 16, offset: 4 }" :xxl="{ span: 16, offset: 4 }">
|
||||
|
||||
|
||||
<div style="background: white;width:100%;height:40px;">
|
||||
<img style="height:40px;float:left;" src="favicon.png">
|
||||
|
||||
<div style="height: 40px;float: left;padding-top: 15px;padding-left: 20px;">
|
||||
<Icon type="md-create" style="font-size: 22px;color:red" /><a class="noteKey">{{ noteForm.key }}</a>
|
||||
</div>
|
||||
|
||||
<div style="float:right;width:auto;">
|
||||
<Button-group size="large">
|
||||
<Button type="error" :loading="loading" style="margin-left:5px; border-radius: 0px;font-size: 24px;"
|
||||
@click="submitNote()" icon="md-cloud-upload"></Button>
|
||||
<Button type="error" style="margin-left:5px; border-radius: 0px;font-size: 24px;"
|
||||
@click="createNote()" icon="md-add"></Button>
|
||||
|
||||
</Button-group>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
<div style="float:right;width:auto;">
|
||||
<Button-group size="default">
|
||||
<Button type="error" @click="unLockNote()" icon="md-eye"></Button>
|
||||
<Button type="error" @click="lockNote()" icon="md-eye-off"></Button>
|
||||
</Button-group>
|
||||
|
||||
</div>
|
||||
-->
|
||||
|
||||
|
||||
<!--
|
||||
<div style="width:auto;float:right;font-size: 24px;">
|
||||
<Button-group vertical size="large" style="position:absolute;right;0px;top:0px;">
|
||||
<Button type="error" style="border-radius: 0px;font-size: 24px;" icon="md-add"
|
||||
@click="showExt = true"></Button>
|
||||
<Button type="error" v-show="showExt" style="border-radius: 0px;font-size: 24px;"
|
||||
icon="md-cloud-upload"></Button>
|
||||
<Button type="error" v-show="showExt" style="border-radius: 0px;font-size: 24px;"
|
||||
icon="logo-googleplus"></Button>
|
||||
<Button type="error" v-show="showExt" style="border-radius: 0px;font-size: 24px;"
|
||||
icon="logo-tumblr"></Button>
|
||||
</Button-group>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Header>
|
||||
</Affix>
|
||||
<Content class="content">
|
||||
<div style="min-height: 650px;">
|
||||
<Row>
|
||||
<Col :xs="{ span: 24, offset: 0 }" :sm="{ span: 22, offset: 1 }" :md="{ span: 20, offset: 2 }"
|
||||
:lg="{ span: 18, offset: 3 }" :xl="{ span: 16, offset: 4 }" :xxl="{ span: 16, offset: 4 }">
|
||||
<Card :padding="0">
|
||||
<Form :model="noteForm" :label-width="80">
|
||||
<div id="wrapper" style="border-left: 3px solid #FF3366;">
|
||||
|
||||
<Input element-id="noteText" type="textarea" :border="false" v-model="noteForm.text"
|
||||
:autosize="{ minRows: 30, maxRows: 1024 }" placeholder="Enter something..." v-on:input="log"
|
||||
@on-keydown="down" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</Form>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
</Row>
|
||||
</div>
|
||||
|
||||
</Content>
|
||||
|
||||
<Footer class="layout-footer-center">2022 © openif.com</Footer>
|
||||
</Layout>
|
||||
<Modal v-model="modal1" title="Common Modal dialog box title" @on-ok="ok" @on-cancel="cancel">
|
||||
|
||||
<p>Content of dialog</p>
|
||||
<i-switch true-color="#ff4949" @on-change="change">
|
||||
<Icon type="md-lock" slot="open"></Icon>
|
||||
<Icon type="md-unlock" slot="close"></Icon>
|
||||
</i-switch>
|
||||
<Input v-show="showPassword" maxlength="10" placeholder="Enter password..."
|
||||
style="width: 150px;margin-left:20px;" />
|
||||
<p>Content of dialog</p>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { unzip, aesDecrypt } from '../libs/secret'
|
||||
// import html2canvas from "html2canvas";
|
||||
import Jquery from "jquery";
|
||||
import { saveNote } from "@/api/note";
|
||||
import { getSecretKey, getStoreKey } from "@/api/lock";
|
||||
import storage from "@/libs/storage";
|
||||
import { setStoreText } from "@/libs/noteStorage";
|
||||
|
||||
export default {
|
||||
name: 'EditNote',
|
||||
props: {},
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
modal1: false,
|
||||
showExt: false,
|
||||
showPassword: false,
|
||||
noteForm: {
|
||||
text: '',
|
||||
key: '',
|
||||
},
|
||||
secret: {
|
||||
storeKey: '',
|
||||
secretKey: '',
|
||||
cipher: '',
|
||||
},
|
||||
state: {
|
||||
lock: '0',
|
||||
|
||||
locking: '0',
|
||||
|
||||
commited: '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.noteForm.key = this.$route.params.name;
|
||||
let storeKey = getStoreKey(this.noteForm.key);
|
||||
this.secret.storeKey = storeKey;
|
||||
|
||||
let keyMeta = storage.session.getObject(storeKey + '.keyMeta');
|
||||
if (keyMeta) {
|
||||
this.state.lock = '0';
|
||||
this.secret.cipher = keyMeta.cipher;
|
||||
storage.local.setText(storeKey + '.text', "0|" + this.secret.cipher);
|
||||
storage.session.setObject(storeKey + '.keyMeta', null);
|
||||
} else {
|
||||
let storeText = storage.local.getText(storeKey + '.text');
|
||||
this.state.lock = storeText.substring(0, 1);
|
||||
this.secret.cipher = storeText.substring(2, 34)
|
||||
}
|
||||
|
||||
if (this.state.lock == "1") {
|
||||
this.noteForm.text = "*****lock*****";
|
||||
return;
|
||||
} else {
|
||||
this.loadText();
|
||||
}
|
||||
|
||||
|
||||
this.bindEvent();
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
updated() {
|
||||
},
|
||||
beforeDestroy() {
|
||||
},
|
||||
destroyed() {
|
||||
},
|
||||
computed: {},
|
||||
watch: {},
|
||||
methods: {
|
||||
change(status) {
|
||||
this.showPassword = status;
|
||||
},
|
||||
ok() {
|
||||
|
||||
},
|
||||
cancel() {
|
||||
|
||||
},
|
||||
loadText() {
|
||||
let secretKey = getSecretKey(this.secret.cipher);
|
||||
let storeText = storage.local.getText(this.secret.storeKey + '.text');
|
||||
|
||||
if (null != storeText && '' != storeText) {
|
||||
let lock = storeText.substring(0, 1);
|
||||
if (lock == "1") {
|
||||
alert("note is encrypt!");
|
||||
return;
|
||||
}
|
||||
|
||||
//let cipher = storeText.substring(2,34)
|
||||
|
||||
storeText = storeText.substring(35);
|
||||
|
||||
storeText = unzip(storeText);
|
||||
let plainText = aesDecrypt(storeText, secretKey);
|
||||
if (plainText.startsWith("FLAGNOTE#")) {
|
||||
this.noteForm.text = plainText.substring(9);
|
||||
this.state.lock = "0";
|
||||
} else {
|
||||
if (this.state.lock == "1") {
|
||||
alert("password is wrong!")
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
log() {
|
||||
setStoreText(this.noteForm.text, this.secret);
|
||||
},
|
||||
down(event) {
|
||||
if (event.keyCode == 9) {
|
||||
let start = event.currentTarget.selectionStart, end = event.currentTarget.selectionEnd;
|
||||
let text = event.currentTarget.value;
|
||||
let tab = ' ';//\t
|
||||
text = text.substr(0, start) + tab + text.substr(start);
|
||||
event.currentTarget.value = text;
|
||||
event.currentTarget.selectionStart = start + tab.length;
|
||||
event.currentTarget.selectionEnd = end + tab.length;
|
||||
event.stopPropagation();
|
||||
if (event.preventDefault) {
|
||||
event.preventDefault();
|
||||
}
|
||||
this.noteForm.text = event.currentTarget.value;
|
||||
setStoreText(event.currentTarget.value, this.secret);
|
||||
|
||||
// html2canvas(document.body).then(function (canvas) {
|
||||
// canvas;
|
||||
// //document.body.appendChild(canvas);
|
||||
// });
|
||||
|
||||
} else if (event.ctrlKey && event.which == 13) {
|
||||
this.save().then(res => {
|
||||
if (res) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
save() {
|
||||
this.noteForm.cipher = this.secret.cipher;
|
||||
this.noteForm.lock = this.state.lock;
|
||||
return saveNote(this.noteForm);
|
||||
},
|
||||
submitNote() {
|
||||
if (this.state.locking == "1") {
|
||||
return;
|
||||
}
|
||||
this.state.locking = "1";
|
||||
|
||||
this.loading = true;
|
||||
|
||||
this.save().then(res => {
|
||||
if (res) {
|
||||
location.reload();
|
||||
this.state.locking = "0";
|
||||
}
|
||||
});
|
||||
},
|
||||
lockNote() {
|
||||
if (this.state.locking == "1") {
|
||||
return;
|
||||
}
|
||||
this.state.locking = "1";
|
||||
|
||||
if (this.state.lock == "1") {
|
||||
this.state.locking = "0";
|
||||
return;
|
||||
}
|
||||
|
||||
let password = '123456';
|
||||
|
||||
setStoreText(this.noteForm.text, this.secret, password);
|
||||
|
||||
this.state.lock = "1"
|
||||
|
||||
this.noteForm.text = '******lock******';
|
||||
|
||||
|
||||
var noteText = document.getElementById("noteText");
|
||||
|
||||
noteText.select();
|
||||
noteText.selectionStart = 0;
|
||||
noteText.selectionEnd = 1;
|
||||
|
||||
|
||||
this.state.locking = "0";
|
||||
|
||||
},
|
||||
unLockNote() {
|
||||
if (this.state.locking == "1") {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.locking = "1";
|
||||
|
||||
if (this.state.lock == "0") {
|
||||
this.state.locking = "0";
|
||||
return;
|
||||
}
|
||||
|
||||
let password = '123456';
|
||||
|
||||
let storeText = storage.local.getText(this.secret.storeKey + '.text');
|
||||
if (storeText) {
|
||||
let lock = storeText.substring(0, 1);
|
||||
|
||||
if (!lock) {
|
||||
alert("note is not encrypt!");
|
||||
}
|
||||
|
||||
storeText = storeText.substring(35);
|
||||
|
||||
storeText = unzip(storeText);
|
||||
|
||||
let secretKey = getSecretKey(this.secret.cipher, password);
|
||||
let plainText = aesDecrypt(storeText, secretKey);
|
||||
if (plainText.startsWith("FLAGNOTE#")) {
|
||||
lock = "0";
|
||||
} else {
|
||||
let secretKey = getSecretKey(this.secret.cipher);
|
||||
let plainText = aesDecrypt(storeText, secretKey);
|
||||
if (plainText.startsWith("FLAGNOTE#")) {
|
||||
lock = "0";
|
||||
} else {
|
||||
lock = "1";
|
||||
}
|
||||
}
|
||||
|
||||
if (lock == '0') {
|
||||
this.noteForm.text = plainText.substring(9);
|
||||
this.state.lock = "0";
|
||||
setStoreText(this.noteForm.text, this.secret);
|
||||
|
||||
this.state.locking = "0";
|
||||
} else {
|
||||
alert("password is wrong!")
|
||||
this.state.locking = "0";
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
alert("storeText is deleted!")
|
||||
this.state.locking = "0";
|
||||
}
|
||||
|
||||
},
|
||||
createNote() {
|
||||
window.open("/");
|
||||
},
|
||||
bindEvent() {
|
||||
if (document.body.createTextRange) {
|
||||
Jquery(document).keydown(function (e) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.keyCode == 65) {
|
||||
e.preventDefault();
|
||||
|
||||
var element = document.getElementById("noteText");
|
||||
|
||||
element.select();
|
||||
|
||||
}
|
||||
});
|
||||
} else if (window.getSelection) {
|
||||
|
||||
Jquery(document).keydown(function (e) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.keyCode == 65) {
|
||||
e.preventDefault();
|
||||
|
||||
var element = document.getElementById("noteText");
|
||||
|
||||
element.select();
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
//alert('none');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
17
src/views/ErrorView.vue
Normal file
17
src/views/ErrorView.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
error
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ErrorView',
|
||||
components: {},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
19
src/views/HomeView.vue
Normal file
19
src/views/HomeView.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
home
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HomeView',
|
||||
components: {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
created() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
327
src/views/ViewNote.vue
Normal file
327
src/views/ViewNote.vue
Normal file
@@ -0,0 +1,327 @@
|
||||
<style scoped>
|
||||
.layout {
|
||||
height: 100%;
|
||||
background: #dddddd;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: #dddddd;
|
||||
}
|
||||
|
||||
.ivu-layout-header {
|
||||
line-height: normal;
|
||||
height: auto;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
|
||||
.content {
|
||||
background: #dddddd;
|
||||
}
|
||||
|
||||
.ivu-layout-content {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.layout-footer-center {
|
||||
background: #dddddd;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.ivu-card-bordered {
|
||||
border: 0px solid #dcdee2;
|
||||
border-color: #e8eaec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.noteKey {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
font-family: "Bitstream Vera Sans Mono", Consolas, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei"
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
#noteText {
|
||||
color: black;
|
||||
padding: 10px;
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
background: white;
|
||||
min-width: 200px;
|
||||
border-radius: 0px;
|
||||
overflow-y: auto;
|
||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
|
||||
/*min-height: 400px;*/
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
#noteText::selection {
|
||||
background: firebrick;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#noteText::-moz-selection {
|
||||
background: firebrick;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#noteText br::selection {
|
||||
background: firebrick;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#noteText br::-moz-selection {
|
||||
background: firebrick;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
<template>
|
||||
<div class="layout" onkeydown="keydown">
|
||||
<Layout>
|
||||
<Affix :offset-top="0">
|
||||
|
||||
|
||||
<Header class="header">
|
||||
<div>
|
||||
<Row>
|
||||
<Col :xs="{ span: 24, offset: 0}" :sm="{ span: 22, offset: 1 }" :md="{ span: 20, offset: 2 }"
|
||||
:lg="{ span: 18, offset: 3 }" :xl="{ span: 16, offset: 4 }" :xxl="{ span: 16, offset: 4 }">
|
||||
|
||||
|
||||
<div style="background: white;width:100%;height:40px;">
|
||||
<img style="height:40px;float:left;" src="favicon.png">
|
||||
|
||||
<div style="height: 40px;float: left;padding-top: 15px;padding-left: 20px;">
|
||||
<Icon type="md-cloud-done" style="font-size: 22px;color:red" /><a class="noteKey">{{ noteForm.key
|
||||
}}</a>
|
||||
</div>
|
||||
|
||||
|
||||
<div style="float:right;width:auto;">
|
||||
<Button-group size="large">
|
||||
<Button type="error" style="margin-left:5px; border-radius: 0px;font-size: 24px;"
|
||||
@click="deleteNote()" icon="md-trash"></Button>
|
||||
<Button type="error" style="margin-left:5px; border-radius: 0px;font-size: 24px;"
|
||||
@click="createNote()" icon="md-add"></Button>
|
||||
</Button-group>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
<div style="float:right;width:auto;">
|
||||
<Button-group size="default">
|
||||
<Button type="error" @click="unLockNote()" icon="md-eye"></Button>
|
||||
<Button type="error" @click="lockNote()" icon="md-eye-off"></Button>
|
||||
</Button-group>
|
||||
|
||||
</div>
|
||||
-->
|
||||
|
||||
|
||||
<!--
|
||||
<div style="width:auto;float:right;font-size: 24px;">
|
||||
<Button-group vertical size="large" style="position:absolute;right;0px;top:0px;">
|
||||
<Button type="error" style="border-radius: 0px;font-size: 24px;" icon="md-add"
|
||||
@click="showExt = true"></Button>
|
||||
<Button type="error" v-show="showExt" style="border-radius: 0px;font-size: 24px;"
|
||||
icon="md-cloud-upload"></Button>
|
||||
<Button type="error" v-show="showExt" style="border-radius: 0px;font-size: 24px;"
|
||||
icon="logo-googleplus"></Button>
|
||||
<Button type="error" v-show="showExt" style="border-radius: 0px;font-size: 24px;"
|
||||
icon="logo-tumblr"></Button>
|
||||
</Button-group>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Header>
|
||||
</Affix>
|
||||
<Content class="content">
|
||||
<div style="min-height: 650px;">
|
||||
<Row>
|
||||
<Col :xs="{ span: 24, offset: 0}" :sm="{ span: 22, offset: 1 }" :md="{ span: 20, offset: 2 }"
|
||||
:lg="{ span: 18, offset: 3 }" :xl="{ span: 16, offset: 4 }" :xxl="{ span: 16, offset: 4 }">
|
||||
<Card :padding="0">
|
||||
<div style="border-left: 3px solid #FF3366;">
|
||||
<div id="noteText" style="text-align: left;min-height: 650px;" class="monoFt"
|
||||
v-html="this.noteForm.escapeText">
|
||||
view2
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Content>
|
||||
|
||||
|
||||
|
||||
|
||||
</Layout>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import { aesDecrypt, md5, unzip } from "@/libs/secret";
|
||||
import Jquery from "jquery";
|
||||
import { getSecretKey, getStoreKey } from "@/api/lock";
|
||||
import storage from "@/libs/storage";
|
||||
import { getEscapeText } from "@/libs/noteStorage";
|
||||
|
||||
export default {
|
||||
name: 'ViewNote',
|
||||
components: {},
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
noteForm: {
|
||||
url: '',
|
||||
text: '',
|
||||
escapeText: '',
|
||||
key: '',
|
||||
md5: '',
|
||||
lock: '0',
|
||||
},
|
||||
secret: {
|
||||
storeKey: '',
|
||||
secretKey: '',
|
||||
cipher: '',
|
||||
},
|
||||
state: {
|
||||
lock: '0',
|
||||
locking: '0',
|
||||
commited: '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.noteForm.key = this.$route.params.name;
|
||||
let storeKey = getStoreKey(this.noteForm.key);
|
||||
this.secret.storeKey = storeKey;
|
||||
let noteMeta = storage.session.getObject(storeKey + '.noteMeta');
|
||||
if (noteMeta) {
|
||||
this.state.lock = noteMeta.lock;
|
||||
this.secret.cipher = noteMeta.cipher;
|
||||
this.noteForm.md5 = noteMeta.md5;
|
||||
this.loadText();
|
||||
}
|
||||
|
||||
this.bindEvent();
|
||||
},
|
||||
methods: {
|
||||
decryptNote() {
|
||||
// let password = "123456";
|
||||
// let secretKey = getSecretKey(this.noteForm.key, password);
|
||||
// this.loadText(secretKey);
|
||||
|
||||
},
|
||||
createNote() {
|
||||
window.open("/");
|
||||
},
|
||||
loadText() {
|
||||
|
||||
let password;
|
||||
|
||||
if (this.noteForm.lock == "1") {
|
||||
password = "123456"
|
||||
}
|
||||
|
||||
|
||||
if (!password) {
|
||||
password = "";
|
||||
}
|
||||
let secretKey = getSecretKey(this.secret.cipher, password);
|
||||
let storeText = storage.local.getText(this.secret.storeKey + '.text');
|
||||
if (!storeText || md5(storeText.substring(35)) != this.noteForm.md5) {
|
||||
let note = this.getNote(this.noteForm.key);
|
||||
storeText = this.noteForm.lock + '|' + this.secret.cipher + '|' + note.text;
|
||||
storage.local.setText(this.secret.storeKey + '.text', storeText);
|
||||
}
|
||||
|
||||
if (storeText) {
|
||||
storeText = unzip(storeText.substring(35));
|
||||
let plainText = aesDecrypt(storeText, secretKey);
|
||||
if (plainText.startsWith("FLAGNOTE#")) {
|
||||
this.noteForm.text = plainText.substring(9);
|
||||
let escapeText = getEscapeText(this.noteForm.text);
|
||||
this.noteForm.escapeText = escapeText;
|
||||
}
|
||||
}
|
||||
},
|
||||
getNote(key) {
|
||||
let noteObject;
|
||||
Jquery.ajax({
|
||||
url: '/note/' + key,
|
||||
async: false,
|
||||
success: function (data) {
|
||||
noteObject = data;
|
||||
}
|
||||
});
|
||||
return noteObject;
|
||||
},
|
||||
bindEvent() {
|
||||
if (document.body.createTextRange) {
|
||||
Jquery(document).keydown(function (e) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.keyCode == 65) {
|
||||
e.preventDefault();
|
||||
|
||||
var element = document.getElementById("noteText");
|
||||
let range = document.body.createTextRange();
|
||||
range.moveToElementText(element);
|
||||
range.select();
|
||||
|
||||
}
|
||||
});
|
||||
} else if (window.getSelection) {
|
||||
|
||||
Jquery(document).keydown(function (e) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.keyCode == 65) {
|
||||
e.preventDefault();
|
||||
|
||||
var element = document.getElementById("noteText");
|
||||
|
||||
let selection = window.getSelection();
|
||||
let range = document.createRange();
|
||||
range.selectNodeContents(element);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
//alert('none');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user