el-finder 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +11 -0
- data/LICENSE +19 -0
- data/README.md +66 -0
- data/Rakefile +21 -0
- data/assets/api.js +273 -0
- data/assets/bootstrap-contextmenu.js +147 -0
- data/assets/bootstrap/css/bootstrap-responsive.min.css +9 -0
- data/assets/bootstrap/css/bootstrap.min.css +9 -0
- data/assets/bootstrap/img/glyphicons-halflings-white.png +0 -0
- data/assets/bootstrap/img/glyphicons-halflings.png +0 -0
- data/assets/bootstrap/js/bootstrap.min.js +6 -0
- data/assets/colorbox/colorbox.css +44 -0
- data/assets/colorbox/images/controls.png +0 -0
- data/assets/colorbox/images/loading.gif +0 -0
- data/assets/colorbox/jquery.colorbox-min.js +6 -0
- data/assets/fileupload.js +31 -0
- data/assets/icons/Oxygen/preview.png +0 -0
- data/assets/icons/Oxygen/video.png +0 -0
- data/assets/icons/delete.png +0 -0
- data/assets/icons/file.png +0 -0
- data/assets/icons/folder.png +0 -0
- data/assets/jQuery-File-Upload/css/jquery.fileupload-ui-noscript.css +27 -0
- data/assets/jQuery-File-Upload/css/jquery.fileupload-ui.css +83 -0
- data/assets/jQuery-File-Upload/css/style.css +15 -0
- data/assets/jQuery-File-Upload/img/loading.gif +0 -0
- data/assets/jQuery-File-Upload/img/progressbar.gif +0 -0
- data/assets/jQuery-File-Upload/js/cors/jquery.postmessage-transport.js +117 -0
- data/assets/jQuery-File-Upload/js/cors/jquery.xdr-transport.js +87 -0
- data/assets/jQuery-File-Upload/js/jquery.fileupload-fp.js +227 -0
- data/assets/jQuery-File-Upload/js/jquery.fileupload-ui.js +807 -0
- data/assets/jQuery-File-Upload/js/jquery.fileupload.js +1201 -0
- data/assets/jQuery-File-Upload/js/jquery.iframe-transport.js +185 -0
- data/assets/jQuery-File-Upload/js/main.js +83 -0
- data/assets/jQuery-File-Upload/js/vendor/jquery.ui.widget.js +530 -0
- data/assets/jquery-ui/css/ui-lightness/images/animated-overlay.gif +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
- data/assets/jquery-ui/css/ui-lightness/jquery-ui.css +5 -0
- data/assets/jquery-ui/js/jquery-ui.js +6 -0
- data/assets/jquery.js +5 -0
- data/assets/noty/jquery.noty.js +520 -0
- data/assets/noty/layouts/top.js +34 -0
- data/assets/noty/layouts/topRight.js +43 -0
- data/assets/noty/themes/default.js +156 -0
- data/assets/select2-bootstrap.css +86 -0
- data/assets/select2/select2-spinner.gif +0 -0
- data/assets/select2/select2.css +615 -0
- data/assets/select2/select2.min.js +22 -0
- data/assets/select2/select2.png +0 -0
- data/assets/select2/select2x2.png +0 -0
- data/assets/ui.css +43 -0
- data/assets/ui.js +34 -0
- data/assets/xhr.js +4 -0
- data/el-finder.gemspec +25 -0
- data/examples/basic/Gemfile +15 -0
- data/examples/basic/app.rb +21 -0
- data/lib/el-finder.rb +6 -0
- data/lib/el-finder/el-finder.rb +200 -0
- data/lib/el-finder/templates/column.slim +64 -0
- data/lib/el-finder/templates/index.slim +235 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
OTJkMzg2ZGMzMzFmNWI2YTQ1ZDE5NjMyYmNmODQ1OTUyMzU2MzQ5Mg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MjhiOTFjZDNiODdiZjEyMWJkNzVlMTkzMGQzNmJhYzAzZDM4NGI5Yg==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NjYzNjNkYzdkOGRkY2QyNjM5MzYzNzgyNmY0N2U1YTRkNGY2Njk5MzVlNjY2
|
10
|
+
MDI4MmMyNDZhNGJjMzM2NmM2ZmRhM2VjNTY2NDJiNjA2OGU4YTRmYzczMWI4
|
11
|
+
NTZiMTYzN2Y2YmFiNDY5NGM5ZGExZDQzZjEzM2EzNzg3ZjkyMDk=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MGRjN2ZkOGY5NWQwN2VjMjk3ZGRmMjg2ZDAwYzFkZmY1YTkxMGEzM2JhZDZm
|
14
|
+
MWVlYjdkYjYzODMyM2MwYTIyNWY3YjUzZTdjYmQyYWFhY2MzYjI4ODk0MGFl
|
15
|
+
NjA3NDQwNjgwMzdkMWE2NWJiYTAxMGE1OGNjNGIzYTYwM2VlZjA=
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
Copyright (c) 2013 Silviu Rusu <slivuz@gmail.com>
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
of this software and associated documentation files (the "Software"),
|
6
|
+
to deal in the Software without restriction, including without limitation
|
7
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
8
|
+
and/or sell copies of the Software, and to permit persons to whom the Software
|
9
|
+
is furnished to do so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
12
|
+
copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
## [Espresso Lungo](https://github.com/espresso/espresso-lungo) / Finder
|
3
|
+
|
4
|
+
### Web-based File Manager for [Espresso](https://github.com/espresso/espresso) applications
|
5
|
+
|
6
|
+
## Install
|
7
|
+
|
8
|
+
```bash
|
9
|
+
$ gem install el-finder
|
10
|
+
```
|
11
|
+
|
12
|
+
or add `gem 'el-finder'` to your `Gemfile`
|
13
|
+
|
14
|
+
## Load
|
15
|
+
|
16
|
+
Ignore this if `el-finder` added to `Gemfile` and `Bundler.require` used.
|
17
|
+
|
18
|
+
Otherwise you'll have to load `el-finder` gem explicitly.
|
19
|
+
|
20
|
+
Just add `require 'el-finder'` at the top of your app.
|
21
|
+
|
22
|
+
## Use
|
23
|
+
|
24
|
+
First of all include `EL::Finder` into controllers you need a file manager.
|
25
|
+
|
26
|
+
Then use `finder` into your views passing path to managed directory via first argument:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
finder 'path/to/files'
|
30
|
+
```
|
31
|
+
|
32
|
+
## Editors
|
33
|
+
|
34
|
+
By default, `el-finder` will use a textarea to edit files.
|
35
|
+
|
36
|
+
To use `Ace` editor, install and load `el-ace` gem.
|
37
|
+
|
38
|
+
See [github.com/espresso/el-ace](https://github.com/espresso/el-ace) for instructions.
|
39
|
+
|
40
|
+
Then simply set editor via `:editor` option:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
finder 'path/to/files', editor: :ace
|
44
|
+
```
|
45
|
+
|
46
|
+
Same for `CKEditor`, see [github.com/espresso/el-ckeditor](https://github.com/espresso/el-ckeditor) for instructions.
|
47
|
+
|
48
|
+
Feel free to pass any editor options:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
finder 'path/to/files', editor: :ace, mode: 'html'
|
52
|
+
```
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
finder 'path/to/files', editor: :ckeditor, lang: 'fr'
|
56
|
+
```
|
57
|
+
|
58
|
+
<hr>
|
59
|
+
|
60
|
+
**Issues/Bugs:** [github.com/espresso/espresso-lungo/issues](https://github.com/espresso/espresso-lungo/issues)
|
61
|
+
|
62
|
+
**Mailing List:** [groups.google.com/.../espresso-framework](https://groups.google.com/forum/?fromgroups#!forum/espresso-framework)
|
63
|
+
|
64
|
+
**IRC channel:** #espressorb on irc.freenode.net
|
65
|
+
|
66
|
+
**Author** - [Silviu Rusu](https://github.com/slivu)
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.require
|
3
|
+
|
4
|
+
namespace :assets do
|
5
|
+
desc 'Update css files to correctly load background images'
|
6
|
+
task :css do
|
7
|
+
puts "Looking for css files containing background(-image)?:url ..."
|
8
|
+
src = /background(\-image)?[\s+]?\:(.*?)url\((\W+)?([^\.]*)\.(\w+)(\W+)?\)/
|
9
|
+
dst = 'background\1:\2url(\3\4.\5%s\6)' % EL::FinderHelpers::ASSETS_EXT
|
10
|
+
Dir[File.expand_path('../assets/**/*.css', __FILE__)].each do |file|
|
11
|
+
css = File.read(file)
|
12
|
+
if css =~ src
|
13
|
+
puts "Updating #{file}"
|
14
|
+
File.open(file, 'w') {|f| f << css.gsub(src, dst)}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
puts "Done"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Bundler::GemHelper.install_tasks
|
data/assets/api.js
ADDED
@@ -0,0 +1,273 @@
|
|
1
|
+
ELFinderAPI = function(baseURL, params) {
|
2
|
+
|
3
|
+
var baseURL = (baseURL||'').match(/\/$/) ? baseURL : baseURL + '/',
|
4
|
+
params = (params || {});
|
5
|
+
|
6
|
+
this.delete = function(type, path, name) {
|
7
|
+
var path = guess_path(path),
|
8
|
+
name = guess_name(name);
|
9
|
+
|
10
|
+
var warning = "You are going to delete \"" + name + "\" " + type + ". ";
|
11
|
+
warning += "This action can NOT be undone! Continue?";
|
12
|
+
if (!confirm(warning)) return false;
|
13
|
+
|
14
|
+
$.ajax({
|
15
|
+
type: 'DELETE',
|
16
|
+
url: baseURL,
|
17
|
+
data: {
|
18
|
+
action: type,
|
19
|
+
path: path,
|
20
|
+
name: name
|
21
|
+
},
|
22
|
+
complete: function(xhr, txtResponse) {
|
23
|
+
if(txtResponse == 'success') {
|
24
|
+
if (type == 'file') {
|
25
|
+
redirect({location: params.location.URL});
|
26
|
+
} else {
|
27
|
+
if(params.location.URL.match(new RegExp("^" + path + "/" + name)) ||
|
28
|
+
(path == params.location.path && name == params.location.name))
|
29
|
+
redirect({location: path});
|
30
|
+
else
|
31
|
+
reload();
|
32
|
+
}
|
33
|
+
} else displayError(xhr);
|
34
|
+
}
|
35
|
+
});
|
36
|
+
}
|
37
|
+
|
38
|
+
this.download = function(path, name) {
|
39
|
+
var url = baseURL + build_query_string({
|
40
|
+
action: 'download',
|
41
|
+
path: guess_path(path),
|
42
|
+
name: guess_name(name)
|
43
|
+
});
|
44
|
+
$('#ELFinderDownloadIframe').attr('src', url);
|
45
|
+
}
|
46
|
+
|
47
|
+
this.rename = function(selector) {
|
48
|
+
if (selector) {
|
49
|
+
var element = $(selector);
|
50
|
+
var path = element.attr('data-path'),
|
51
|
+
name = element.attr('data-name'),
|
52
|
+
new_name = element.val();
|
53
|
+
} else
|
54
|
+
var path, name, new_name = $('#input-rename').val();
|
55
|
+
|
56
|
+
$.ajax({
|
57
|
+
type: 'POST',
|
58
|
+
url: baseURL,
|
59
|
+
data: {
|
60
|
+
action: 'rename',
|
61
|
+
path: guess_path(path),
|
62
|
+
name: guess_name(name),
|
63
|
+
new_name: new_name
|
64
|
+
},
|
65
|
+
complete: function(xhr, txtResponse) {
|
66
|
+
if(txtResponse == 'success') {
|
67
|
+
if (params.file) {
|
68
|
+
redirect({location: params.location.URL, file: [params.file.path, new_name].join('/')});
|
69
|
+
} else {
|
70
|
+
if (selector) window.location.reload();
|
71
|
+
else redirect({location: [params.location.path, new_name].join('/')});
|
72
|
+
}
|
73
|
+
} else displayError(xhr);
|
74
|
+
}
|
75
|
+
});
|
76
|
+
}
|
77
|
+
|
78
|
+
this.save_file = function() {
|
79
|
+
$.ajax({
|
80
|
+
type: 'POST',
|
81
|
+
url: baseURL,
|
82
|
+
data: {
|
83
|
+
action: 'update',
|
84
|
+
path: params.file.path,
|
85
|
+
name: params.file.name,
|
86
|
+
content: $('#ELFinderEditor').val()
|
87
|
+
},
|
88
|
+
complete: function(xhr, txtResponse) {
|
89
|
+
if(txtResponse == 'success')
|
90
|
+
ELFinder.alert(params.file.name + ' Successfully Updated');
|
91
|
+
else displayError(xhr);
|
92
|
+
}
|
93
|
+
});
|
94
|
+
}
|
95
|
+
|
96
|
+
this.create = function(type) {
|
97
|
+
var new_name = $('#input-create').val();
|
98
|
+
$.ajax({
|
99
|
+
type: 'POST',
|
100
|
+
url: baseURL,
|
101
|
+
data: {
|
102
|
+
action: type,
|
103
|
+
path: params.location.path,
|
104
|
+
name: params.location.name,
|
105
|
+
new_name: new_name
|
106
|
+
},
|
107
|
+
complete: function(xhr, txtResponse) {
|
108
|
+
if(txtResponse == 'success') reload();
|
109
|
+
else displayError(xhr);
|
110
|
+
}
|
111
|
+
});
|
112
|
+
}
|
113
|
+
|
114
|
+
this.move = function(source, target) {
|
115
|
+
$.ajax({
|
116
|
+
type: 'POST',
|
117
|
+
url: baseURL,
|
118
|
+
data: {
|
119
|
+
action: 'move',
|
120
|
+
source: source,
|
121
|
+
target: target
|
122
|
+
},
|
123
|
+
complete: function(xhr, txtResponse) {
|
124
|
+
if(txtResponse == 'success') {
|
125
|
+
var actual = params.location.path + '/' + params.location.name;
|
126
|
+
if(actual.match(new RegExp("^" + source)))
|
127
|
+
redirect({location: target});
|
128
|
+
else
|
129
|
+
reload();
|
130
|
+
} else displayError(xhr);
|
131
|
+
}
|
132
|
+
});
|
133
|
+
}
|
134
|
+
|
135
|
+
this.loadScript = function(url, checkLoaded, callback) {
|
136
|
+
if (checkLoaded && checkLoaded())
|
137
|
+
return callback && callback();
|
138
|
+
|
139
|
+
var script = document.createElement("script");
|
140
|
+
script.type = "text/javascript";
|
141
|
+
script.async = true;
|
142
|
+
|
143
|
+
if (script.readyState) {
|
144
|
+
script.onreadystatechange = function () {
|
145
|
+
if (script.readyState == "loaded" || script.readyState == "complete") {
|
146
|
+
script.onreadystatechange = null;
|
147
|
+
callback && callback();
|
148
|
+
}
|
149
|
+
}
|
150
|
+
} else {
|
151
|
+
script.onload = function() {
|
152
|
+
callback && callback();
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
script.src = url;
|
157
|
+
document.head.appendChild(script);
|
158
|
+
}
|
159
|
+
|
160
|
+
this.loadStylesheet = function(url, checkForClasses) {
|
161
|
+
if (checkForClasses) {
|
162
|
+
if (typeof checkForClasses == "string")
|
163
|
+
checkForClasses = checkForClasses.split(/\s+/g);
|
164
|
+
var hasClass = false, styleSheets = document.styleSheets;
|
165
|
+
for (var x = 0; x < styleSheets.length; x++) {
|
166
|
+
var sheetClasses = styleSheets[x].rules || document.styleSheets[x].cssRules;
|
167
|
+
for (var y = 0; y < sheetClasses.length; y++) {
|
168
|
+
if ( checkForClasses.indexOf(sheetClasses[y].selectorText) > -1 ) {
|
169
|
+
hasClass = true; break;
|
170
|
+
}
|
171
|
+
}
|
172
|
+
}
|
173
|
+
if (hasClass) return true;
|
174
|
+
}
|
175
|
+
var link = document.createElement("link");
|
176
|
+
link.media = "all";
|
177
|
+
link.type = "text/css"
|
178
|
+
link.rel = "stylesheet"
|
179
|
+
link.async = true;
|
180
|
+
link.href = url;
|
181
|
+
document.head.appendChild(link);
|
182
|
+
}
|
183
|
+
|
184
|
+
this.alert = function(msg, timeout) {
|
185
|
+
noty({
|
186
|
+
text: msg,
|
187
|
+
type: 'information',
|
188
|
+
layout: 'topRight',
|
189
|
+
timeout: timeout || 2000
|
190
|
+
});
|
191
|
+
}
|
192
|
+
|
193
|
+
this.sticky_alert = function(msg) {
|
194
|
+
noty({
|
195
|
+
text: msg,
|
196
|
+
type: 'information',
|
197
|
+
layout: 'topRight'
|
198
|
+
});
|
199
|
+
}
|
200
|
+
|
201
|
+
this.warn = function(msg, timeout) {
|
202
|
+
noty({
|
203
|
+
text: msg,
|
204
|
+
type: 'warning',
|
205
|
+
layout: 'topRight',
|
206
|
+
timeout: timeout || 3000
|
207
|
+
});
|
208
|
+
}
|
209
|
+
|
210
|
+
this.sticky_warn = function(msg) {
|
211
|
+
noty({
|
212
|
+
text: msg,
|
213
|
+
type: 'warning',
|
214
|
+
layout: 'topRight'
|
215
|
+
});
|
216
|
+
}
|
217
|
+
|
218
|
+
this.error = function(msg) {
|
219
|
+
msg = msg.replace(/</gm, '<').replace(/>/gm, '>').
|
220
|
+
replace(/\n|\r/gm, '<br>').replace(/\t/gm, ' ');
|
221
|
+
noty({
|
222
|
+
text: '<div style="text-align: left; max-height: 280px; overflow: auto;">' + msg + '</div>',
|
223
|
+
type: 'warning',
|
224
|
+
layout: 'top',
|
225
|
+
modal: true,
|
226
|
+
closeWith: ['button'],
|
227
|
+
buttons: [
|
228
|
+
{addClass: 'btn btn-primary', text: 'Close', onClick: function($noty) {
|
229
|
+
$noty.close();
|
230
|
+
}
|
231
|
+
}
|
232
|
+
]
|
233
|
+
});
|
234
|
+
}
|
235
|
+
|
236
|
+
var redirect = function(_url, _params) {
|
237
|
+
var requestURI = $(location).attr('pathname');
|
238
|
+
if((typeof _url) == 'object') {
|
239
|
+
params = _url;
|
240
|
+
url = requestURI;
|
241
|
+
} else {
|
242
|
+
params = _params || params;
|
243
|
+
url = _url || requestURI;
|
244
|
+
}
|
245
|
+
window.location = url + build_query_string(params);
|
246
|
+
}
|
247
|
+
|
248
|
+
var reload = function() {
|
249
|
+
window.location.reload(true);
|
250
|
+
}
|
251
|
+
|
252
|
+
var build_query_string = function(_params) {
|
253
|
+
var params = _params || params;
|
254
|
+
if(jQuery.isEmptyObject(params)) return '';
|
255
|
+
var query_string = [];
|
256
|
+
$.each(params, function(k,v) { query_string.push(k + '=' + v) });
|
257
|
+
return '?' + query_string.join('&');
|
258
|
+
}
|
259
|
+
|
260
|
+
var guess_path = function(path) {
|
261
|
+
return (path || (params.file ? params.file.path : params.location.path));
|
262
|
+
}
|
263
|
+
var guess_name = function(name) {
|
264
|
+
return (name || (params.file ? params.file.name : params.location.name));
|
265
|
+
}
|
266
|
+
|
267
|
+
var displayError = function(xhr) {
|
268
|
+
if(xhr.status == 400)
|
269
|
+
ELFinder.warn(xhr.responseText);
|
270
|
+
else if(xhr.status == 500)
|
271
|
+
ELFinder.error(xhr.responseText);
|
272
|
+
}
|
273
|
+
}
|
@@ -0,0 +1,147 @@
|
|
1
|
+
/*!
|
2
|
+
* Bootstrap Context Menu
|
3
|
+
* Version: 2.0
|
4
|
+
* A small variation of the dropdown plugin by @sydcanem
|
5
|
+
* https://github.com/sydcanem/bootstrap-contextmenu
|
6
|
+
*
|
7
|
+
* Twitter Bootstrap (http://twitter.github.com/bootstrap).
|
8
|
+
*/
|
9
|
+
|
10
|
+
/* =========================================================
|
11
|
+
* bootstrap-dropdown.js
|
12
|
+
* http://twitter.github.com/bootstrap/
|
13
|
+
* =========================================================
|
14
|
+
* Copyright 2012 Twitter, Inc.
|
15
|
+
*
|
16
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
17
|
+
* you may not use this file except in compliance with the License.
|
18
|
+
* You may obtain a copy of the License at
|
19
|
+
*
|
20
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
21
|
+
*
|
22
|
+
* Unless required by applicable law or agreed to in writing, software
|
23
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
24
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
25
|
+
* See the License for the specific language governing permissions and
|
26
|
+
* limitations under the License.
|
27
|
+
* ========================================================= */
|
28
|
+
|
29
|
+
!(function($) {
|
30
|
+
|
31
|
+
"use strict"; // jshint ;_;
|
32
|
+
|
33
|
+
/* CONTEXTMENU CLASS DEFINITION
|
34
|
+
* ============================ */
|
35
|
+
|
36
|
+
var toggle = '[data-toggle=context]'
|
37
|
+
, ContextMenu = function (element) {
|
38
|
+
var $el = $(element).on('contextmenu.context.data-api', this.toggle);
|
39
|
+
var $target = $($el.attr('data-target'));
|
40
|
+
$('html').on('click.context.data-api', function (e) {
|
41
|
+
if (!e.ctrlKey) {
|
42
|
+
$target.removeClass('open');
|
43
|
+
}
|
44
|
+
});
|
45
|
+
}
|
46
|
+
|
47
|
+
ContextMenu.prototype = {
|
48
|
+
|
49
|
+
constructor: ContextMenu
|
50
|
+
,toggle: function(e) {
|
51
|
+
|
52
|
+
var $this = $(this)
|
53
|
+
, $menu
|
54
|
+
, $contextmenu
|
55
|
+
, evt;
|
56
|
+
|
57
|
+
if ($this.is('.disabled, :disabled')) return;
|
58
|
+
|
59
|
+
evt = $.Event('context');
|
60
|
+
$this.trigger(evt);
|
61
|
+
|
62
|
+
$menu = getMenu($this);
|
63
|
+
$menu.removeClass('open');
|
64
|
+
|
65
|
+
var tp = getPosition(e, $menu);
|
66
|
+
$menu.attr('style', '')
|
67
|
+
.css(tp)
|
68
|
+
.toggleClass('open');
|
69
|
+
|
70
|
+
return false;
|
71
|
+
}
|
72
|
+
|
73
|
+
}
|
74
|
+
|
75
|
+
function getMenu($this) {
|
76
|
+
var selector = $this.attr('data-target')
|
77
|
+
, $menu;
|
78
|
+
|
79
|
+
if (!selector) {
|
80
|
+
selector = $this.attr('href')
|
81
|
+
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
|
82
|
+
}
|
83
|
+
|
84
|
+
$menu = $(selector);
|
85
|
+
|
86
|
+
return $menu;
|
87
|
+
}
|
88
|
+
|
89
|
+
function getPosition(e, $menu) {
|
90
|
+
var mouseX = e.clientX
|
91
|
+
, mouseY = e.clientY
|
92
|
+
, boundsX = $(window).width()
|
93
|
+
, boundsY = $(window).height()
|
94
|
+
, menuWidth = $menu.find('.dropdown-menu').outerWidth()
|
95
|
+
, menuHeight = $menu.find('.dropdown-menu').outerHeight()
|
96
|
+
, tp = {"position":"fixed"}
|
97
|
+
, Y, X;
|
98
|
+
|
99
|
+
if (mouseY + menuHeight > boundsY) {
|
100
|
+
Y = {"top": mouseY - menuHeight};
|
101
|
+
} else {
|
102
|
+
Y = {"top": mouseY};
|
103
|
+
}
|
104
|
+
|
105
|
+
if (mouseX + menuWidth > boundsX) {
|
106
|
+
X = {"left": mouseX - menuWidth};
|
107
|
+
} else {
|
108
|
+
X = {"left": mouseX};
|
109
|
+
}
|
110
|
+
|
111
|
+
return $.extend(tp, Y, X);
|
112
|
+
}
|
113
|
+
|
114
|
+
function clearMenus(e) {
|
115
|
+
if (!e.ctrlKey) {
|
116
|
+
$(toggle).each(function() {
|
117
|
+
getMenu($(this))
|
118
|
+
.removeClass('open');
|
119
|
+
});
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
/* CONTEXT MENU PLUGIN DEFINITION
|
124
|
+
* ========================== */
|
125
|
+
|
126
|
+
$.fn.contextmenu = function (option) {
|
127
|
+
return this.each(function () {
|
128
|
+
var $this = $(this)
|
129
|
+
, data = $this.data('context');
|
130
|
+
if (!data) $this.data('context', (data = new ContextMenu(this)));
|
131
|
+
if (typeof option == 'string') data[option].call($this);
|
132
|
+
});
|
133
|
+
}
|
134
|
+
|
135
|
+
$.fn.contextmenu.Constructor = ContextMenu;
|
136
|
+
|
137
|
+
/* APPLY TO STANDARD CONTEXT MENU ELEMENTS
|
138
|
+
* =================================== */
|
139
|
+
|
140
|
+
$(function () {
|
141
|
+
$('html')
|
142
|
+
.on('click.context.data-api', clearMenus)
|
143
|
+
$('body')
|
144
|
+
.on('contextmenu.context.data-api', toggle, ContextMenu.prototype.toggle);
|
145
|
+
});
|
146
|
+
|
147
|
+
}(window.jQuery));
|