blogelator 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/blogelator/version.rb +1 -1
- data/vendor/assets/javascripts/codemirror/closebrackets.js +84 -0
- data/vendor/assets/javascripts/codemirror/codemirror.js +6093 -0
- data/vendor/assets/javascripts/codemirror/coffeescript.js +353 -0
- data/vendor/assets/javascripts/codemirror/css.js +693 -0
- data/vendor/assets/javascripts/codemirror/gfm.js +102 -0
- data/vendor/assets/javascripts/codemirror/htmlmixed.js +105 -0
- data/vendor/assets/javascripts/codemirror/javascript.js +638 -0
- data/vendor/assets/javascripts/codemirror/markdown.js +748 -0
- data/vendor/assets/javascripts/codemirror/overlay.js +59 -0
- data/vendor/assets/javascripts/codemirror/php.js +131 -0
- data/vendor/assets/javascripts/codemirror/ruby.js +250 -0
- data/vendor/assets/javascripts/codemirror/sass.js +330 -0
- data/vendor/assets/javascripts/codemirror/xml.js +333 -0
- data/vendor/assets/javascripts/codemirror/yaml.js +97 -0
- data/vendor/assets/javascripts/inline-attach.js +336 -0
- data/vendor/assets/javascripts/marked.js +1251 -0
- data/vendor/assets/javascripts/moment.js +6 -0
- data/vendor/assets/javascripts/prettify.js +1655 -0
- data/vendor/assets/stylesheets/codemirror.css +221 -0
- data/vendor/assets/stylesheets/normalize.css +423 -0
- metadata +22 -2
@@ -0,0 +1,333 @@
|
|
1
|
+
CodeMirror.defineMode("xml", function(config, parserConfig) {
|
2
|
+
var indentUnit = config.indentUnit;
|
3
|
+
var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
|
4
|
+
var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag;
|
5
|
+
if (multilineTagIndentPastTag == null) multilineTagIndentPastTag = true;
|
6
|
+
|
7
|
+
var Kludges = parserConfig.htmlMode ? {
|
8
|
+
autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
|
9
|
+
'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
|
10
|
+
'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
|
11
|
+
'track': true, 'wbr': true},
|
12
|
+
implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
|
13
|
+
'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
|
14
|
+
'th': true, 'tr': true},
|
15
|
+
contextGrabbers: {
|
16
|
+
'dd': {'dd': true, 'dt': true},
|
17
|
+
'dt': {'dd': true, 'dt': true},
|
18
|
+
'li': {'li': true},
|
19
|
+
'option': {'option': true, 'optgroup': true},
|
20
|
+
'optgroup': {'optgroup': true},
|
21
|
+
'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
|
22
|
+
'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
|
23
|
+
'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
|
24
|
+
'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
|
25
|
+
'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
|
26
|
+
'rp': {'rp': true, 'rt': true},
|
27
|
+
'rt': {'rp': true, 'rt': true},
|
28
|
+
'tbody': {'tbody': true, 'tfoot': true},
|
29
|
+
'td': {'td': true, 'th': true},
|
30
|
+
'tfoot': {'tbody': true},
|
31
|
+
'th': {'td': true, 'th': true},
|
32
|
+
'thead': {'tbody': true, 'tfoot': true},
|
33
|
+
'tr': {'tr': true}
|
34
|
+
},
|
35
|
+
doNotIndent: {"pre": true},
|
36
|
+
allowUnquoted: true,
|
37
|
+
allowMissing: true
|
38
|
+
} : {
|
39
|
+
autoSelfClosers: {},
|
40
|
+
implicitlyClosed: {},
|
41
|
+
contextGrabbers: {},
|
42
|
+
doNotIndent: {},
|
43
|
+
allowUnquoted: false,
|
44
|
+
allowMissing: false
|
45
|
+
};
|
46
|
+
var alignCDATA = parserConfig.alignCDATA;
|
47
|
+
|
48
|
+
// Return variables for tokenizers
|
49
|
+
var tagName, type, setStyle;
|
50
|
+
|
51
|
+
function inText(stream, state) {
|
52
|
+
function chain(parser) {
|
53
|
+
state.tokenize = parser;
|
54
|
+
return parser(stream, state);
|
55
|
+
}
|
56
|
+
|
57
|
+
var ch = stream.next();
|
58
|
+
if (ch == "<") {
|
59
|
+
if (stream.eat("!")) {
|
60
|
+
if (stream.eat("[")) {
|
61
|
+
if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
|
62
|
+
else return null;
|
63
|
+
} else if (stream.match("--")) {
|
64
|
+
return chain(inBlock("comment", "-->"));
|
65
|
+
} else if (stream.match("DOCTYPE", true, true)) {
|
66
|
+
stream.eatWhile(/[\w\._\-]/);
|
67
|
+
return chain(doctype(1));
|
68
|
+
} else {
|
69
|
+
return null;
|
70
|
+
}
|
71
|
+
} else if (stream.eat("?")) {
|
72
|
+
stream.eatWhile(/[\w\._\-]/);
|
73
|
+
state.tokenize = inBlock("meta", "?>");
|
74
|
+
return "meta";
|
75
|
+
} else {
|
76
|
+
var isClose = stream.eat("/");
|
77
|
+
tagName = "";
|
78
|
+
var c;
|
79
|
+
while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
|
80
|
+
if (!tagName) return "tag error";
|
81
|
+
type = isClose ? "closeTag" : "openTag";
|
82
|
+
state.tokenize = inTag;
|
83
|
+
return "tag";
|
84
|
+
}
|
85
|
+
} else if (ch == "&") {
|
86
|
+
var ok;
|
87
|
+
if (stream.eat("#")) {
|
88
|
+
if (stream.eat("x")) {
|
89
|
+
ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
|
90
|
+
} else {
|
91
|
+
ok = stream.eatWhile(/[\d]/) && stream.eat(";");
|
92
|
+
}
|
93
|
+
} else {
|
94
|
+
ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
|
95
|
+
}
|
96
|
+
return ok ? "atom" : "error";
|
97
|
+
} else {
|
98
|
+
stream.eatWhile(/[^&<]/);
|
99
|
+
return null;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
function inTag(stream, state) {
|
104
|
+
var ch = stream.next();
|
105
|
+
if (ch == ">" || (ch == "/" && stream.eat(">"))) {
|
106
|
+
state.tokenize = inText;
|
107
|
+
type = ch == ">" ? "endTag" : "selfcloseTag";
|
108
|
+
return "tag";
|
109
|
+
} else if (ch == "=") {
|
110
|
+
type = "equals";
|
111
|
+
return null;
|
112
|
+
} else if (ch == "<") {
|
113
|
+
state.tokenize = inText;
|
114
|
+
state.state = baseState;
|
115
|
+
state.tagName = state.tagStart = null;
|
116
|
+
var next = state.tokenize(stream, state);
|
117
|
+
return next ? next + " error" : "error";
|
118
|
+
} else if (/[\'\"]/.test(ch)) {
|
119
|
+
state.tokenize = inAttribute(ch);
|
120
|
+
state.stringStartCol = stream.column();
|
121
|
+
return state.tokenize(stream, state);
|
122
|
+
} else {
|
123
|
+
stream.eatWhile(/[^\s\u00a0=<>\"\']/);
|
124
|
+
return "word";
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
function inAttribute(quote) {
|
129
|
+
var closure = function(stream, state) {
|
130
|
+
while (!stream.eol()) {
|
131
|
+
if (stream.next() == quote) {
|
132
|
+
state.tokenize = inTag;
|
133
|
+
break;
|
134
|
+
}
|
135
|
+
}
|
136
|
+
return "string";
|
137
|
+
};
|
138
|
+
closure.isInAttribute = true;
|
139
|
+
return closure;
|
140
|
+
}
|
141
|
+
|
142
|
+
function inBlock(style, terminator) {
|
143
|
+
return function(stream, state) {
|
144
|
+
while (!stream.eol()) {
|
145
|
+
if (stream.match(terminator)) {
|
146
|
+
state.tokenize = inText;
|
147
|
+
break;
|
148
|
+
}
|
149
|
+
stream.next();
|
150
|
+
}
|
151
|
+
return style;
|
152
|
+
};
|
153
|
+
}
|
154
|
+
function doctype(depth) {
|
155
|
+
return function(stream, state) {
|
156
|
+
var ch;
|
157
|
+
while ((ch = stream.next()) != null) {
|
158
|
+
if (ch == "<") {
|
159
|
+
state.tokenize = doctype(depth + 1);
|
160
|
+
return state.tokenize(stream, state);
|
161
|
+
} else if (ch == ">") {
|
162
|
+
if (depth == 1) {
|
163
|
+
state.tokenize = inText;
|
164
|
+
break;
|
165
|
+
} else {
|
166
|
+
state.tokenize = doctype(depth - 1);
|
167
|
+
return state.tokenize(stream, state);
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
return "meta";
|
172
|
+
};
|
173
|
+
}
|
174
|
+
|
175
|
+
function Context(state, tagName, startOfLine) {
|
176
|
+
this.prev = state.context;
|
177
|
+
this.tagName = tagName;
|
178
|
+
this.indent = state.indented;
|
179
|
+
this.startOfLine = startOfLine;
|
180
|
+
if (Kludges.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
|
181
|
+
this.noIndent = true;
|
182
|
+
}
|
183
|
+
function popContext(state) {
|
184
|
+
if (state.context) state.context = state.context.prev;
|
185
|
+
}
|
186
|
+
function maybePopContext(state, nextTagName) {
|
187
|
+
var parentTagName;
|
188
|
+
while (true) {
|
189
|
+
if (!state.context) {
|
190
|
+
return;
|
191
|
+
}
|
192
|
+
parentTagName = state.context.tagName.toLowerCase();
|
193
|
+
if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
|
194
|
+
!Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
|
195
|
+
return;
|
196
|
+
}
|
197
|
+
popContext(state);
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
function baseState(type, stream, state) {
|
202
|
+
if (type == "openTag") {
|
203
|
+
state.tagName = tagName;
|
204
|
+
state.tagStart = stream.column();
|
205
|
+
return attrState;
|
206
|
+
} else if (type == "closeTag") {
|
207
|
+
var err = false;
|
208
|
+
if (state.context) {
|
209
|
+
if (state.context.tagName != tagName) {
|
210
|
+
if (Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName.toLowerCase()))
|
211
|
+
popContext(state);
|
212
|
+
err = !state.context || state.context.tagName != tagName;
|
213
|
+
}
|
214
|
+
} else {
|
215
|
+
err = true;
|
216
|
+
}
|
217
|
+
if (err) setStyle = "error";
|
218
|
+
return err ? closeStateErr : closeState;
|
219
|
+
} else {
|
220
|
+
return baseState;
|
221
|
+
}
|
222
|
+
}
|
223
|
+
function closeState(type, _stream, state) {
|
224
|
+
if (type != "endTag") {
|
225
|
+
setStyle = "error";
|
226
|
+
return closeState;
|
227
|
+
}
|
228
|
+
popContext(state);
|
229
|
+
return baseState;
|
230
|
+
}
|
231
|
+
function closeStateErr(type, stream, state) {
|
232
|
+
setStyle = "error";
|
233
|
+
return closeState(type, stream, state);
|
234
|
+
}
|
235
|
+
|
236
|
+
function attrState(type, _stream, state) {
|
237
|
+
if (type == "word") {
|
238
|
+
setStyle = "attribute";
|
239
|
+
return attrEqState;
|
240
|
+
} else if (type == "endTag" || type == "selfcloseTag") {
|
241
|
+
var tagName = state.tagName, tagStart = state.tagStart;
|
242
|
+
state.tagName = state.tagStart = null;
|
243
|
+
if (type == "selfcloseTag" ||
|
244
|
+
Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase())) {
|
245
|
+
maybePopContext(state, tagName.toLowerCase());
|
246
|
+
} else {
|
247
|
+
maybePopContext(state, tagName.toLowerCase());
|
248
|
+
state.context = new Context(state, tagName, tagStart == state.indented);
|
249
|
+
}
|
250
|
+
return baseState;
|
251
|
+
}
|
252
|
+
setStyle = "error";
|
253
|
+
return attrState;
|
254
|
+
}
|
255
|
+
function attrEqState(type, stream, state) {
|
256
|
+
if (type == "equals") return attrValueState;
|
257
|
+
if (!Kludges.allowMissing) setStyle = "error";
|
258
|
+
return attrState(type, stream, state);
|
259
|
+
}
|
260
|
+
function attrValueState(type, stream, state) {
|
261
|
+
if (type == "string") return attrContinuedState;
|
262
|
+
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return attrState;}
|
263
|
+
setStyle = "error";
|
264
|
+
return attrState(type, stream, state);
|
265
|
+
}
|
266
|
+
function attrContinuedState(type, stream, state) {
|
267
|
+
if (type == "string") return attrContinuedState;
|
268
|
+
return attrState(type, stream, state);
|
269
|
+
}
|
270
|
+
|
271
|
+
return {
|
272
|
+
startState: function() {
|
273
|
+
return {tokenize: inText,
|
274
|
+
state: baseState,
|
275
|
+
indented: 0,
|
276
|
+
tagName: null, tagStart: null,
|
277
|
+
context: null};
|
278
|
+
},
|
279
|
+
|
280
|
+
token: function(stream, state) {
|
281
|
+
if (!state.tagName && stream.sol())
|
282
|
+
state.indented = stream.indentation();
|
283
|
+
|
284
|
+
if (stream.eatSpace()) return null;
|
285
|
+
tagName = type = null;
|
286
|
+
var style = state.tokenize(stream, state);
|
287
|
+
if ((style || type) && style != "comment") {
|
288
|
+
setStyle = null;
|
289
|
+
state.state = state.state(type || style, stream, state);
|
290
|
+
if (setStyle)
|
291
|
+
style = setStyle == "error" ? style + " error" : setStyle;
|
292
|
+
}
|
293
|
+
return style;
|
294
|
+
},
|
295
|
+
|
296
|
+
indent: function(state, textAfter, fullLine) {
|
297
|
+
var context = state.context;
|
298
|
+
// Indent multi-line strings (e.g. css).
|
299
|
+
if (state.tokenize.isInAttribute) {
|
300
|
+
return state.stringStartCol + 1;
|
301
|
+
}
|
302
|
+
if (context && context.noIndent) return CodeMirror.Pass;
|
303
|
+
if (state.tokenize != inTag && state.tokenize != inText)
|
304
|
+
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
|
305
|
+
// Indent the starts of attribute names.
|
306
|
+
if (state.tagName) {
|
307
|
+
if (multilineTagIndentPastTag)
|
308
|
+
return state.tagStart + state.tagName.length + 2;
|
309
|
+
else
|
310
|
+
return state.tagStart + indentUnit * multilineTagIndentFactor;
|
311
|
+
}
|
312
|
+
if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
|
313
|
+
if (context && /^<\//.test(textAfter))
|
314
|
+
context = context.prev;
|
315
|
+
while (context && !context.startOfLine)
|
316
|
+
context = context.prev;
|
317
|
+
if (context) return context.indent + indentUnit;
|
318
|
+
else return 0;
|
319
|
+
},
|
320
|
+
|
321
|
+
electricChars: "/",
|
322
|
+
blockCommentStart: "<!--",
|
323
|
+
blockCommentEnd: "-->",
|
324
|
+
|
325
|
+
configuration: parserConfig.htmlMode ? "html" : "xml",
|
326
|
+
helperType: parserConfig.htmlMode ? "html" : "xml"
|
327
|
+
};
|
328
|
+
});
|
329
|
+
|
330
|
+
CodeMirror.defineMIME("text/xml", "xml");
|
331
|
+
CodeMirror.defineMIME("application/xml", "xml");
|
332
|
+
if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
|
333
|
+
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
|
@@ -0,0 +1,97 @@
|
|
1
|
+
CodeMirror.defineMode("yaml", function() {
|
2
|
+
|
3
|
+
var cons = ['true', 'false', 'on', 'off', 'yes', 'no'];
|
4
|
+
var keywordRegex = new RegExp("\\b(("+cons.join(")|(")+"))$", 'i');
|
5
|
+
|
6
|
+
return {
|
7
|
+
token: function(stream, state) {
|
8
|
+
var ch = stream.peek();
|
9
|
+
var esc = state.escaped;
|
10
|
+
state.escaped = false;
|
11
|
+
/* comments */
|
12
|
+
if (ch == "#" && (stream.pos == 0 || /\s/.test(stream.string.charAt(stream.pos - 1)))) {
|
13
|
+
stream.skipToEnd(); return "comment";
|
14
|
+
}
|
15
|
+
if (state.literal && stream.indentation() > state.keyCol) {
|
16
|
+
stream.skipToEnd(); return "string";
|
17
|
+
} else if (state.literal) { state.literal = false; }
|
18
|
+
if (stream.sol()) {
|
19
|
+
state.keyCol = 0;
|
20
|
+
state.pair = false;
|
21
|
+
state.pairStart = false;
|
22
|
+
/* document start */
|
23
|
+
if(stream.match(/---/)) { return "def"; }
|
24
|
+
/* document end */
|
25
|
+
if (stream.match(/\.\.\./)) { return "def"; }
|
26
|
+
/* array list item */
|
27
|
+
if (stream.match(/\s*-\s+/)) { return 'meta'; }
|
28
|
+
}
|
29
|
+
/* inline pairs/lists */
|
30
|
+
if (stream.match(/^(\{|\}|\[|\])/)) {
|
31
|
+
if (ch == '{')
|
32
|
+
state.inlinePairs++;
|
33
|
+
else if (ch == '}')
|
34
|
+
state.inlinePairs--;
|
35
|
+
else if (ch == '[')
|
36
|
+
state.inlineList++;
|
37
|
+
else
|
38
|
+
state.inlineList--;
|
39
|
+
return 'meta';
|
40
|
+
}
|
41
|
+
|
42
|
+
/* list seperator */
|
43
|
+
if (state.inlineList > 0 && !esc && ch == ',') {
|
44
|
+
stream.next();
|
45
|
+
return 'meta';
|
46
|
+
}
|
47
|
+
/* pairs seperator */
|
48
|
+
if (state.inlinePairs > 0 && !esc && ch == ',') {
|
49
|
+
state.keyCol = 0;
|
50
|
+
state.pair = false;
|
51
|
+
state.pairStart = false;
|
52
|
+
stream.next();
|
53
|
+
return 'meta';
|
54
|
+
}
|
55
|
+
|
56
|
+
/* start of value of a pair */
|
57
|
+
if (state.pairStart) {
|
58
|
+
/* block literals */
|
59
|
+
if (stream.match(/^\s*(\||\>)\s*/)) { state.literal = true; return 'meta'; };
|
60
|
+
/* references */
|
61
|
+
if (stream.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i)) { return 'variable-2'; }
|
62
|
+
/* numbers */
|
63
|
+
if (state.inlinePairs == 0 && stream.match(/^\s*-?[0-9\.\,]+\s?$/)) { return 'number'; }
|
64
|
+
if (state.inlinePairs > 0 && stream.match(/^\s*-?[0-9\.\,]+\s?(?=(,|}))/)) { return 'number'; }
|
65
|
+
/* keywords */
|
66
|
+
if (stream.match(keywordRegex)) { return 'keyword'; }
|
67
|
+
}
|
68
|
+
|
69
|
+
/* pairs (associative arrays) -> key */
|
70
|
+
if (!state.pair && stream.match(/^\s*\S+(?=\s*:($|\s))/i)) {
|
71
|
+
state.pair = true;
|
72
|
+
state.keyCol = stream.indentation();
|
73
|
+
return "atom";
|
74
|
+
}
|
75
|
+
if (state.pair && stream.match(/^:\s*/)) { state.pairStart = true; return 'meta'; }
|
76
|
+
|
77
|
+
/* nothing found, continue */
|
78
|
+
state.pairStart = false;
|
79
|
+
state.escaped = (ch == '\\');
|
80
|
+
stream.next();
|
81
|
+
return null;
|
82
|
+
},
|
83
|
+
startState: function() {
|
84
|
+
return {
|
85
|
+
pair: false,
|
86
|
+
pairStart: false,
|
87
|
+
keyCol: 0,
|
88
|
+
inlinePairs: 0,
|
89
|
+
inlineList: 0,
|
90
|
+
literal: false,
|
91
|
+
escaped: false
|
92
|
+
};
|
93
|
+
}
|
94
|
+
};
|
95
|
+
});
|
96
|
+
|
97
|
+
CodeMirror.defineMIME("text/x-yaml", "yaml");
|
@@ -0,0 +1,336 @@
|
|
1
|
+
/*jslint newcap: true */
|
2
|
+
/*global XMLHttpRequest: false, inlineAttach: false, FormData: false */
|
3
|
+
/*
|
4
|
+
* Inline Text Attachment
|
5
|
+
*
|
6
|
+
* Author: Roy van Kaathoven
|
7
|
+
* Contact: ik@royvankaathoven.nl
|
8
|
+
*/
|
9
|
+
(function(document, window) {
|
10
|
+
"use strict";
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Simple function to merge the given objects
|
14
|
+
*
|
15
|
+
* @param {Object[]} object Multiple object parameters
|
16
|
+
* @returns {Object}
|
17
|
+
*/
|
18
|
+
function merge() {
|
19
|
+
var result = {};
|
20
|
+
for (var i = arguments.length - 1; i >= 0; i--) {
|
21
|
+
var obj = arguments[i];
|
22
|
+
for (var k in obj) {
|
23
|
+
result[k] = obj[k];
|
24
|
+
}
|
25
|
+
}
|
26
|
+
return result;
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* @param {Object} options
|
31
|
+
*/
|
32
|
+
window.inlineAttach = function(options, instance) {
|
33
|
+
|
34
|
+
var settings = merge(options, inlineAttach.defaults),
|
35
|
+
editor = instance,
|
36
|
+
filenameTag = '{filename}',
|
37
|
+
lastValue,
|
38
|
+
me = this;
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Upload a given file blob
|
42
|
+
*
|
43
|
+
* @param {Blob} file
|
44
|
+
*/
|
45
|
+
this.uploadFile = function(file) {
|
46
|
+
var formData = new FormData(),
|
47
|
+
xhr = new XMLHttpRequest(),
|
48
|
+
extension = 'png';
|
49
|
+
|
50
|
+
// Attach the file. If coming from clipboard, add a default filename (only works in Chrome for now)
|
51
|
+
// http://stackoverflow.com/questions/6664967/how-to-give-a-blob-uploaded-as-formdata-a-file-name
|
52
|
+
if (file.name) {
|
53
|
+
var fileNameMatches = file.name.match(/\.(.+)$/);
|
54
|
+
if (fileNameMatches) {
|
55
|
+
extension = fileNameMatches[1];
|
56
|
+
}
|
57
|
+
}
|
58
|
+
formData.append(settings.uploadFieldName, file, "image-" + Date.now() + "." + extension);
|
59
|
+
|
60
|
+
// Add any available extra parameters
|
61
|
+
if (typeof settings.extraParams === "object") {
|
62
|
+
for (var key in settings.extraParams) {
|
63
|
+
if (settings.extraParams.hasOwnProperty(key)) {
|
64
|
+
formData.append(key, settings.extraParams[key]);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
xhr.open('POST', settings.uploadUrl);
|
70
|
+
xhr.onload = function() {
|
71
|
+
// If HTTP status is OK or Created
|
72
|
+
if (xhr.status === 200 || xhr.status === 201) {
|
73
|
+
var data = JSON.parse(xhr.responseText);
|
74
|
+
me.onUploadedFile(data);
|
75
|
+
} else {
|
76
|
+
me.onErrorUploading();
|
77
|
+
}
|
78
|
+
};
|
79
|
+
xhr.send(formData);
|
80
|
+
};
|
81
|
+
|
82
|
+
/**
|
83
|
+
* Check if the given file is allowed
|
84
|
+
*
|
85
|
+
* @param {File} file
|
86
|
+
*/
|
87
|
+
this.isAllowedFile = function(file) {
|
88
|
+
return settings.allowedTypes.indexOf(file.type) >= 0;
|
89
|
+
};
|
90
|
+
|
91
|
+
/**
|
92
|
+
* When a file has finished uploading
|
93
|
+
*
|
94
|
+
* @param {Object} data
|
95
|
+
*/
|
96
|
+
this.onUploadedFile = function(data) {
|
97
|
+
var result = settings.onUploadedFile(data),
|
98
|
+
filename = data[settings.downloadFieldName];
|
99
|
+
if (result !== false && filename) {
|
100
|
+
var text = editor.getValue().replace(lastValue, settings.urlText.replace(filenameTag, filename));
|
101
|
+
editor.setValue(text);
|
102
|
+
}
|
103
|
+
};
|
104
|
+
|
105
|
+
/**
|
106
|
+
* Custom upload handler
|
107
|
+
*
|
108
|
+
* @param {Blob} file
|
109
|
+
* @return {Boolean} when false is returned it will prevent default upload behavior
|
110
|
+
*/
|
111
|
+
this.customUploadHandler = function(file) {
|
112
|
+
return settings.customUploadHandler(file);
|
113
|
+
};
|
114
|
+
|
115
|
+
/**
|
116
|
+
* When a file didn't upload properly.
|
117
|
+
* Override by passing your own onErrorUploading function with settings.
|
118
|
+
*
|
119
|
+
* @param {Object} data
|
120
|
+
*/
|
121
|
+
this.onErrorUploading = function() {
|
122
|
+
var text = editor.getValue().replace(lastValue, "");
|
123
|
+
editor.setValue(text);
|
124
|
+
if (settings.customErrorHandler()) {
|
125
|
+
window.alert(settings.errorText);
|
126
|
+
}
|
127
|
+
};
|
128
|
+
|
129
|
+
/**
|
130
|
+
* Append a line of text at the bottom, ensuring there aren't unnecessary newlines
|
131
|
+
*
|
132
|
+
* @param {String} appended Current content
|
133
|
+
* @param {String} previous Value which should be appended after the current content
|
134
|
+
*/
|
135
|
+
function appendInItsOwnLine(previous, appended) {
|
136
|
+
return (previous + "\n\n[[D]]" + appended)
|
137
|
+
.replace(/(\n{2,})\[\[D\]\]/, "\n\n")
|
138
|
+
.replace(/^(\n*)/, "");
|
139
|
+
}
|
140
|
+
|
141
|
+
/**
|
142
|
+
* When a file has been received by a drop or paste event
|
143
|
+
* @param {Blob} file
|
144
|
+
*/
|
145
|
+
this.onReceivedFile = function(file) {
|
146
|
+
var result = settings.onReceivedFile(file);
|
147
|
+
if (result !== false) {
|
148
|
+
lastValue = settings.progressText;
|
149
|
+
editor.setValue(appendInItsOwnLine(editor.getValue(), lastValue));
|
150
|
+
}
|
151
|
+
};
|
152
|
+
|
153
|
+
/**
|
154
|
+
* Catches the paste event
|
155
|
+
*
|
156
|
+
* @param {Event} e
|
157
|
+
* @returns {Boolean} If a file is handled
|
158
|
+
*/
|
159
|
+
this.onPaste = function(e) {
|
160
|
+
var result = false,
|
161
|
+
clipboardData = e.clipboardData,
|
162
|
+
items;
|
163
|
+
|
164
|
+
if (typeof clipboardData === "object") {
|
165
|
+
|
166
|
+
items = clipboardData.items || clipboardData.files || [];
|
167
|
+
|
168
|
+
for (var i = 0; i < items.length; i++) {
|
169
|
+
var item = items[i];
|
170
|
+
if (me.isAllowedFile(item)) {
|
171
|
+
result = true;
|
172
|
+
this.onReceivedFile(item.getAsFile());
|
173
|
+
if (this.customUploadHandler(item.getAsFile())) {
|
174
|
+
this.uploadFile(item.getAsFile());
|
175
|
+
}
|
176
|
+
}
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
180
|
+
|
181
|
+
return result;
|
182
|
+
};
|
183
|
+
|
184
|
+
/**
|
185
|
+
* Catches onDrop event
|
186
|
+
*
|
187
|
+
* @param {Event} e
|
188
|
+
* @returns {Boolean} If a file is handled
|
189
|
+
*/
|
190
|
+
this.onDrop = function(e) {
|
191
|
+
var result = false;
|
192
|
+
for (var i = 0; i < e.dataTransfer.files.length; i++) {
|
193
|
+
var file = e.dataTransfer.files[i];
|
194
|
+
if (me.isAllowedFile(file)) {
|
195
|
+
result = true;
|
196
|
+
this.onReceivedFile(file);
|
197
|
+
if (this.customUploadHandler(file)) {
|
198
|
+
this.uploadFile(file);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
return result;
|
204
|
+
};
|
205
|
+
};
|
206
|
+
|
207
|
+
/**
|
208
|
+
* Editor
|
209
|
+
*/
|
210
|
+
window.inlineAttach.Editor = function(instance) {
|
211
|
+
|
212
|
+
var input = instance;
|
213
|
+
|
214
|
+
return {
|
215
|
+
getValue: function() {
|
216
|
+
return input.value;
|
217
|
+
},
|
218
|
+
setValue: function(val) {
|
219
|
+
input.value = val;
|
220
|
+
}
|
221
|
+
};
|
222
|
+
};
|
223
|
+
|
224
|
+
/**
|
225
|
+
* Default configuration
|
226
|
+
*/
|
227
|
+
window.inlineAttach.defaults = {
|
228
|
+
/**
|
229
|
+
* URL to upload the attachment
|
230
|
+
*/
|
231
|
+
uploadUrl: 'upload_attachment.php',
|
232
|
+
|
233
|
+
/**
|
234
|
+
* Request field name where the attachment will be placed in the form data
|
235
|
+
*/
|
236
|
+
uploadFieldName: 'file',
|
237
|
+
|
238
|
+
/**
|
239
|
+
* Where is the filename placed in the response
|
240
|
+
*/
|
241
|
+
downloadFieldName: 'filename',
|
242
|
+
|
243
|
+
/**
|
244
|
+
* Allowed types
|
245
|
+
*/
|
246
|
+
allowedTypes: [
|
247
|
+
'image/jpeg',
|
248
|
+
'image/png',
|
249
|
+
'image/jpg',
|
250
|
+
'image/gif'
|
251
|
+
],
|
252
|
+
|
253
|
+
/**
|
254
|
+
* Will be inserted on a drop or paste event
|
255
|
+
*/
|
256
|
+
progressText: '![Uploading file...]()',
|
257
|
+
|
258
|
+
/**
|
259
|
+
* When a file has successfully been uploaded the last inserted text
|
260
|
+
* will be replaced by the urlText, the {filename} tag will be replaced
|
261
|
+
* by the filename that has been returned by the server
|
262
|
+
*/
|
263
|
+
urlText: "",
|
264
|
+
|
265
|
+
/**
|
266
|
+
* Text for default error when uploading
|
267
|
+
*/
|
268
|
+
errorText: "Error uploading file",
|
269
|
+
|
270
|
+
/**
|
271
|
+
* Extra parameters which will be send when uploading a file
|
272
|
+
*/
|
273
|
+
extraParams: {},
|
274
|
+
|
275
|
+
/**
|
276
|
+
* When a file is received by drag-drop or paste
|
277
|
+
*/
|
278
|
+
onReceivedFile: function() {},
|
279
|
+
|
280
|
+
/**
|
281
|
+
* Custom upload handler
|
282
|
+
*
|
283
|
+
* @return {Boolean} when false is returned it will prevent default upload behavior
|
284
|
+
*/
|
285
|
+
customUploadHandler: function() {
|
286
|
+
return true;
|
287
|
+
},
|
288
|
+
|
289
|
+
/**
|
290
|
+
* Custom error handler. Runs after removing the placeholder text and before the alert().
|
291
|
+
* Return false from this function to prevent the alert dialog.
|
292
|
+
*
|
293
|
+
* @return {Boolean} when false is returned it will prevent default error behavior
|
294
|
+
*/
|
295
|
+
customErrorHandler: function() {
|
296
|
+
return true;
|
297
|
+
},
|
298
|
+
|
299
|
+
/**
|
300
|
+
* When a file has succesfully been uploaded
|
301
|
+
*/
|
302
|
+
onUploadedFile: function() {}
|
303
|
+
};
|
304
|
+
|
305
|
+
/**
|
306
|
+
* Attach to a standard input field
|
307
|
+
*
|
308
|
+
* @param {Input} input
|
309
|
+
* @param {Object} options
|
310
|
+
*/
|
311
|
+
window.inlineAttach.attachToInput = function(input, options) {
|
312
|
+
|
313
|
+
options = options || {};
|
314
|
+
|
315
|
+
var editor = new inlineAttach.Editor(input),
|
316
|
+
inlineattach = new inlineAttach(options, editor);
|
317
|
+
|
318
|
+
input.addEventListener('paste', function(e) {
|
319
|
+
inlineattach.onPaste(e);
|
320
|
+
}, false);
|
321
|
+
input.addEventListener('drop', function(e) {
|
322
|
+
e.stopPropagation();
|
323
|
+
e.preventDefault();
|
324
|
+
inlineattach.onDrop(e);
|
325
|
+
}, false);
|
326
|
+
input.addEventListener('dragenter', function(e) {
|
327
|
+
e.stopPropagation();
|
328
|
+
e.preventDefault();
|
329
|
+
}, false);
|
330
|
+
input.addEventListener('dragover', function(e) {
|
331
|
+
e.stopPropagation();
|
332
|
+
e.preventDefault();
|
333
|
+
}, false);
|
334
|
+
};
|
335
|
+
|
336
|
+
})(document, window);
|