| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- <template>
- <section>
- <h3 v-text="i18n('labelDataExport')"></h3>
- <div class="export-list">
- <div
- v-for="(item, index) in items"
- :key="index"
- class="ellipsis"
- :class="{active: item.active}"
- @click="item.active = !item.active"
- v-text="getName(item)"
- />
- </div>
- <button v-text="i18n('buttonAllNone')" @click="toggleSelection()"></button>
- <button v-text="i18n('buttonExportData')" @click="exportData" :disabled="exporting"></button>
- <label class="ml-1">
- <setting-check name="exportValues" />
- <span v-text="i18n('labelExportScriptData')"></span>
- </label>
- <modal
- v-if="store.ffDownload"
- transition="in-out"
- :visible="!!store.ffDownload.url"
- @close="store.ffDownload = {}">
- <div class="export-modal modal-content">
- <a :download="store.ffDownload.name" :href="store.ffDownload.url">
- Right click and save as<br />
- <strong>scripts.zip</strong>
- </a>
- </div>
- </modal>
- </section>
- </template>
- <script>
- import Modal from 'vueleton/lib/modal';
- import { sendMessage, getLocaleString } from '#/common';
- import { objectGet } from '#/common/object';
- import options from '#/common/options';
- import { isFirefox } from '#/common/ua';
- import SettingCheck from '#/common/ui/setting-check';
- import { downloadBlob } from '#/common/download';
- import { store } from '../../utils';
- /**
- * Note:
- * - Firefox does not support multiline <select>
- */
- if (isFirefox) store.ffDownload = {};
- export default {
- components: {
- SettingCheck,
- Modal,
- },
- data() {
- return {
- isFirefox,
- store,
- exporting: false,
- items: [],
- };
- },
- watch: {
- 'store.scripts': 'initItems',
- },
- computed: {
- selectedIds() {
- return this.items.filter(item => item.active).map(item => item.script.props.id);
- },
- },
- created() {
- this.initItems();
- },
- methods: {
- initItems() {
- this.items = (store.scripts || [])
- .filter(({ config: { removed } }) => !removed)
- .map(script => ({
- script,
- active: true,
- }));
- },
- toggleSelection() {
- if (!store.scripts.length) return;
- const active = this.selectedIds.length < store.scripts.length;
- this.items.forEach(item => { item.active = active; });
- },
- exportData() {
- this.exporting = true;
- Promise.resolve(exportData(this.selectedIds))
- .then(download)
- .catch(err => {
- console.error(err);
- })
- .then(() => {
- this.exporting = false;
- });
- },
- getName(item) {
- return item.script.custom.name || getLocaleString(item.script.meta, 'name');
- },
- },
- };
- function getWriter() {
- return new Promise(resolve => {
- zip.createWriter(new zip.BlobWriter(), writer => {
- resolve(writer);
- });
- });
- }
- function addFile(writer, file) {
- return new Promise(resolve => {
- writer.add(file.name, new zip.TextReader(file.content), () => {
- resolve(writer);
- });
- });
- }
- function leftpad(src, length, pad = '0') {
- let str = `${src}`;
- while (str.length < length) str = pad + str;
- return str;
- }
- function getTimestamp() {
- const date = new Date();
- return `${
- date.getFullYear()
- }-${
- leftpad(date.getMonth() + 1, 2)
- }-${
- leftpad(date.getDate(), 2)
- }_${
- leftpad(date.getHours(), 2)
- }.${
- leftpad(date.getMinutes(), 2)
- }.${
- leftpad(date.getSeconds(), 2)
- }`;
- }
- function getExportname() {
- return `scripts_${getTimestamp()}.zip`;
- }
- function download(blob) {
- // Known issue: does not work on Firefox
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1331176
- if (isFirefox) {
- const reader = new FileReader();
- reader.onload = () => {
- store.ffDownload = {
- name: getExportname(),
- url: reader.result,
- };
- };
- reader.readAsDataURL(blob);
- } else {
- downloadBlob(blob, getExportname());
- }
- }
- function exportData(selectedIds) {
- const withValues = options.get('exportValues');
- return (selectedIds.length ? sendMessage({
- cmd: 'ExportZip',
- data: {
- values: withValues,
- ids: selectedIds,
- },
- }) : Promise.resolve())
- .then(data => {
- const names = {};
- const vm = {
- scripts: {},
- settings: options.get(),
- };
- delete vm.settings.sync;
- if (withValues) vm.values = {};
- const files = (objectGet(data, 'items') || []).map(({ script, code }) => {
- let name = script.custom.name || script.meta.name || script.props.id;
- if (names[name]) {
- names[name] += 1;
- name = `${name}_${names[name]}`;
- } else names[name] = 1;
- const info = {
- custom: script.custom,
- config: script.config,
- position: script.props.position,
- };
- if (withValues) {
- // `values` are related to scripts by `props.id` in Violentmonkey,
- // but by the global `props.uri` when exported.
- const values = data.values[script.props.id];
- if (values) vm.values[script.props.uri] = values;
- }
- vm.scripts[name] = info;
- return {
- name: `${name}.user.js`,
- content: code,
- };
- });
- files.push({
- name: 'violentmonkey',
- content: JSON.stringify(vm),
- });
- return files;
- })
- .then(files => files.reduce((result, file) => (
- result.then(writer => addFile(writer, file))
- ), getWriter()))
- .then(writer => new Promise(resolve => {
- writer.close(blob => {
- resolve(blob);
- });
- }));
- }
- </script>
- <style>
- .export-list {
- display: block;
- min-height: 4rem;
- max-height: 20rem;
- overflow-y: auto;
- padding: .3rem;
- white-space: normal;
- border: 1px solid #ddd;
- > .ellipsis {
- display: inline-block;
- width: 13rem;
- max-width: 100%;
- line-height: 1.5;
- margin-right: .2rem;
- margin-bottom: .1rem;
- padding: 0 .3rem;
- border: 1px solid #bbb;
- border-radius: 3px;
- cursor: pointer;
- &.active {
- border-color: #2c82c9;
- background: #3498db;
- color: white;
- }
- }
- }
- .export-modal {
- width: 13rem;
- }
- </style>
|