uxcore-tinymce
Version:
uxcore-tinymce component for uxcore.
604 lines (534 loc) • 18.8 kB
JavaScript
/* eslint-disable */
var iframeCount = 0;
var doc = document;
function Uploader(options) {
if (!(this instanceof Uploader)) {
return new Uploader(options);
}
if (isString(options)) {
options = {
trigger: options
};
}
var settings = {
trigger: null,
name: null,
action: null,
data: null,
accept: null,
change: null,
error: null,
multiple: true,
success: null
};
if (options) {
Object.keys(options).forEach(function(key) {
if (options[key]) {
settings[key] = options[key];
}
});
}
if (settings.trigger.nodeType === 1) {
this.$trigger = settings.trigger;
} else {
this.$trigger = doc.getElementById(settings.trigger);
}
this.$mce = options.mce;
settings.action = settings.action || '/upload';
settings.name = settings.name || 'file';
this.settings = settings;
this.setup();
this.bind();
}
// initialize
// create input, form, iframe
Uploader.prototype.setup = function() {
this.form = doc.createElement('form');
this.form.setAttribute('method', 'post');
this.form.setAttribute('enctype', 'multipart/form-data');
this.form.setAttribute('action', this.settings.action);
this.iframe = newIframe();
this.form.setAttribute('target', this.iframe.getAttribute('name'));
var data = this.settings.data;
createInputs(data).forEach(function(input) {
this.form.appendChild(input);
}.bind(this));
createInputs(window.FormData ? {
'_uploader_': 'formdata'
} : {
'_uploader_': 'iframe'
}).forEach(function(input) {
this.form.appendChild(input);
}.bind(this));
var input = document.createElement('input');
input.type = 'file';
input.name = this.settings.name;
if (this.settings.accept) {
input.accept = this.settings.accept;
}
if (this.settings.multiple) {
input.multiple = true;
input.setAttribute('multiple', 'multiple');
}
this.input = input;
var self = this;
var $trigger = this.$trigger;
var $mce = this.$mce;
input.setAttribute('hidefocus', true);
var height = $trigger.offsetHeight;
input.style.cssText = 'position: absolute; top: 0; right: 0; opacity: 0; outline: 0; filter:alpha(opacity=0);cursor: pointer; height: ' + height + '; fontSize: ' + Math.max(64, height * 5);
this.form.appendChild(input);
_positionForm();
// doc.body.appendChild(this.form);
this.$mce.appendChild(this.form);
$trigger.addEventListener('mouseenter', function() {
_positionForm();
});
function _positionForm() {
var formStyle = '';
var triggerRect = $trigger.getBoundingClientRect();
var mceRect = $mce.getBoundingClientRect();
var scrollY = window.scrollY || window.pageYOffset || document.documentElement.scrollTop;
var w = triggerRect.width || $trigger.clientWidth;
var h = triggerRect.height || $trigger.clientHeight;
var formCss = {
position: 'absolute',
top: (triggerRect.top - mceRect.top) + 'px',
left: (triggerRect.left - mceRect.left) + 'px',
overflow: 'hidden',
width: w + 'px',
height: h + 'px',
'z-Index': 110
};
Object.keys(formCss).forEach(function(prop) {
formStyle += [prop, formCss[prop]].join(': ') + ';';
});
self.form.style.cssText = formStyle;
}
return this;
};
// bind events
Uploader.prototype.bind = function() {
var self = this;
// var $trigger = this.$trigger;
self.bindInput();
};
Uploader.prototype.bindInput = function() {
var self = this;
self.input.onchange = function(e) {
// ie9 don't support FileList Object
// http://stackoverflow.com/questions/12830058/ie8-input-type-file-get-files
var ev = e || window.event;
var target = ev.target || ev.srcElement;
self._files = this.files || [{
name: target.value
}];
var file = self.input.value;
if (self.settings.change) {
self.settings.change.call(self, self._files);
} else if (file) {
return self.submit();
}
};
};
// handle submit event
// prepare for submiting form
Uploader.prototype.submit = function() {
var self = this;
if (window.FormData && self._files) {
// build a FormData
var fd = new FormData(self.form);
// use FormData to upload
fd.append(self.settings.name, self._files);
var xhr = new XMLHttpRequest();
if (self.settings.progress && xhr.upload) {
// fix the progress target file
xhr.upload.addEventListener('progress', function(event) {
var percent = 0;
var position = event.loaded || event.position; /*event.position is deprecated*/
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position / total * 100);
}
self.settings.progress(event, position, total, percent, self._files);
}, false);
}
xhr.open('post', self.settings.action);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200 || xhr.status == 201) {
self.settings.success && self.settings.success(xhr.responseText);
} else {
self.settings.error && self.settings.error(xhr.statusText);
}
}
};
xhr.send(fd);
return this;
} else {
// iframe upload
self.iframe = newIframe();
self.form.setAttribute('target', self.iframe.getAttribute('name'));
doc.body.appendChild(self.iframe);
self.iframe.onload = function() {
// https://github.com/blueimp/jQuery-File-Upload/blob/9.5.6/js/jquery.iframe-transport.js#L102
// Fix for IE endless progress bar activity bug
// (happens on form submits to iframe targets):
var _iframe = doc.createElement('iframe');
_iframe.setAttribute('src', 'javascript:false');
self.form.appendChild(_iframe);
self.form.removeChild(_iframe);
// $('<iframe src="javascript:false;"></iframe>')
// .appendTo(self.form)
// .remove();
var response;
try {
response = this.contentDocument.body.innerHTML;
} catch (e) {
response = "cross-domain";
}
doc.body.removeChild(this);
if (!response) {
if (self.settings.error) {
self.settings.error(self.input.value);
}
} else {
if (self.settings.success) {
self.settings.success(response);
}
}
};
self.form.submit();
}
return this;
};
Uploader.prototype.refreshInput = function() {
//replace the input element, or the same file can not to be uploaded
var newInput = this.input.cloneNode();
newInput.value = null;
var parentNode = this.input.parentNode;
parentNode.insertBefore(newInput, this.input);
this.input.onchange = null;
parentNode.removeChild(this.input);
this.input = newInput;
this.bindInput();
};
// handle change event
// when value in file input changed
Uploader.prototype.change = function(callback) {
if (!callback) {
return this;
}
this.settings.change = callback;
return this;
};
// handle when upload success
Uploader.prototype.success = function(callback) {
var me = this;
this.settings.success = function(response) {
me.refreshInput();
if (callback) {
callback(response);
}
};
return this;
};
// handle when upload success
Uploader.prototype.error = function(callback) {
var me = this;
this.settings.error = function(response) {
if (callback) {
me.refreshInput();
callback(response);
}
};
return this;
};
// enable
Uploader.prototype.enable = function() {
this.input.removeAttribute('disabled');
this.input.style.cursor = 'pointer';
};
// disable
Uploader.prototype.disable = function() {
this.input.prop('disabled', 'disabled');
this.input.style.cursor = 'not-allowed';
};
// Helpers
// -------------
function isString(val) {
return Object.prototype.toString.call(val) === '[object String]';
}
function createInputs(data) {
if (!data) return [];
var inputs = [],
i;
for (var name in data) {
i = doc.createElement('input');
i.type = 'hidden';
i.name = name;
i.value = data[name];
inputs.push(i);
}
return inputs;
}
function newIframe() {
var iframeName = 'iframe-uploader-' + iframeCount;
var iframe = doc.createElement('iframe');
iframe.setAttribute('name', iframeName);
iframe.style.display = 'none';
iframeCount += 1;
return iframe;
}
/**
* Created by Samoay on 11/30/14.
* 本地文件上传
*/
tinymce.PluginManager.add('upload', function(editor) {
var uploadConfig = editor.settings.uploadConfig;
function getImageSize(url, callback) {
var img = document.createElement('img');
function done(width, height) {
img.parentNode.removeChild(img);
callback({
width: width,
height: height
});
}
img.onload = function() {
done(img.clientWidth, img.clientHeight);
};
img.onerror = function() {
done();
};
img.src = url;
var style = img.style;
style.visibility = 'hidden';
style.position = 'fixed';
style.bottom = style.left = 0;
style.width = style.height = 'auto';
document.body.appendChild(img);
}
function showDialog(sourceImage) {
var win, data, dom = editor.dom,
imgElm = editor.selection.getNode();
var width, height;
function recalcSize(e) {
var widthCtrl, heightCtrl, newWidth, newHeight;
widthCtrl = win.find('#width')[0];
heightCtrl = win.find('#height')[0];
newWidth = widthCtrl.value();
newHeight = heightCtrl.value();
if (win.find('#constrain')[0].checked() && width && height && newWidth && newHeight) {
if (e.control == widthCtrl) {
newHeight = Math.round((newWidth / width) * newHeight);
heightCtrl.value(newHeight);
} else {
newWidth = Math.round((newHeight / height) * newWidth);
widthCtrl.value(newWidth);
}
}
width = newWidth;
height = newHeight;
}
function onSubmitForm() {
function waitLoad(imgElm) {
function selectImage() {
imgElm.onload = imgElm.onerror = null;
//editor.selection.select(imgElm);
editor.nodeChanged();
}
imgElm.onload = function() {
if (!data.width && !data.height) {
dom.setAttribs(imgElm, {
width: imgElm.clientWidth,
height: imgElm.clientHeight
});
}
selectImage();
};
imgElm.onerror = selectImage;
}
var data = win.toJSON();
if (data.width === '') {
data.width = null;
}
if (data.height === '') {
data.height = null;
}
if (data.style === '') {
data.style = null;
}
data = {
src: data.src,
alt: data.alt,
width: data.width,
height: data.height,
style: data.style
};
editor.undoManager.transact(function() {
if (!data.src) {
if (imgElm) {
dom.remove(imgElm);
editor.nodeChanged();
}
return;
}
if (!imgElm) {
data.id = '__mcenew';
editor.selection.setContent(dom.createHTML('img', data));
imgElm = dom.get('__mcenew');
dom.setAttrib(imgElm, 'id', null);
} else {
dom.setAttribs(imgElm, data);
}
waitLoad(imgElm);
});
}
function updateSize() {
getImageSize(this.value(), function(data) {
if (data.width && data.height) {
width = data.width;
height = data.height;
win.find('#width').value(width);
win.find('#height').value(height);
}
});
}
width = dom.getAttrib(imgElm, 'width');
height = dom.getAttrib(imgElm, 'height');
if (imgElm.nodeName == 'IMG' && !imgElm.getAttribute('data-mce-object')) {
data = {
src: dom.getAttrib(imgElm, 'src'),
alt: dom.getAttrib(imgElm, 'alt'),
width: width,
height: height
};
} else {
imgElm = null;
}
// General settings shared between simple and advanced dialogs
var generalFormItems = [{
name: 'src',
type: 'textbox',
filetype: 'image',
label: '图片路径',
autofocus: true,
onchange: updateSize
}, {
name: 'alt',
type: 'textbox',
label: '图片描述'
}, {
type: 'container',
label: '图片尺寸',
layout: 'flex',
direction: 'row',
align: 'center',
spacing: 5,
items: [{
name: 'width',
type: 'textbox',
maxLength: 4,
size: 4,
onchange: recalcSize
}, {
type: 'label',
text: 'x'
}, {
name: 'height',
type: 'textbox',
maxLength: 4,
size: 4,
onchange: recalcSize
}, {
name: 'constrain',
type: 'checkbox',
checked: true,
text: '强制等比例'
}]
}];
// Simple default dialog
win = editor.windowManager.open({
title: '编辑图片',
width: 400,
height: 150,
data: data,
body: generalFormItems,
onSubmit: onSubmitForm
});
win.find('#src').value(sourceImage.value);
win.find('#alt').value(sourceImage.title);
updateSize.call(win.find('#src'))
}
function isFunction(obj) {
return Object.prototype.toString.call(obj) == '[object Function]';
}
function isString(obj) {
return Object.prototype.toString.call(obj) == '[object String]';
}
editor.addButton('upload', {
icon: 'image',
tooltip: '上传图片',
classes: "widget ext-btn ext-btn-small uploader-picture",
onPostRender: function() {
var self = this;
setTimeout(function() {
var parents = self.parents();
var mce = parents[parents.length - 1].getEl();
var uploader;
if (!uploader) {
uploader = new Uploader({
trigger: self.getEl(),
mce: mce,
name: uploadConfig.inputName || 'file',
action: uploadConfig.actionUrl,
data: {
'xsrf': 'hash'
},
multiple: uploadConfig.multiple || false,
progress: function(event, position, total, percent, files) {
if (uploadConfig.progressCallback && isFunction(uploadConfig.progressCallback)) {
uploadConfig.progressCallback.apply(uploader, Array.prototype.slice.call(arguments));
}
}
});
// 使用success实例方法传入成功回调,否则refreshInput方法将得不到调用 by jinhong@20161205
uploader.success(function(response) {
var result;
if (isString(response)) {
try {
result = JSON.parse(response);
} catch (e) {
result = {
hasError: true
};
}
} else {
result = response;
}
if (uploadConfig.formatResult && isFunction(uploadConfig.formatResult)) {
result = uploadConfig.formatResult.call(null, result);
}
if (!result.hasError) {
showDialog({
title: result.content.name,
value: result.content.downloarUrl
});
} else {
alert('文件上传失败');
}
});
// 使用error实例方法传入成功回调,否则refreshInput方法将得不到调用 by jinhong@20161205
uploader.error(function() {
if (uploadConfig.errorCallback && isFunction(uploadConfig.errorCallback)) {
uploadConfig.errorCallback.apply(uploader, Array.prototype.slice.call(arguments));
} else {
alert('文件上传失败');
}
});
}
}, 1000);
}
});
});