import PropTypes from '../../_util/vue-types'; import BaseMixin from '../../_util/BaseMixin'; import classNames from '../../_util/classNames'; import getUid from './uid'; import warning from '../../_util/warning'; import { getSlot, findDOMNode } from '../../_util/props-util'; const IFRAME_STYLE = { position: 'absolute', top: 0, opacity: 0, filter: 'alpha(opacity=0)', left: 0, zIndex: 9999, }; // diferent from AjaxUpload, can only upload on at one time, serial seriously const IframeUploader = { name: 'IframeUploader', mixins: [BaseMixin], props: { componentTag: PropTypes.string, // style: PropTypes.object, disabled: PropTypes.looseBool, prefixCls: PropTypes.string, // className: PropTypes.string, accept: PropTypes.string, // onStart: PropTypes.func, multiple: PropTypes.looseBool, // children: PropTypes.any, data: PropTypes.oneOfType([PropTypes.object, PropTypes.func]), action: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), name: PropTypes.string, }, data() { this.file = {}; return { uploading: false, }; }, methods: { onLoad() { if (!this.uploading) { return; } const { file } = this; let response; try { const doc = this.getIframeDocument(); const script = doc.getElementsByTagName('script')[0]; if (script && script.parentNode === doc.body) { doc.body.removeChild(script); } response = doc.body.innerHTML; this.__emit('success', response, file); } catch (err) { warning( false, 'cross domain error for Upload. Maybe server should return document.domain script. see Note from https://github.com/react-component/upload', ); response = 'cross-domain'; this.__emit('error', err, null, file); } this.endUpload(); }, onChange() { const target = this.getFormInputNode(); // ie8/9 don't support FileList Object // http://stackoverflow.com/questions/12830058/ie8-input-type-file-get-files const file = (this.file = { uid: getUid(), name: target.value && target.value.substring(target.value.lastIndexOf('\\') + 1, target.value.length), }); this.startUpload(); const { $props: props } = this; if (!props.beforeUpload) { return this.post(file); } const before = props.beforeUpload(file); if (before && before.then) { before.then( () => { this.post(file); }, () => { this.endUpload(); }, ); } else if (before !== false) { this.post(file); } else { this.endUpload(); } }, getIframeNode() { return this.$refs.iframeRef; }, getIframeDocument() { return this.getIframeNode().contentDocument; }, getFormNode() { return this.getIframeDocument().getElementById('form'); }, getFormInputNode() { return this.getIframeDocument().getElementById('input'); }, getFormDataNode() { return this.getIframeDocument().getElementById('data'); }, getFileForMultiple(file) { return this.multiple ? [file] : file; }, getIframeHTML(domain) { let domainScript = ''; let domainInput = ''; if (domain) { const script = 'script'; domainScript = `<${script}>document.domain="${domain}";`; domainInput = ``; } return ` ${domainScript}
${domainInput}
`; }, initIframeSrc() { if (this.domain) { this.getIframeNode().src = `javascript:void((function(){ var d = document; d.open(); d.domain='${this.domain}'; d.write(''); d.close(); })())`; } }, initIframe() { const iframeNode = this.getIframeNode(); let win = iframeNode.contentWindow; let doc; this.domain = this.domain || ''; this.initIframeSrc(); try { doc = win.document; } catch (e) { this.domain = document.domain; this.initIframeSrc(); win = iframeNode.contentWindow; doc = win.document; } doc.open('text/html', 'replace'); doc.write(this.getIframeHTML(this.domain)); doc.close(); this.getFormInputNode().onchange = this.onChange; }, endUpload() { if (this.uploading) { this.file = {}; // hack avoid batch this.uploading = false; this.setState({ uploading: false, }); this.initIframe(); } }, startUpload() { if (!this.uploading) { this.uploading = true; this.setState({ uploading: true, }); } }, updateIframeWH() { const rootNode = findDOMNode(this); const iframeNode = this.getIframeNode(); iframeNode.style.height = `${rootNode.offsetHeight}px`; iframeNode.style.width = `${rootNode.offsetWidth}px`; }, abort(file) { if (file) { let uid = file; if (file && file.uid) { uid = file.uid; } if (uid === this.file.uid) { this.endUpload(); } } else { this.endUpload(); } }, post(file) { const formNode = this.getFormNode(); const dataSpan = this.getFormDataNode(); let { data } = this.$props; if (typeof data === 'function') { data = data(file); } const inputs = document.createDocumentFragment(); for (const key in data) { if (data.hasOwnProperty(key)) { const input = document.createElement('input'); input.setAttribute('name', key); input.value = data[key]; inputs.appendChild(input); } } dataSpan.appendChild(inputs); new Promise(resolve => { const { action } = this; if (typeof action === 'function') { return resolve(action(file)); } resolve(action); }).then(action => { formNode.setAttribute('action', action); formNode.submit(); dataSpan.innerHTML = ''; this.__emit('start', file); }); }, }, mounted() { this.$nextTick(() => { this.updateIframeWH(); this.initIframe(); }); }, updated() { this.$nextTick(() => { this.updateIframeWH(); }); }, render() { const { componentTag: Tag, disabled, prefixCls } = this.$props; const { class: className, style } = this.$attrs; const iframeStyle = { ...IFRAME_STYLE, display: this.uploading || disabled ? 'none' : '', }; const cls = classNames({ [prefixCls]: true, [`${prefixCls}-disabled`]: disabled, [className]: className, }); return (