From 4e64583a0b6175d2c9a6729ffde1472dd55d389c Mon Sep 17 00:00:00 2001 From: Wu Cheng-Han Date: Fri, 15 May 2015 12:58:13 +0800 Subject: Marked as 0.2.8 --- .../codemirror.inline-attachment.js | 95 +++++ .../vendor/inlineAttachment/inline-attachment.js | 435 +++++++++++++++++++++ 2 files changed, 530 insertions(+) create mode 100755 public/vendor/inlineAttachment/codemirror.inline-attachment.js create mode 100755 public/vendor/inlineAttachment/inline-attachment.js (limited to 'public/vendor/inlineAttachment') 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 +// . +// +// 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 -- cgit v1.2.3