bee_api 0.0.5
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.
- data/bin/bee_api +84 -0
- data/lib/mdpreview.rb +80 -0
- data/lib/mdpreview/translator.rb +60 -0
- data/lib/mdpreview/version.rb +3 -0
- data/test/mdptest.rb +100 -0
- data/vendor/HISTORY.md +237 -0
- data/vendor/Jakefile.js +316 -0
- data/vendor/LICENSE +176 -0
- data/vendor/NOTICE +17 -0
- data/vendor/README.md +102 -0
- data/vendor/app/chrome/documentation.txt +12 -0
- data/vendor/app/chrome/manifest.json +24 -0
- data/vendor/app/web/ajax.js +43 -0
- data/vendor/app/web/app.css +292 -0
- data/vendor/app/web/app.js +377 -0
- data/vendor/app/web/beta/index.html +17 -0
- data/vendor/app/web/datapolicy.txt +48 -0
- data/vendor/app/web/doc/doc.css +60 -0
- data/vendor/app/web/doc/img/actions_menu.png +0 -0
- data/vendor/app/web/doc/img/button_actions_menu.png +0 -0
- data/vendor/app/web/doc/img/button_dragarea.png +0 -0
- data/vendor/app/web/doc/img/jsoneditor.png +0 -0
- data/vendor/app/web/doc/img/jsonformatter.png +0 -0
- data/vendor/app/web/doc/img/main_menu.png +0 -0
- data/vendor/app/web/doc/img/splitter.png +0 -0
- data/vendor/app/web/doc/index.html +201 -0
- data/vendor/app/web/favicon.ico +0 -0
- data/vendor/app/web/fileretriever.css +54 -0
- data/vendor/app/web/fileretriever.js +567 -0
- data/vendor/app/web/fileretriever.php +120 -0
- data/vendor/app/web/googlea47c4a0b36d11021.html +1 -0
- data/vendor/app/web/hash.js +133 -0
- data/vendor/app/web/img/description.txt +20 -0
- data/vendor/app/web/img/header_background.png +0 -0
- data/vendor/app/web/img/icon_128.png +0 -0
- data/vendor/app/web/img/icon_16.png +0 -0
- data/vendor/app/web/img/icon_gray.svg +151 -0
- data/vendor/app/web/img/icon_gray_16.svg +150 -0
- data/vendor/app/web/img/icon_orange.svg +151 -0
- data/vendor/app/web/img/logo.png +0 -0
- data/vendor/app/web/img/logo.xcf +0 -0
- data/vendor/app/web/img/logo_app.png +0 -0
- data/vendor/app/web/img/logo_app.xcf +0 -0
- data/vendor/app/web/index.html +191 -0
- data/vendor/app/web/notify.js +150 -0
- data/vendor/app/web/queryparams.js +71 -0
- data/vendor/app/web/robots.txt +0 -0
- data/vendor/app/web/splitter.js +179 -0
- data/vendor/app/web/test.html +224 -0
- data/vendor/component.json +33 -0
- data/vendor/docs/api.md +188 -0
- data/vendor/docs/usage.md +137 -0
- data/vendor/examples/01_basic_usage.html +45 -0
- data/vendor/examples/02_viewer.html +38 -0
- data/vendor/examples/03_switch_mode.html +98 -0
- data/vendor/examples/cur.file +1 -0
- data/vendor/examples/jquery.js +2 -0
- data/vendor/examples/meta.js +1 -0
- data/vendor/examples/requirejs_demo/requirejs_demo.html +19 -0
- data/vendor/examples/requirejs_demo/scripts/main.js +25 -0
- data/vendor/examples/requirejs_demo/scripts/require.js +35 -0
- data/vendor/img/jsoneditor-icons.png +0 -0
- data/vendor/jsoneditor-min.css +1 -0
- data/vendor/jsoneditor-min.js +34 -0
- data/vendor/jsoneditor.css +597 -0
- data/vendor/jsoneditor.js +6069 -0
- data/vendor/jsoneditor/css/contextmenu.css +219 -0
- data/vendor/jsoneditor/css/img/description.txt +13 -0
- data/vendor/jsoneditor/css/img/export.sh +16 -0
- data/vendor/jsoneditor/css/img/jsoneditor-icons.png +0 -0
- data/vendor/jsoneditor/css/img/jsoneditor-icons.svg +861 -0
- data/vendor/jsoneditor/css/jsoneditor.css +220 -0
- data/vendor/jsoneditor/css/menu.css +81 -0
- data/vendor/jsoneditor/css/searchbox.css +73 -0
- data/vendor/jsoneditor/js/appendnode.js +211 -0
- data/vendor/jsoneditor/js/contextmenu.js +440 -0
- data/vendor/jsoneditor/js/header.js +32 -0
- data/vendor/jsoneditor/js/highlighter.js +82 -0
- data/vendor/jsoneditor/js/history.js +218 -0
- data/vendor/jsoneditor/js/jsoneditor.js +206 -0
- data/vendor/jsoneditor/js/module.js +50 -0
- data/vendor/jsoneditor/js/node.js +2864 -0
- data/vendor/jsoneditor/js/searchbox.js +288 -0
- data/vendor/jsoneditor/js/texteditor.js +311 -0
- data/vendor/jsoneditor/js/treeeditor.js +770 -0
- data/vendor/jsoneditor/js/util.js +582 -0
- data/vendor/lib/ace/ace.js +11 -0
- data/vendor/lib/ace/mode-json.js +1 -0
- data/vendor/lib/ace/theme-jsoneditor.js +144 -0
- data/vendor/lib/ace/theme-textmate.js +163 -0
- data/vendor/lib/ace/worker-json.js +1 -0
- data/vendor/lib/jsonlint/README.md +62 -0
- data/vendor/lib/jsonlint/jsonlint.js +432 -0
- data/vendor/misc/screenshots/actionsmenu_640x400.png +0 -0
- data/vendor/misc/screenshots/codeeditor_640x400.png +0 -0
- data/vendor/misc/screenshots/description.json +17 -0
- data/vendor/misc/screenshots/jsoneditoronline.png +0 -0
- data/vendor/misc/screenshots/jsoneditoronline_640x400.png +0 -0
- data/vendor/misc/screenshots/search_640x400.png +0 -0
- data/vendor/misc/screenshots/small_tile.xcf +0 -0
- data/vendor/misc/screenshots/small_tile_440x280.png +0 -0
- data/vendor/misc/todo.txt +101 -0
- data/vendor/package.json +28 -0
- data/vendor/test/couchdbeditor.html +100 -0
- data/vendor/test/largefile.json +12605 -0
- data/vendor/test/test_ace.html +60 -0
- data/vendor/test/test_editable_div.html +449 -0
- metadata +154 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<!DOCTYPE HTML>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>JSON Editor Online - Documentation</title>
|
|
5
|
+
<link href="doc.css" rel="stylesheet" type="text/css">
|
|
6
|
+
</head>
|
|
7
|
+
|
|
8
|
+
<body>
|
|
9
|
+
<div id="container">
|
|
10
|
+
<h1 id="documentation">JSON Editor Online - Documentation</h1>
|
|
11
|
+
|
|
12
|
+
<h2 id="introduction">Introduction</h2>
|
|
13
|
+
<p>
|
|
14
|
+
JSON Editor Online is a web-based tool to view, edit, and format JSON.
|
|
15
|
+
It shows your data side by side in a clear, editable treeview and in
|
|
16
|
+
a code editor.
|
|
17
|
+
</p>
|
|
18
|
+
<p>
|
|
19
|
+
Supported browsers: Chrome, Firefox, Safari, Opera, Internet Explorer 8+.
|
|
20
|
+
</p>
|
|
21
|
+
<p>
|
|
22
|
+
Website: <a href="http://jsoneditoronline.org" target="_blank">
|
|
23
|
+
http://jsoneditoronline.org</a>.
|
|
24
|
+
</p>
|
|
25
|
+
<p>
|
|
26
|
+
Contents:
|
|
27
|
+
</p>
|
|
28
|
+
<ul>
|
|
29
|
+
<li><a href="#main_menu">Main menu</a></li>
|
|
30
|
+
<li><a href="#panels">Panels</a></li>
|
|
31
|
+
<li><a href="#formatter">Formatter</a></li>
|
|
32
|
+
<li><a href="#editor">Editor</a></li>
|
|
33
|
+
<li><a href="#shortcut_keys">Shortcut keys</a></li>
|
|
34
|
+
</ul>
|
|
35
|
+
|
|
36
|
+
<h2 id="main_menu">Main menu</h2>
|
|
37
|
+
<p>
|
|
38
|
+
The applications main menu contains options to clear, load and save the
|
|
39
|
+
JSON contents of the application. Files can be loaded from disk or url,
|
|
40
|
+
and can be saved to disk. Please note that due to security restrictions,
|
|
41
|
+
the application can only open files from public websites, not from an
|
|
42
|
+
intranet. The data policy is described
|
|
43
|
+
<a href="http://jsoneditoronline.org/datapolicy.txt">here</a>.
|
|
44
|
+
</p>
|
|
45
|
+
<img src="img/main_menu.png" alt="Main menu">
|
|
46
|
+
|
|
47
|
+
<h2 id="panels">Panels</h2>
|
|
48
|
+
<p>
|
|
49
|
+
The application contains two panels: a <b>JSON Formatter</b> on the left,
|
|
50
|
+
and a <b>JSON Editor</b> on the right.
|
|
51
|
+
</p>
|
|
52
|
+
<p>
|
|
53
|
+
There is a splitter between the two panels, allowing to change the
|
|
54
|
+
width of both panels according to ones needs.
|
|
55
|
+
To copy the contents from one panel to an other, the two copy buttons
|
|
56
|
+
between the panels can be used.
|
|
57
|
+
</p>
|
|
58
|
+
<img src="img/splitter.png">
|
|
59
|
+
|
|
60
|
+
<h2 id="formatter"> Formatter</h2>
|
|
61
|
+
<p>
|
|
62
|
+
The JSON Formatter displays JSON data in a code editor.
|
|
63
|
+
The Formatter is capable of formatting, compacting, and inspecting JSON.
|
|
64
|
+
</p>
|
|
65
|
+
<img src="img/jsonformatter.png">
|
|
66
|
+
<p>
|
|
67
|
+
The menu of the Formatter contains the following buttons:
|
|
68
|
+
</p>
|
|
69
|
+
<ul>
|
|
70
|
+
<li>
|
|
71
|
+
<b>Format</b>.
|
|
72
|
+
Format the JSON data, make the data readable by applying indentation
|
|
73
|
+
and returns.
|
|
74
|
+
</li>
|
|
75
|
+
<li>
|
|
76
|
+
<b>Compact</b>.
|
|
77
|
+
Compact the JSON data, remove all unnecessary characters like
|
|
78
|
+
whitespaces and returns.
|
|
79
|
+
</li>
|
|
80
|
+
</ul>
|
|
81
|
+
|
|
82
|
+
<h2 id="editor">Editor</h2>
|
|
83
|
+
<p>
|
|
84
|
+
The JSON Editor displays the JSON data in an editable tree.
|
|
85
|
+
The editor makes it easy to create, duplicate, remove fields,
|
|
86
|
+
and to edit the contents of the fields.
|
|
87
|
+
</p>
|
|
88
|
+
<img src="img/jsoneditor.png">
|
|
89
|
+
<p>
|
|
90
|
+
The menu of the editor contains the following functions:
|
|
91
|
+
</p>
|
|
92
|
+
<ul>
|
|
93
|
+
<li>
|
|
94
|
+
<b>Expand all</b>. Expand all fields in the editor.
|
|
95
|
+
</li>
|
|
96
|
+
<li>
|
|
97
|
+
<b>Collapse all</b>. Collapse all fields in the editor.
|
|
98
|
+
</li>
|
|
99
|
+
<li>
|
|
100
|
+
<b>Undo</b>. Undo last action.
|
|
101
|
+
</li>
|
|
102
|
+
<li>
|
|
103
|
+
<b>Redo</b>. Redo last action.
|
|
104
|
+
</li>
|
|
105
|
+
<li>
|
|
106
|
+
<b>Search</b>. Search for text in the JSON editor.
|
|
107
|
+
Search results will be highlighted, and can be iterated by
|
|
108
|
+
repeatedly pressing Enter or Shift+Enter.
|
|
109
|
+
The right side of the search box two buttons to go to the next or
|
|
110
|
+
previous search result.
|
|
111
|
+
</li>
|
|
112
|
+
</ul>
|
|
113
|
+
|
|
114
|
+
<p>
|
|
115
|
+
The field values in the editor are editable input fields.
|
|
116
|
+
The fields can be dragged up and down using the dragarea
|
|
117
|
+
<img src="img/button_dragarea.png" class="icon">
|
|
118
|
+
on the left side of the fields. When a field is the last item of the
|
|
119
|
+
childs of an array or object, the field can also be dragged horizontally
|
|
120
|
+
to move it in or out of the array or object.
|
|
121
|
+
</p>
|
|
122
|
+
<img src="img/actions_menu.png" align="right" style="padding-left: 20px;">
|
|
123
|
+
<p>
|
|
124
|
+
Right from the dragarea is a button
|
|
125
|
+
<img src="img/button_actions_menu.png" class="icon">
|
|
126
|
+
to open the <b>actions menu</b>.
|
|
127
|
+
Depending on the type of field, the following functionality is
|
|
128
|
+
available in the actions menu:
|
|
129
|
+
</p>
|
|
130
|
+
<ul>
|
|
131
|
+
<li>
|
|
132
|
+
<b>Type</b>. Change the type of the field. Choose from:
|
|
133
|
+
<ul>
|
|
134
|
+
<li>
|
|
135
|
+
<b>auto</b> The field type is automatically determined from
|
|
136
|
+
the value and can be a string, number, boolean, or null.
|
|
137
|
+
</li>
|
|
138
|
+
<li>
|
|
139
|
+
<b>object</b> An unordered set of key/value pairs.
|
|
140
|
+
</li>
|
|
141
|
+
<li>
|
|
142
|
+
<b>array</b> An ordered collection of values.
|
|
143
|
+
</li>
|
|
144
|
+
<li>
|
|
145
|
+
<b>string</b> Field type is not determined from the value,
|
|
146
|
+
but always returned as string.
|
|
147
|
+
</li>
|
|
148
|
+
</ul>
|
|
149
|
+
</li>
|
|
150
|
+
<li>
|
|
151
|
+
<b>Sort</b>. Sort the childs of an array or object.
|
|
152
|
+
For an array, the values of the childs will be sorted. In case of
|
|
153
|
+
an object, the childs will be sorted by key.
|
|
154
|
+
Arrays and objects can be sorted ascending or descending.
|
|
155
|
+
</li>
|
|
156
|
+
<li>
|
|
157
|
+
<b>Insert</b>. Insert a new field before current field.
|
|
158
|
+
Available types are auto (default), object, array, and string.
|
|
159
|
+
</li>
|
|
160
|
+
<li>
|
|
161
|
+
<b>Append</b>. Insert a new field after current field.
|
|
162
|
+
Available types are the same as the insert action.
|
|
163
|
+
</li>
|
|
164
|
+
<li>
|
|
165
|
+
<b>Duplicate</b>. Duplicate the field including all childs.
|
|
166
|
+
</li>
|
|
167
|
+
<li>
|
|
168
|
+
<b>Remove</b>. Remove the field including all childs.
|
|
169
|
+
</li>
|
|
170
|
+
</ul>
|
|
171
|
+
|
|
172
|
+
<h2 id="shortcut_keys">Shortcut keys</h2>
|
|
173
|
+
<p>
|
|
174
|
+
The JSON Editor supports shortcut keys for all available actions.
|
|
175
|
+
The editor can be used by just a keyboard.
|
|
176
|
+
The following short cut keys are available:
|
|
177
|
+
</p>
|
|
178
|
+
|
|
179
|
+
<table>
|
|
180
|
+
<tr><th>Key</th><th>Description</th></tr>
|
|
181
|
+
<tr><td>Alt+Arrows</td><td>Move the caret up/down/left/right between fields</td></tr>
|
|
182
|
+
<tr><td>Shift+Alt+Arrows</td><td>Move field up/down/left/right</td></tr>
|
|
183
|
+
|
|
184
|
+
<tr><td>Ctrl+D</td><td>Duplicate field</td></tr>
|
|
185
|
+
<tr><td>Ctrl+Del</td><td>Remove field</td></tr>
|
|
186
|
+
<tr><td>Ctrl+Enter</td><td>Open link when on a field containing an url</td></tr>
|
|
187
|
+
<tr><td>Ctrl+Ins</td><td>Insert a new field with type auto</td></tr>
|
|
188
|
+
<tr><td>Ctrl+Shift+Ins</td><td>Append a new field with type auto</td></tr>
|
|
189
|
+
<tr><td>Ctrl+E</td><td>Expand or collapse field</td></tr>
|
|
190
|
+
<tr><td>Alt+End</td><td>Move the caret to the last field</td></tr>
|
|
191
|
+
<tr><td>Ctrl+F</td><td>Find</td></tr>
|
|
192
|
+
<tr><td>F3, Ctrl+G<br></td><td>Find next</td></tr>
|
|
193
|
+
<tr><td>Shift+F3, Ctrl+Shift+G</td><td>Find previous</td></tr>
|
|
194
|
+
<tr><td>Alt+Home</td><td>Move the caret to the first field</td></tr>
|
|
195
|
+
<tr><td>Ctrl+M</td><td>Show actions menu</td></tr>
|
|
196
|
+
<tr><td>Ctrl+Z</td><td>Undo last action</td></tr>
|
|
197
|
+
<tr><td>Ctrl+Shift+Z</td><td>Redo</td></tr>
|
|
198
|
+
</table>
|
|
199
|
+
</div>
|
|
200
|
+
</body>
|
|
201
|
+
</html>
|
|
Binary file
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
div.fileretriever-overlay, div.fileretriever-background {
|
|
3
|
+
position: absolute;
|
|
4
|
+
left: 0;
|
|
5
|
+
top: 0;
|
|
6
|
+
width: 100%;
|
|
7
|
+
height: 100%;
|
|
8
|
+
z-index: 999;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
div.fileretriever-overlay {
|
|
12
|
+
background-color: gray;
|
|
13
|
+
opacity: 0.2;
|
|
14
|
+
filter: alpha(opacity = 20);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
div.fileretriever-border {
|
|
18
|
+
width: 410px;
|
|
19
|
+
margin: 100px auto;
|
|
20
|
+
padding: 20px;
|
|
21
|
+
background-color: white;
|
|
22
|
+
border: 1px solid gray;
|
|
23
|
+
border-radius: 2px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
form.fileretriever-form {
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
div.fileretriever-title {
|
|
30
|
+
font-weight: bold;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
div.fileretriever-contents {
|
|
34
|
+
margin: 30px 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
div.fileretriever-buttons {
|
|
38
|
+
text-align: right;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
input.fileretriever-field[type="file"] {
|
|
42
|
+
width: 400px;
|
|
43
|
+
padding: 3px;
|
|
44
|
+
}
|
|
45
|
+
input.fileretriever-field[type="text"] {
|
|
46
|
+
width: 400px;
|
|
47
|
+
border: 1px solid lightgray;
|
|
48
|
+
border-radius: 2px;
|
|
49
|
+
padding: 3px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
input.fileretriever-submit, input.fileretriever-cancel {
|
|
53
|
+
margin-left: 10px;
|
|
54
|
+
}
|
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file fileretriever.js
|
|
3
|
+
*
|
|
4
|
+
* FileRetriever manages client side loading and saving of files.
|
|
5
|
+
* It requires a server script (fileretriever.php). Loading and saving
|
|
6
|
+
* files is done purely clientside using HTML5 techniques when supported
|
|
7
|
+
* by the browser.
|
|
8
|
+
*
|
|
9
|
+
* Requires ajax.js.
|
|
10
|
+
*
|
|
11
|
+
* Supported browsers: Chrome, Firefox, Opera, Safari,
|
|
12
|
+
* Internet Explorer 8+.
|
|
13
|
+
*
|
|
14
|
+
* Example usage:
|
|
15
|
+
* var retriever = new FileRetriever({
|
|
16
|
+
* 'serverUrl': 'fileretriever.php'
|
|
17
|
+
* });
|
|
18
|
+
* retriever.loadFile(function (err, data) {
|
|
19
|
+
* console.log('file loaded:', data);
|
|
20
|
+
* });
|
|
21
|
+
* retriever.loadUrl(function (err, data) {
|
|
22
|
+
* console.log('url loaded:', data);
|
|
23
|
+
* });
|
|
24
|
+
* retriever.saveFile("some text");
|
|
25
|
+
*
|
|
26
|
+
* @constructor FileRetriever
|
|
27
|
+
* @param {String} options Available options:
|
|
28
|
+
* {string} serverUrl Server side script for
|
|
29
|
+
* handling files, for
|
|
30
|
+
* example "fileretriever.php"
|
|
31
|
+
* {Number} [maxSize] Maximum allowed file size
|
|
32
|
+
* in bytes. (this should
|
|
33
|
+
* be the same as maximum
|
|
34
|
+
* size allowed by the server
|
|
35
|
+
* side script). Default is
|
|
36
|
+
* 1024 * 1024 bytes.
|
|
37
|
+
* {Number} [timeout] Timeout in milliseconds.
|
|
38
|
+
* 30000 ms by default.
|
|
39
|
+
* {Boolean} [html5] Use HTML5 solutions
|
|
40
|
+
* to load/save files when
|
|
41
|
+
* supported by the browser.
|
|
42
|
+
* True by default.
|
|
43
|
+
* {Notify} [notify] A handler for notifications
|
|
44
|
+
* If provided, messages like
|
|
45
|
+
* "loading" and "saving" are created.
|
|
46
|
+
*
|
|
47
|
+
* @license
|
|
48
|
+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
49
|
+
* use this file except in compliance with the License. You may obtain a copy
|
|
50
|
+
* of the License at
|
|
51
|
+
*
|
|
52
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
53
|
+
*
|
|
54
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
55
|
+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
56
|
+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
57
|
+
* License for the specific language governing permissions and limitations under
|
|
58
|
+
* the License.
|
|
59
|
+
*
|
|
60
|
+
* Copyright (c) 2013 Jos de Jong, http://jsoneditoronline.org
|
|
61
|
+
*
|
|
62
|
+
* @author Jos de Jong, <wjosdejong@gmail.com>
|
|
63
|
+
* @date 2013-01-01
|
|
64
|
+
*/
|
|
65
|
+
var FileRetriever = function (options) {
|
|
66
|
+
// set options and variables
|
|
67
|
+
options = options || {};
|
|
68
|
+
this.options = {
|
|
69
|
+
maxSize: ((options.maxSize != undefined) ? options.maxSize : 1024 * 1024),
|
|
70
|
+
html5: ((options.html5 != undefined) ? options.html5 : true)
|
|
71
|
+
};
|
|
72
|
+
this.timeout = Number(options.timeout) || 30000;
|
|
73
|
+
this.headers = {'Accept': 'application/json'}; // headers for ajax requests
|
|
74
|
+
this.scriptUrl = options.scriptUrl || 'fileretriever.php';
|
|
75
|
+
this.notify = options.notify || undefined;
|
|
76
|
+
this.defaultFilename = 'document.json';
|
|
77
|
+
this.dom = {};
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* make an HTML DOM element invisible
|
|
82
|
+
* @param {Element} elem
|
|
83
|
+
* @private
|
|
84
|
+
*/
|
|
85
|
+
FileRetriever.prototype._hide = function (elem) {
|
|
86
|
+
elem.style.visibility = 'hidden';
|
|
87
|
+
elem.style.position = 'absolute';
|
|
88
|
+
elem.style.left = '-1000px';
|
|
89
|
+
elem.style.top = '-1000px';
|
|
90
|
+
elem.style.width = '0';
|
|
91
|
+
elem.style.height = '0';
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Delete all HTML DOM elements created by the FileRetriever.
|
|
96
|
+
* The FileRetriever cannot be used after its DOM elements are deleted.
|
|
97
|
+
*/
|
|
98
|
+
FileRetriever.prototype.remove = function () {
|
|
99
|
+
var dom = this.dom;
|
|
100
|
+
for (var prop in dom) {
|
|
101
|
+
if (dom.hasOwnProperty(prop)) {
|
|
102
|
+
var elem = dom[prop];
|
|
103
|
+
if (elem.parentNode) {
|
|
104
|
+
elem.parentNode.removeChild(elem);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
this.dom = {};
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* get a filename from a path or url.
|
|
113
|
+
* For example "http://site.com/files/example.json" will return "example.json"
|
|
114
|
+
* @param {String} path A filename, path, or url
|
|
115
|
+
* @return {String} filename
|
|
116
|
+
* @private
|
|
117
|
+
*/
|
|
118
|
+
FileRetriever.prototype._getFilename = function (path) {
|
|
119
|
+
// http://stackoverflow.com/a/423385/1262753
|
|
120
|
+
return path ? path.replace(/^.*[\\\/]/, '') : '';
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Set the last url
|
|
125
|
+
* @param {String} url
|
|
126
|
+
*/
|
|
127
|
+
FileRetriever.prototype.setUrl = function (url) {
|
|
128
|
+
this.url = url;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get last filename
|
|
133
|
+
* @return {String} filename
|
|
134
|
+
*/
|
|
135
|
+
FileRetriever.prototype.getFilename = function () {
|
|
136
|
+
return this.defaultFilename;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get the last url
|
|
141
|
+
* @return {String | undefined} url
|
|
142
|
+
*/
|
|
143
|
+
FileRetriever.prototype.getUrl = function () {
|
|
144
|
+
return this.url;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Load a url
|
|
149
|
+
* @param {String} url The url to be retrieved
|
|
150
|
+
* @param {function} callback Callback method, called with parameters:
|
|
151
|
+
* {Error} error
|
|
152
|
+
* {string} data
|
|
153
|
+
*/
|
|
154
|
+
FileRetriever.prototype.loadUrl = function (url, callback) {
|
|
155
|
+
// set current filename (will be used when saving a file again)
|
|
156
|
+
this.setUrl(url);
|
|
157
|
+
|
|
158
|
+
// loading notification
|
|
159
|
+
var loading = undefined;
|
|
160
|
+
if (this.notify) {
|
|
161
|
+
loading = this.notify.showNotification('loading url...');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// method to ensure the callback is only executed once
|
|
165
|
+
var me = this;
|
|
166
|
+
var callbackOnce = function (error, data) {
|
|
167
|
+
if (callback) {
|
|
168
|
+
callback(error, data);
|
|
169
|
+
callback = undefined;
|
|
170
|
+
}
|
|
171
|
+
if (me.notify && loading) {
|
|
172
|
+
me.notify.removeMessage(loading);
|
|
173
|
+
loading = undefined;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// try to fetch to the url directly (may result in a cross-domain error)
|
|
178
|
+
var scriptUrl = this.scriptUrl;
|
|
179
|
+
ajax.get(url, me.headers, function(data, status) {
|
|
180
|
+
if (status == 200) {
|
|
181
|
+
// success. great. no cross-domain error
|
|
182
|
+
callbackOnce(null, data);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
// cross-domain error (or other). retrieve the url via the server
|
|
186
|
+
var indirectUrl = scriptUrl + '?url=' + encodeURIComponent(url);
|
|
187
|
+
var err;
|
|
188
|
+
ajax.get(indirectUrl, me.headers, function(data, status) {
|
|
189
|
+
if (status == 200) {
|
|
190
|
+
callbackOnce(null, data);
|
|
191
|
+
}
|
|
192
|
+
else if (status == 404) {
|
|
193
|
+
console.log('Error: url "' + url + '" not found', status, data);
|
|
194
|
+
err = new Error('Error: url "' + url + '" not found');
|
|
195
|
+
callbackOnce(err, null);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
console.log('Error: failed to load url "' + url + '"', status, data);
|
|
199
|
+
err = new Error('Error: failed to load url "' + url + '"');
|
|
200
|
+
callbackOnce(err, null);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// safety mechanism: callback after a timeout
|
|
207
|
+
setTimeout(function () {
|
|
208
|
+
callbackOnce(new Error('Error loading url (time out)'));
|
|
209
|
+
}, this.timeout);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Load a file from disk.
|
|
214
|
+
* A file explorer will be opened to select a file and press ok.
|
|
215
|
+
* In case of Internet Explorer, an upload form will be shown where the
|
|
216
|
+
* user has to select a file via a file explorer after that click load.
|
|
217
|
+
* @param {function} callback Callback method, called with parameters:
|
|
218
|
+
* {Error} error
|
|
219
|
+
* {string} data
|
|
220
|
+
*/
|
|
221
|
+
FileRetriever.prototype.loadFile = function (callback) {
|
|
222
|
+
// loading notification
|
|
223
|
+
var loading = undefined;
|
|
224
|
+
var me = this;
|
|
225
|
+
|
|
226
|
+
var startLoading = function () {
|
|
227
|
+
if (me.notify && !loading) {
|
|
228
|
+
loading = me.notify.showNotification('loading file...');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// safety mechanism: callback after a timeout
|
|
232
|
+
setTimeout(function () {
|
|
233
|
+
callbackOnce(new Error('Error loading url (time out)'));
|
|
234
|
+
}, me.timeout);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// method to ensure the callback is only executed once
|
|
238
|
+
var callbackOnce = function (error, data) {
|
|
239
|
+
if (callback) {
|
|
240
|
+
callback(error, data);
|
|
241
|
+
callback = undefined;
|
|
242
|
+
}
|
|
243
|
+
if (me.notify && loading) {
|
|
244
|
+
me.notify.removeMessage(loading);
|
|
245
|
+
loading = undefined;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// create an iframe for uploading files
|
|
250
|
+
// the iframe must have an unique name, allowing multiple
|
|
251
|
+
// FileRetrievers. The name is needed as target for the uploadForm
|
|
252
|
+
var iframeName = 'fileretriever-upload-' + Math.round(Math.random() * 1E15);
|
|
253
|
+
var iframe = document.createElement('iframe');
|
|
254
|
+
iframe.name = iframeName;
|
|
255
|
+
me._hide(iframe);
|
|
256
|
+
iframe.onload = function () {
|
|
257
|
+
// when a downloaded file is retrieved, send a callback with
|
|
258
|
+
// the retrieved data
|
|
259
|
+
var id = iframe.contentWindow.document.body.innerHTML;
|
|
260
|
+
if (id) {
|
|
261
|
+
var url = me.scriptUrl + '?id=' + id + '&filename=' + me.getFilename();
|
|
262
|
+
ajax.get(url, me.headers, function (data, status) {
|
|
263
|
+
if (status == 200) {
|
|
264
|
+
callbackOnce(null, data);
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
var err = new Error('Error loading file ' + me.getFilename());
|
|
268
|
+
callbackOnce(err, null);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
document.body.appendChild(iframe);
|
|
274
|
+
|
|
275
|
+
var isIE = (navigator.appName == 'Microsoft Internet Explorer');
|
|
276
|
+
if (!isIE) {
|
|
277
|
+
// create a hidden form to select a file
|
|
278
|
+
var domForm = document.createElement('form');
|
|
279
|
+
domForm.action = this.scriptUrl;
|
|
280
|
+
domForm.method = 'POST';
|
|
281
|
+
domForm.enctype = 'multipart/form-data';
|
|
282
|
+
domForm.target = iframeName;
|
|
283
|
+
this._hide(domForm);
|
|
284
|
+
var domFile = document.createElement('input');
|
|
285
|
+
domFile.type = 'file';
|
|
286
|
+
domFile.name = 'file';
|
|
287
|
+
domFile.onchange = function () {
|
|
288
|
+
startLoading();
|
|
289
|
+
|
|
290
|
+
// there is a file selected
|
|
291
|
+
setTimeout(function () { // Timeout needed for IE
|
|
292
|
+
var filename = domFile.value;
|
|
293
|
+
if (filename.length) {
|
|
294
|
+
if (me.options.html5 && window.File && window.FileReader) {
|
|
295
|
+
// load file via HTML5 FileReader (no size limits)
|
|
296
|
+
var file = domFile.files[0];
|
|
297
|
+
var reader = new FileReader();
|
|
298
|
+
reader.onload = function(event) {
|
|
299
|
+
var data = event.target.result;
|
|
300
|
+
callbackOnce(null, data);
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// Read in the image file as a data URL.
|
|
304
|
+
reader.readAsText(file);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
// load by uploading to server
|
|
308
|
+
// TODO: how to check the file size? (on older browsers)
|
|
309
|
+
//console.log('submitting...');
|
|
310
|
+
|
|
311
|
+
domForm.submit();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
// cancel
|
|
316
|
+
callbackOnce(null, null);
|
|
317
|
+
}
|
|
318
|
+
}, 0);
|
|
319
|
+
};
|
|
320
|
+
domForm.appendChild(domFile);
|
|
321
|
+
document.body.appendChild(domForm);
|
|
322
|
+
|
|
323
|
+
// activate file selection (the click is done after a timeout,
|
|
324
|
+
// as in Opera and Safari, the form is not yet rendered)
|
|
325
|
+
setTimeout(function () {
|
|
326
|
+
domFile.click();
|
|
327
|
+
}, 0);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
// create a visual form and submit manually (for IE)
|
|
331
|
+
this.prompt({
|
|
332
|
+
title: 'Open file',
|
|
333
|
+
titleSubmit: 'Open',
|
|
334
|
+
inputType: 'file',
|
|
335
|
+
inputName: 'file',
|
|
336
|
+
formAction: this.scriptUrl,
|
|
337
|
+
formMethod: 'POST',
|
|
338
|
+
formTarget: iframeName,
|
|
339
|
+
callback: function (value) {
|
|
340
|
+
if (value) {
|
|
341
|
+
startLoading();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
// TODO: handle a cancel
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Show a dialog to select and load an url.
|
|
351
|
+
* @param {function} callback Callback method, called with parameters:
|
|
352
|
+
* {Error} error
|
|
353
|
+
* {String} data
|
|
354
|
+
*/
|
|
355
|
+
FileRetriever.prototype.loadUrlDialog = function (callback) {
|
|
356
|
+
var me = this;
|
|
357
|
+
this.prompt({
|
|
358
|
+
title: 'Open url',
|
|
359
|
+
titleSubmit: 'Open',
|
|
360
|
+
inputType: 'text',
|
|
361
|
+
inputName: 'url',
|
|
362
|
+
inputDefault: this.getUrl(),
|
|
363
|
+
callback: function (url) {
|
|
364
|
+
if (url) {
|
|
365
|
+
me.loadUrl(url, callback);
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
// cancel
|
|
369
|
+
callback();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Show a prompt.
|
|
377
|
+
* The propmt can either:
|
|
378
|
+
* - Post a form when formAction, and formMethod are provided.
|
|
379
|
+
* Will call callback on submit.
|
|
380
|
+
* - Call the callback method "callback" with the entered value as parameter.
|
|
381
|
+
* This happens when a callback parameter is provided.
|
|
382
|
+
* @param {Object} params Available parameters:
|
|
383
|
+
* {String} title
|
|
384
|
+
* {String} titleSubmit
|
|
385
|
+
* {String} titleCancel
|
|
386
|
+
* {String} inputType
|
|
387
|
+
* {String} inputName
|
|
388
|
+
* {String} inputDefault
|
|
389
|
+
* {String} formTarget
|
|
390
|
+
* {String} formAction
|
|
391
|
+
* {String} formMethod
|
|
392
|
+
* {function} callback
|
|
393
|
+
*/
|
|
394
|
+
FileRetriever.prototype.prompt = function (params) {
|
|
395
|
+
var removeDialog = function () {
|
|
396
|
+
// remove the form
|
|
397
|
+
if (background.parentNode) {
|
|
398
|
+
background.parentNode.removeChild(background);
|
|
399
|
+
}
|
|
400
|
+
if (overlay.parentNode) {
|
|
401
|
+
overlay.parentNode.removeChild(overlay);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
jsoneditor.util.removeEventListener(document, 'keydown', onKeyDown);
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
var onCancel = function () {
|
|
408
|
+
removeDialog();
|
|
409
|
+
if(params.callback) {
|
|
410
|
+
params.callback(null);
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
var onKeyDown = jsoneditor.util.addEventListener(document, 'keydown', function (event) {
|
|
415
|
+
event = event || window.event;
|
|
416
|
+
var keynum = event.which || event.keyCode;
|
|
417
|
+
if (keynum == 27) { // ESC
|
|
418
|
+
onCancel();
|
|
419
|
+
jsoneditor.util.preventDefault(event);
|
|
420
|
+
jsoneditor.util.stopPropagation(event);
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
var overlay = document.createElement('div');
|
|
425
|
+
overlay.className = 'fileretriever-overlay';
|
|
426
|
+
document.body.appendChild(overlay);
|
|
427
|
+
|
|
428
|
+
var form = document.createElement('form');
|
|
429
|
+
form.className = 'fileretriever-form';
|
|
430
|
+
form.target = params.formTarget || '';
|
|
431
|
+
form.action = params.formAction || '';
|
|
432
|
+
form.method = params.formMethod || 'POST';
|
|
433
|
+
form.enctype = 'multipart/form-data';
|
|
434
|
+
form.encoding = 'multipart/form-data'; // needed for IE8 and older
|
|
435
|
+
form.onsubmit = function () {
|
|
436
|
+
if (field.value) {
|
|
437
|
+
setTimeout(function () {
|
|
438
|
+
// remove after the submit has taken place!
|
|
439
|
+
removeDialog();
|
|
440
|
+
}, 0);
|
|
441
|
+
if (params.callback) {
|
|
442
|
+
params.callback(field.value);
|
|
443
|
+
}
|
|
444
|
+
return (params.formAction != undefined && params.formMethod != undefined);
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
alert('Enter a ' + params.inputName + ' first...');
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
var title = document.createElement('div');
|
|
453
|
+
title.className = 'fileretriever-title';
|
|
454
|
+
title.appendChild(document.createTextNode(params.title || 'Dialog'));
|
|
455
|
+
form.appendChild(title);
|
|
456
|
+
|
|
457
|
+
var field = document.createElement('input');
|
|
458
|
+
field.className = 'fileretriever-field';
|
|
459
|
+
field.type = params.inputType || 'text';
|
|
460
|
+
field.name = params.inputName || 'text';
|
|
461
|
+
field.value = params.inputDefault || '';
|
|
462
|
+
|
|
463
|
+
var contents = document.createElement('div');
|
|
464
|
+
contents.className = 'fileretriever-contents';
|
|
465
|
+
contents.appendChild(field);
|
|
466
|
+
form.appendChild(contents);
|
|
467
|
+
|
|
468
|
+
var cancel = document.createElement('input');
|
|
469
|
+
cancel.className = 'fileretriever-cancel';
|
|
470
|
+
cancel.type = 'button';
|
|
471
|
+
cancel.value = params.titleCancel || 'Cancel';
|
|
472
|
+
cancel.onclick = onCancel;
|
|
473
|
+
|
|
474
|
+
var submit = document.createElement('input');
|
|
475
|
+
submit.className = 'fileretriever-submit';
|
|
476
|
+
submit.type = 'submit';
|
|
477
|
+
submit.value = params.titleSubmit || 'Ok';
|
|
478
|
+
|
|
479
|
+
var buttons = document.createElement('div');
|
|
480
|
+
buttons.className = 'fileretriever-buttons';
|
|
481
|
+
buttons.appendChild(cancel);
|
|
482
|
+
buttons.appendChild(submit);
|
|
483
|
+
form.appendChild(buttons);
|
|
484
|
+
|
|
485
|
+
var border = document.createElement('div');
|
|
486
|
+
border.className = 'fileretriever-border';
|
|
487
|
+
border.appendChild(form);
|
|
488
|
+
|
|
489
|
+
var background = document.createElement('div');
|
|
490
|
+
background.className = 'fileretriever-background';
|
|
491
|
+
background.appendChild(border);
|
|
492
|
+
document.body.appendChild(background);
|
|
493
|
+
|
|
494
|
+
field.focus();
|
|
495
|
+
field.select();
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Save data to disk
|
|
500
|
+
* @param {String} data
|
|
501
|
+
* @param {function} [callback] Callback when the file is saved, called
|
|
502
|
+
* with parameter:
|
|
503
|
+
* {Error} error
|
|
504
|
+
*/
|
|
505
|
+
FileRetriever.prototype.saveFile = function (data, callback) {
|
|
506
|
+
// saving notification
|
|
507
|
+
var saving = undefined;
|
|
508
|
+
if (this.notify) {
|
|
509
|
+
saving = this.notify.showNotification('saving file...');
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// method to ensure the callback is only executed once
|
|
513
|
+
var me = this;
|
|
514
|
+
var callbackOnce = function (error) {
|
|
515
|
+
if (callback) {
|
|
516
|
+
callback(error);
|
|
517
|
+
callback = undefined;
|
|
518
|
+
}
|
|
519
|
+
if (me.notify && saving) {
|
|
520
|
+
me.notify.removeMessage(saving);
|
|
521
|
+
saving = undefined;
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// create an anchor to save files to disk (if supported by the browser)
|
|
526
|
+
var a = document.createElement('a');
|
|
527
|
+
if (this.options.html5 && a.download != undefined) {
|
|
528
|
+
// save file directly using a data URL
|
|
529
|
+
a.href = 'data:application/json;charset=utf-8,' + encodeURIComponent(data);
|
|
530
|
+
a.download = this.getFilename();
|
|
531
|
+
a.click();
|
|
532
|
+
callbackOnce();
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
// save file by uploading it to the server and then downloading
|
|
536
|
+
// it via an iframe
|
|
537
|
+
if (data.length < this.options.maxSize) {
|
|
538
|
+
ajax.post(me.scriptUrl, data, me.headers, function(id, status) {
|
|
539
|
+
if (status == 200) {
|
|
540
|
+
var iframe = document.createElement('iframe');
|
|
541
|
+
iframe.src = me.scriptUrl + '?id=' + id + '&filename=' + me.getFilename();
|
|
542
|
+
me._hide(iframe);
|
|
543
|
+
document.body.appendChild(iframe);
|
|
544
|
+
/* TODO: send callback after the iframe is loaded. Problem: iframe.onload does not work on IE
|
|
545
|
+
iframe.onload = function () {
|
|
546
|
+
callbackOnce();
|
|
547
|
+
};
|
|
548
|
+
//*/
|
|
549
|
+
callbackOnce();
|
|
550
|
+
// TODO: cleanup the iframe after the file is saved. Problem: we cannot know when the save dialog is closed.
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
callbackOnce(new Error('Error saving file'));
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
else {
|
|
558
|
+
callbackOnce(new Error('Maximum allowed file size exceeded (' +
|
|
559
|
+
this.options.maxSize + ' bytes)'));
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// safety mechanism: callback after a timeout
|
|
564
|
+
setTimeout(function () {
|
|
565
|
+
callbackOnce(new Error('Error saving file (time out)'));
|
|
566
|
+
}, this.timeout);
|
|
567
|
+
};
|