summaryrefslogtreecommitdiff
path: root/public/vendor/inlineAttachment
diff options
context:
space:
mode:
authorWu Cheng-Han2015-05-15 12:58:13 +0800
committerWu Cheng-Han2015-05-15 12:58:13 +0800
commit4e64583a0b6175d2c9a6729ffde1472dd55d389c (patch)
tree75253f2425f2e4f5906ed4fd30eca29a906ee47a /public/vendor/inlineAttachment
parent2d36d7ce84c636faac17cef3d3a7c22568df38fe (diff)
Marked as 0.2.8
Diffstat (limited to 'public/vendor/inlineAttachment')
-rwxr-xr-xpublic/vendor/inlineAttachment/codemirror.inline-attachment.js95
-rwxr-xr-xpublic/vendor/inlineAttachment/inline-attachment.js435
2 files changed, 530 insertions, 0 deletions
diff --git a/public/vendor/inlineAttachment/codemirror.inline-attachment.js b/public/vendor/inlineAttachment/codemirror.inline-attachment.js
new file mode 100755
index 00000000..7816d1c8
--- /dev/null
+++ b/public/vendor/inlineAttachment/codemirror.inline-attachment.js
@@ -0,0 +1,95 @@
+/*jslint newcap: true */
+/*global inlineAttachment: false */
+/**
+ * CodeMirror version for inlineAttachment
+ *
+ * Call inlineAttachment.attach(editor) to attach to a codemirror instance
+ */
+(function() {
+ 'use strict';
+
+ var codeMirrorEditor = function(instance) {
+
+ if (!instance.getWrapperElement) {
+ throw "Invalid CodeMirror object given";
+ }
+
+ this.codeMirror = instance;
+ };
+
+ codeMirrorEditor.prototype.getValue = function() {
+ return this.codeMirror.getValue();
+ };
+
+ codeMirrorEditor.prototype.insertValue = function(val) {
+ this.codeMirror.replaceSelection(val);
+ };
+
+ codeMirrorEditor.prototype.setValue = function(val) {
+ var cursor = this.codeMirror.getCursor();
+ this.codeMirror.setValue(val);
+ this.codeMirror.setCursor(cursor);
+ };
+
+ codeMirrorEditor.prototype.replaceRange = function(val) {
+ this.codeMirror.replaceRange(val.replacement, val.from, val.to, "+input");
+ };
+
+ /**
+ * Attach InlineAttachment to CodeMirror
+ *
+ * @param {CodeMirror} codeMirror
+ */
+ codeMirrorEditor.attach = function(codeMirror, options) {
+
+ options = options || {};
+
+ var editor = new codeMirrorEditor(codeMirror),
+ inlineattach = new inlineAttachment(options, editor),
+ el = codeMirror.getWrapperElement();
+
+ el.addEventListener('paste', function(e) {
+ inlineattach.onPaste(e);
+ }, false);
+
+ codeMirror.setOption('onDragEvent', function(data, e) {
+ if (e.type === "drop") {
+ e.stopPropagation();
+ e.preventDefault();
+ return inlineattach.onDrop(e);
+ }
+ });
+ };
+
+ inlineAttachment.editors.codemirror3 = codeMirrorEditor;
+
+ var codeMirrorEditor4 = function(instance) {
+ codeMirrorEditor.call(this, instance);
+ };
+
+ codeMirrorEditor4.attach = function(codeMirror, options) {
+
+ options = options || {};
+
+ var editor = new codeMirrorEditor(codeMirror),
+ inlineattach = new inlineAttachment(options, editor),
+ el = codeMirror.getWrapperElement();
+
+ el.addEventListener('paste', function(e) {
+ inlineattach.onPaste(e);
+ }, false);
+
+ codeMirror.on('drop', function(data, e) {
+ if (inlineattach.onDrop(e)) {
+ e.stopPropagation();
+ e.preventDefault();
+ return true;
+ } else {
+ return false;
+ }
+ });
+ };
+
+ inlineAttachment.editors.codemirror4 = codeMirrorEditor4;
+
+})(); \ No newline at end of file
diff --git a/public/vendor/inlineAttachment/inline-attachment.js b/public/vendor/inlineAttachment/inline-attachment.js
new file mode 100755
index 00000000..9353ebd8
--- /dev/null
+++ b/public/vendor/inlineAttachment/inline-attachment.js
@@ -0,0 +1,435 @@
+/*jslint newcap: true */
+/*global XMLHttpRequest: false, FormData: false */
+/*
+ * Inline Text Attachment
+ *
+ * Author: Roy van Kaathoven
+ * Contact: ik@royvankaathoven.nl
+ */
+(function(document, window) {
+ 'use strict';
+
+ var inlineAttachment = function(options, instance) {
+ this.settings = inlineAttachment.util.merge(options, inlineAttachment.defaults);
+ this.editor = instance;
+ this.filenameTag = '{filename}';
+ this.lastValue = null;
+ };
+
+ /**
+ * Will holds the available editors
+ *
+ * @type {Object}
+ */
+ inlineAttachment.editors = {};
+
+ /**
+ * Utility functions
+ */
+ inlineAttachment.util = {
+
+ /**
+ * Simple function to merge the given objects
+ *
+ * @param {Object[]} object Multiple object parameters
+ * @returns {Object}
+ */
+ merge: function() {
+ var result = {};
+ for (var i = arguments.length - 1; i >= 0; i--) {
+ var obj = arguments[i];
+ for (var k in obj) {
+ if (obj.hasOwnProperty(k)) {
+ result[k] = obj[k];
+ }
+ }
+ }
+ return result;
+ },
+
+ /**
+ * Append a line of text at the bottom, ensuring there aren't unnecessary newlines
+ *
+ * @param {String} appended Current content
+ * @param {String} previous Value which should be appended after the current content
+ */
+ appendInItsOwnLine: function(previous, appended) {
+ return (previous + "\n\n[[D]]" + appended)
+ .replace(/(\n{2,})\[\[D\]\]/, "\n\n")
+ .replace(/^(\n*)/, "");
+ },
+
+ /**
+ * Inserts the given value at the current cursor position of the textarea element
+ *
+ * @param {HtmlElement} el
+ * @param {String} value Text which will be inserted at the cursor position
+ */
+ insertTextAtCursor: function(el, text) {
+ var scrollPos = el.scrollTop,
+ strPos = 0,
+ browser = false,
+ range;
+
+ if ((el.selectionStart || el.selectionStart === '0')) {
+ browser = "ff";
+ } else if (document.selection) {
+ browser = "ie";
+ }
+
+ if (browser === "ie") {
+ el.focus();
+ range = document.selection.createRange();
+ range.moveStart('character', -el.value.length);
+ strPos = range.text.length;
+ } else if (browser === "ff") {
+ strPos = el.selectionStart;
+ }
+
+ var front = (el.value).substring(0, strPos);
+ var back = (el.value).substring(strPos, el.value.length);
+ el.value = front + text + back;
+ strPos = strPos + text.length;
+ if (browser === "ie") {
+ el.focus();
+ range = document.selection.createRange();
+ range.moveStart('character', -el.value.length);
+ range.moveStart('character', strPos);
+ range.moveEnd('character', 0);
+ range.select();
+ } else if (browser === "ff") {
+ el.selectionStart = strPos;
+ el.selectionEnd = strPos;
+ el.focus();
+ }
+ el.scrollTop = scrollPos;
+ }
+ };
+
+ /**
+ * Default configuration options
+ *
+ * @type {Object}
+ */
+ inlineAttachment.defaults = {
+ /**
+ * URL where the file will be send
+ */
+ uploadUrl: 'uploadimage',
+
+ /**
+ * Which method will be used to send the file to the upload URL
+ */
+ uploadMethod: 'POST',
+
+ /**
+ * Name in which the file will be placed
+ */
+ uploadFieldName: 'image',
+
+ /**
+ * Extension which will be used when a file extension could not
+ * be detected
+ */
+ defualtExtension: 'png',
+
+ /**
+ * JSON field which refers to the uploaded file URL
+ */
+ jsonFieldName: 'link',
+
+ /**
+ * Allowed MIME types
+ */
+ allowedTypes: [
+ 'image/jpeg',
+ 'image/png',
+ 'image/jpg',
+ 'image/gif'
+ ],
+
+ /**
+ * Text which will be inserted when dropping or pasting a file.
+ * Acts as a placeholder which will be replaced when the file is done with uploading
+ */
+ progressText: '![Uploading file...{filename}]()',
+
+ /**
+ * When a file has successfully been uploaded the progressText
+ * will be replaced by the urlText, the {filename} tag will be replaced
+ * by the filename that has been returned by the server
+ */
+ urlText: "![]({filename})",
+
+ /**
+ * Text which will be used when uploading has failed
+ */
+ errorText: "Error uploading file",
+
+ /**
+ * Extra parameters which will be send when uploading a file
+ */
+ extraParams: {},
+
+ /**
+ * Extra headers which will be send when uploading a file
+ */
+ extraHeaders: {},
+
+ /**
+ * Before the file is send
+ */
+ beforeFileUpload: function() {
+ return true;
+ },
+
+ /**
+ * Triggers when a file is dropped or pasted
+ */
+ onFileReceived: function() {},
+
+ /**
+ * Custom upload handler
+ *
+ * @return {Boolean} when false is returned it will prevent default upload behavior
+ */
+ onFileUploadResponse: function() {
+ return true;
+ },
+
+ /**
+ * Custom error handler. Runs after removing the placeholder text and before the alert().
+ * Return false from this function to prevent the alert dialog.
+ *
+ * @return {Boolean} when false is returned it will prevent default error behavior
+ */
+ onFileUploadError: function() {
+ return true;
+ },
+
+ /**
+ * When a file has succesfully been uploaded
+ */
+ onFileUploaded: function() {}
+ };
+
+ /**
+ * Uploads the blob
+ *
+ * @param {Blob} file blob data received from event.dataTransfer object
+ * @return {XMLHttpRequest} request object which sends the file
+ */
+ inlineAttachment.prototype.uploadFile = function(file, id) {
+ var me = this,
+ formData = new FormData(),
+ xhr = new XMLHttpRequest(),
+ id = id,
+ settings = this.settings,
+ extension = settings.defualtExtension;
+
+ if (typeof settings.setupFormData === 'function') {
+ settings.setupFormData(formData, file);
+ }
+
+ // Attach the file. If coming from clipboard, add a default filename (only works in Chrome for now)
+ // http://stackoverflow.com/questions/6664967/how-to-give-a-blob-uploaded-as-formdata-a-file-name
+ if (file.name) {
+ var fileNameMatches = file.name.match(/\.(.+)$/);
+ if (fileNameMatches) {
+ extension = fileNameMatches[1];
+ }
+ }
+
+ var remoteFilename = "image-" + Date.now() + "." + extension;
+ if (typeof settings.remoteFilename === 'function') {
+ remoteFilename = settings.remoteFilename(file);
+ }
+
+ formData.append(settings.uploadFieldName, file, remoteFilename);
+
+ // Append the extra parameters to the formdata
+ if (typeof settings.extraParams === "object") {
+ for (var key in settings.extraParams) {
+ if (settings.extraParams.hasOwnProperty(key)) {
+ formData.append(key, settings.extraParams[key]);
+ }
+ }
+ }
+
+ xhr.open('POST', settings.uploadUrl);
+
+ // Add any available extra headers
+ if (typeof settings.extraHeaders === "object") {
+ for (var header in settings.extraHeaders) {
+ if (settings.extraHeaders.hasOwnProperty(header)) {
+ xhr.setRequestHeader(header, settings.extraHeaders[header]);
+ }
+ }
+ }
+
+ xhr.onload = function() {
+ // If HTTP status is OK or Created
+ if (xhr.status === 200 || xhr.status === 201) {
+ me.onFileUploadResponse(xhr, id);
+ } else {
+ me.onFileUploadError(xhr, id);
+ }
+ };
+ if (settings.beforeFileUpload(xhr) !== false) {
+ xhr.send(formData);
+ }
+ return xhr;
+ };
+
+ /**
+ * Returns if the given file is allowed to handle
+ *
+ * @param {File} clipboard data file
+ */
+ inlineAttachment.prototype.isFileAllowed = function(file) {
+ if (this.settings.allowedTypes.indexOf('*') === 0){
+ return true;
+ } else {
+ return this.settings.allowedTypes.indexOf(file.type) >= 0;
+ }
+ };
+
+ /**
+ * Handles upload response
+ *
+ * @param {XMLHttpRequest} xhr
+ * @return {Void}
+ */
+ inlineAttachment.prototype.onFileUploadResponse = function(xhr, id) {
+ if (this.settings.onFileUploadResponse.call(this, xhr) !== false) {
+ var result = JSON.parse(xhr.responseText),
+ filename = result[this.settings.jsonFieldName];
+
+ if (result && filename) {
+ var replacements = [];
+ var string = this.settings.progressText.replace(this.filenameTag, id);
+ var lines = this.editor.getValue().split('\n');
+ var newValue = this.settings.urlText.replace(this.filenameTag, filename);
+ for(var i = 0; i < lines.length; i++) {
+ var ch = lines[i].indexOf(string);
+ if(ch != -1)
+ replacements.push({replacement:newValue, from:{line:i, ch:ch}, to:{line:i, ch:ch + string.length}});
+ }
+ for(var i = 0; i < replacements.length; i++)
+ this.editor.replaceRange(replacements[i]);
+ }
+ }
+ };
+
+
+ /**
+ * Called when a file has failed to upload
+ *
+ * @param {XMLHttpRequest} xhr
+ * @return {Void}
+ */
+ inlineAttachment.prototype.onFileUploadError = function(xhr, id) {
+ if (this.settings.onFileUploadError.call(this, xhr) !== false) {
+ var replacements = [];
+ var string = this.settings.progressText.replace(this.filenameTag, id);
+ var lines = this.editor.getValue().split('\n');
+ for(var i = 0; i < lines.length; i++) {
+ var ch = lines[i].indexOf(this.lastValue);
+ if(ch != -1)
+ replacements.push({replacement:"", from:{line:i, ch:ch}, to:{line:i, ch:ch + string.length}});
+ }
+ for(var i = 0; i < replacements.length; i++)
+ this.editor.replaceRange(replacements[i]);
+ }
+ };
+
+ /**
+ * Called when a file has been inserted, either by drop or paste
+ *
+ * @param {File} file
+ * @return {Void}
+ */
+ inlineAttachment.prototype.onFileInserted = function(file, id) {
+ if (this.settings.onFileReceived.call(this, file) !== false) {
+ this.lastValue = this.settings.progressText.replace(this.filenameTag, id);
+ this.editor.insertValue(this.lastValue);
+ }
+ };
+
+
+ /**
+ * Called when a paste event occured
+ * @param {Event} e
+ * @return {Boolean} if the event was handled
+ */
+ inlineAttachment.prototype.onPaste = function(e) {
+ var result = false,
+ clipboardData = e.clipboardData,
+ items;
+
+ if (typeof clipboardData === "object") {
+ items = clipboardData.items || clipboardData.files || [];
+
+ for (var i = 0; i < items.length; i++) {
+ var item = items[i];
+ if (this.isFileAllowed(item)) {
+ result = true;
+ var id = ID();
+ this.onFileInserted(item.getAsFile(), id);
+ this.uploadFile(item.getAsFile(), id);
+ }
+ }
+ }
+
+ if (result) { e.preventDefault(); }
+
+ return result;
+ };
+
+ /**
+ * Called when a drop event occures
+ * @param {Event} e
+ * @return {Boolean} if the event was handled
+ */
+ inlineAttachment.prototype.onDrop = function(e) {
+ var result = false;
+ for (var i = 0; i < e.dataTransfer.files.length; i++) {
+ var file = e.dataTransfer.files[i];
+ if (this.isFileAllowed(file)) {
+ result = true;
+ var id = ID();
+ this.onFileInserted(file, id);
+ this.uploadFile(file, id);
+ }
+ }
+
+ return result;
+ };
+
+ window.inlineAttachment = inlineAttachment;
+
+})(document, window);
+
+// Generate unique IDs for use as pseudo-private/protected names.
+// Similar in concept to
+// <http://wiki.ecmascript.org/doku.php?id=strawman:names>.
+//
+// The goals of this function are twofold:
+//
+// * Provide a way to generate a string guaranteed to be unique when compared
+// to other strings generated by this function.
+// * Make the string complex enough that it is highly unlikely to be
+// accidentally duplicated by hand (this is key if you're using `ID`
+// as a private/protected name on an object).
+//
+// Use:
+//
+// var privateName = ID();
+// var o = { 'public': 'foo' };
+// o[privateName] = 'bar';
+var ID = function () {
+ // Math.random should be unique because of its seeding algorithm.
+ // Convert it to base 36 (numbers + letters), and grab the first 9 characters
+ // after the decimal.
+ return '_' + Math.random().toString(36).substr(2, 9);
+}; \ No newline at end of file