coupler 0.0.1-java
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitmodules +3 -0
- data/.rvmrc +1 -0
- data/.vimrc +40 -0
- data/Gemfile +27 -0
- data/Gemfile.lock +71 -0
- data/LICENSE +20 -0
- data/NOTES +6 -0
- data/README.rdoc +18 -0
- data/Rakefile +42 -0
- data/TODO +11 -0
- data/VERSION +1 -0
- data/bin/coupler +7 -0
- data/db/.gitignore +6 -0
- data/db/migrate/001_initial_schema.rb +166 -0
- data/db/migrate/002_stub.rb +4 -0
- data/db/migrate/003_stub.rb +4 -0
- data/db/migrate/004_create_comparisons.rb +28 -0
- data/db/migrate/005_move_database_name.rb +19 -0
- data/db/migrate/006_upgrade_comparisons.rb +34 -0
- data/db/migrate/007_add_which_to_comparisons.rb +23 -0
- data/db/migrate/008_add_result_field_to_transformations.rb +33 -0
- data/db/migrate/009_add_generated_flag_to_fields.rb +13 -0
- data/db/migrate/010_create_imports.rb +24 -0
- data/db/migrate/011_add_primary_key_type.rb +13 -0
- data/db/migrate/012_add_transformed_with_to_resources.rb +13 -0
- data/db/migrate/013_add_run_count_to_scenarios.rb +13 -0
- data/db/migrate/014_add_last_accessed_at_to_some_tables.rb +13 -0
- data/db/migrate/015_add_run_number_to_results.rb +15 -0
- data/db/migrate/016_fix_scenario_run_count.rb +27 -0
- data/db/migrate/017_rename_comparison_columns.rb +14 -0
- data/db/migrate/018_fix_scenario_linkage_type.rb +8 -0
- data/db/migrate/019_add_columns_to_imports.rb +24 -0
- data/db/migrate/020_rename_import_columns.rb +12 -0
- data/db/migrate/021_add_fields_to_connections.rb +15 -0
- data/db/migrate/022_remove_database_name_from_resources.rb +11 -0
- data/features/connections.feature +28 -0
- data/features/matchers.feature +35 -0
- data/features/projects.feature +11 -0
- data/features/resources.feature +62 -0
- data/features/scenarios.feature +45 -0
- data/features/step_definitions/coupler_steps.rb +145 -0
- data/features/step_definitions/matchers_steps.rb +26 -0
- data/features/step_definitions/resources_steps.rb +12 -0
- data/features/step_definitions/scenarios_steps.rb +7 -0
- data/features/step_definitions/transformations_steps.rb +3 -0
- data/features/support/env.rb +128 -0
- data/features/transformations.feature +22 -0
- data/features/wizard.feature +10 -0
- data/gfx/coupler-header.svg +213 -0
- data/gfx/coupler-sidebar.svg +656 -0
- data/gfx/coupler.svg +184 -0
- data/gfx/icon.svg +75 -0
- data/lib/coupler/base.rb +63 -0
- data/lib/coupler/config.rb +128 -0
- data/lib/coupler/data_uploader.rb +20 -0
- data/lib/coupler/database.rb +31 -0
- data/lib/coupler/extensions/connections.rb +57 -0
- data/lib/coupler/extensions/exceptions.rb +58 -0
- data/lib/coupler/extensions/imports.rb +43 -0
- data/lib/coupler/extensions/jobs.rb +21 -0
- data/lib/coupler/extensions/matchers.rb +64 -0
- data/lib/coupler/extensions/projects.rb +62 -0
- data/lib/coupler/extensions/resources.rb +89 -0
- data/lib/coupler/extensions/results.rb +100 -0
- data/lib/coupler/extensions/scenarios.rb +50 -0
- data/lib/coupler/extensions/transformations.rb +70 -0
- data/lib/coupler/extensions/transformers.rb +58 -0
- data/lib/coupler/extensions.rb +16 -0
- data/lib/coupler/helpers.rb +121 -0
- data/lib/coupler/import_buffer.rb +48 -0
- data/lib/coupler/logger.rb +16 -0
- data/lib/coupler/models/common_model.rb +104 -0
- data/lib/coupler/models/comparison.rb +166 -0
- data/lib/coupler/models/connection.rb +59 -0
- data/lib/coupler/models/field.rb +55 -0
- data/lib/coupler/models/import.rb +238 -0
- data/lib/coupler/models/job.rb +42 -0
- data/lib/coupler/models/jobify.rb +17 -0
- data/lib/coupler/models/matcher.rb +36 -0
- data/lib/coupler/models/project.rb +40 -0
- data/lib/coupler/models/resource.rb +287 -0
- data/lib/coupler/models/result.rb +92 -0
- data/lib/coupler/models/scenario/runner.rb +357 -0
- data/lib/coupler/models/scenario.rb +115 -0
- data/lib/coupler/models/transformation.rb +117 -0
- data/lib/coupler/models/transformer/runner.rb +28 -0
- data/lib/coupler/models/transformer.rb +110 -0
- data/lib/coupler/models.rb +30 -0
- data/lib/coupler/runner.rb +76 -0
- data/lib/coupler/scheduler.rb +56 -0
- data/lib/coupler.rb +34 -0
- data/log/.gitignore +1 -0
- data/misc/README +5 -0
- data/misc/jruby-json.license +57 -0
- data/misc/rack-flash.license +22 -0
- data/script/dbconsole.rb +5 -0
- data/src/edu/vanderbilt/coupler/Main.java +116 -0
- data/src/edu/vanderbilt/coupler/jruby.properties +1 -0
- data/tasks/annotations.rake +84 -0
- data/tasks/db.rake +120 -0
- data/tasks/environment.rake +12 -0
- data/tasks/jeweler.rake +43 -0
- data/tasks/package.rake +58 -0
- data/tasks/rdoc.rake +13 -0
- data/tasks/test.rake +63 -0
- data/tasks/vendor.rake +43 -0
- data/test/README.txt +6 -0
- data/test/config.yml +9 -0
- data/test/coupler/models/test_import.rb +221 -0
- data/test/factories.rb +91 -0
- data/test/fixtures/duplicate-keys.csv +5 -0
- data/test/fixtures/no-headers.csv +50 -0
- data/test/fixtures/people.csv +51 -0
- data/test/fixtures/varying-row-size.csv +4 -0
- data/test/helper.rb +156 -0
- data/test/integration/extensions/test_connections.rb +80 -0
- data/test/integration/extensions/test_imports.rb +94 -0
- data/test/integration/extensions/test_jobs.rb +52 -0
- data/test/integration/extensions/test_matchers.rb +134 -0
- data/test/integration/extensions/test_projects.rb +82 -0
- data/test/integration/extensions/test_resources.rb +150 -0
- data/test/integration/extensions/test_results.rb +89 -0
- data/test/integration/extensions/test_scenarios.rb +88 -0
- data/test/integration/extensions/test_transformations.rb +113 -0
- data/test/integration/extensions/test_transformers.rb +80 -0
- data/test/integration/test_field.rb +45 -0
- data/test/integration/test_import.rb +78 -0
- data/test/integration/test_running_scenarios.rb +379 -0
- data/test/integration/test_transformation.rb +56 -0
- data/test/integration/test_transforming.rb +154 -0
- data/test/table_sets.rb +76 -0
- data/test/unit/models/test_common_model.rb +130 -0
- data/test/unit/models/test_comparison.rb +619 -0
- data/test/unit/models/test_connection.rb +115 -0
- data/test/unit/models/test_field.rb +99 -0
- data/test/unit/models/test_import.rb +130 -0
- data/test/unit/models/test_job.rb +115 -0
- data/test/unit/models/test_matcher.rb +82 -0
- data/test/unit/models/test_project.rb +102 -0
- data/test/unit/models/test_resource.rb +564 -0
- data/test/unit/models/test_result.rb +90 -0
- data/test/unit/models/test_scenario.rb +199 -0
- data/test/unit/models/test_transformation.rb +193 -0
- data/test/unit/models/test_transformer.rb +188 -0
- data/test/unit/test_base.rb +60 -0
- data/test/unit/test_data_uploader.rb +27 -0
- data/test/unit/test_database.rb +23 -0
- data/test/unit/test_helpers.rb +58 -0
- data/test/unit/test_logger.rb +10 -0
- data/test/unit/test_models.rb +12 -0
- data/test/unit/test_runner.rb +76 -0
- data/test/unit/test_scheduler.rb +66 -0
- data/uploads/.gitignore +2 -0
- data/vendor/java/.gitignore +5 -0
- data/webroot/public/css/960.css +1 -0
- data/webroot/public/css/dataTables.css +1057 -0
- data/webroot/public/css/jquery-ui.css +572 -0
- data/webroot/public/css/jquery.treeview.css +68 -0
- data/webroot/public/css/reset.css +1 -0
- data/webroot/public/css/style.css +504 -0
- data/webroot/public/css/text.css +1 -0
- data/webroot/public/favicon.ico +0 -0
- data/webroot/public/images/12_col.gif +0 -0
- data/webroot/public/images/16_col.gif +0 -0
- data/webroot/public/images/add.png +0 -0
- data/webroot/public/images/ajax-loader.gif +0 -0
- data/webroot/public/images/cog.png +0 -0
- data/webroot/public/images/coupler.png +0 -0
- data/webroot/public/images/foo.png +0 -0
- data/webroot/public/images/hammer.png +0 -0
- data/webroot/public/images/header.png +0 -0
- data/webroot/public/images/home.gif +0 -0
- data/webroot/public/images/jobs.gif +0 -0
- data/webroot/public/images/sidebar-bottom.png +0 -0
- data/webroot/public/images/sidebar.png +0 -0
- data/webroot/public/images/treeview-default-line.gif +0 -0
- data/webroot/public/images/treeview-default.gif +0 -0
- data/webroot/public/images/ui-anim_basic_16x16.gif +0 -0
- data/webroot/public/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/webroot/public/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/webroot/public/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/webroot/public/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/webroot/public/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/webroot/public/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/webroot/public/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/webroot/public/images/ui-bg_highlight-hard_30_565356_1x100.png +0 -0
- data/webroot/public/images/ui-bg_highlight-hard_75_888588_1x100.png +0 -0
- data/webroot/public/images/ui-bg_highlight-soft_30_6e3b3a_1x100.png +0 -0
- data/webroot/public/images/ui-bg_highlight-soft_35_8e8b8e_1x100.png +0 -0
- data/webroot/public/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/webroot/public/images/ui-icons_222222_256x240.png +0 -0
- data/webroot/public/images/ui-icons_2e83ff_256x240.png +0 -0
- data/webroot/public/images/ui-icons_454545_256x240.png +0 -0
- data/webroot/public/images/ui-icons_888888_256x240.png +0 -0
- data/webroot/public/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/webroot/public/images/ui-icons_ffffff_256x240.png +0 -0
- data/webroot/public/js/ajaxupload.js +673 -0
- data/webroot/public/js/application.js +40 -0
- data/webroot/public/js/jquery-ui.combobox.js +98 -0
- data/webroot/public/js/jquery-ui.js +9867 -0
- data/webroot/public/js/jquery-ui.min.js +559 -0
- data/webroot/public/js/jquery.dataTables.min.js +587 -0
- data/webroot/public/js/jquery.min.js +154 -0
- data/webroot/public/js/jquery.timeago.js +140 -0
- data/webroot/public/js/jquery.tooltip.min.js +19 -0
- data/webroot/public/js/jquery.treeview.min.js +15 -0
- data/webroot/public/js/resource.js +11 -0
- data/webroot/public/js/results.js +56 -0
- data/webroot/public/js/transformations.js +95 -0
- data/webroot/views/connections/index.erb +5 -0
- data/webroot/views/connections/list.erb +34 -0
- data/webroot/views/connections/new.erb +55 -0
- data/webroot/views/connections/show.erb +36 -0
- data/webroot/views/imports/edit.erb +60 -0
- data/webroot/views/imports/form.erb +81 -0
- data/webroot/views/imports/new.erb +89 -0
- data/webroot/views/index.erb +12 -0
- data/webroot/views/jobs/index.erb +7 -0
- data/webroot/views/jobs/list.erb +24 -0
- data/webroot/views/layout.erb +38 -0
- data/webroot/views/matchers/form.erb +250 -0
- data/webroot/views/matchers/list.erb +32 -0
- data/webroot/views/projects/form.erb +14 -0
- data/webroot/views/projects/index.erb +96 -0
- data/webroot/views/projects/show.erb +24 -0
- data/webroot/views/resources/edit.erb +88 -0
- data/webroot/views/resources/index.erb +5 -0
- data/webroot/views/resources/list.erb +27 -0
- data/webroot/views/resources/new.erb +121 -0
- data/webroot/views/resources/show.erb +86 -0
- data/webroot/views/resources/transform.erb +2 -0
- data/webroot/views/results/csv.erb +12 -0
- data/webroot/views/results/details.erb +15 -0
- data/webroot/views/results/index.erb +2 -0
- data/webroot/views/results/list.erb +22 -0
- data/webroot/views/results/record.erb +24 -0
- data/webroot/views/results/show.erb +68 -0
- data/webroot/views/scenarios/index.erb +5 -0
- data/webroot/views/scenarios/list.erb +20 -0
- data/webroot/views/scenarios/new.erb +99 -0
- data/webroot/views/scenarios/run.erb +2 -0
- data/webroot/views/scenarios/show.erb +50 -0
- data/webroot/views/sidebar.erb +106 -0
- data/webroot/views/transformations/create.erb +115 -0
- data/webroot/views/transformations/for.erb +16 -0
- data/webroot/views/transformations/index.erb +2 -0
- data/webroot/views/transformations/list.erb +29 -0
- data/webroot/views/transformations/new.erb +126 -0
- data/webroot/views/transformations/preview.erb +46 -0
- data/webroot/views/transformers/edit.erb +6 -0
- data/webroot/views/transformers/form.erb +58 -0
- data/webroot/views/transformers/index.erb +2 -0
- data/webroot/views/transformers/list.erb +25 -0
- data/webroot/views/transformers/new.erb +5 -0
- data/webroot/views/transformers/preview.erb +23 -0
- data/webroot/views/transformers/show.erb +0 -0
- metadata +558 -0
@@ -0,0 +1,673 @@
|
|
1
|
+
/**
|
2
|
+
* AJAX Upload ( http://valums.com/ajax-upload/ )
|
3
|
+
* Copyright (c) Andris Valums
|
4
|
+
* Licensed under the MIT license ( http://valums.com/mit-license/ )
|
5
|
+
* Thanks to Gary Haran, David Mark, Corey Burns and others for contributions
|
6
|
+
*/
|
7
|
+
(function () {
|
8
|
+
/* global window */
|
9
|
+
/* jslint browser: true, devel: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true */
|
10
|
+
|
11
|
+
/**
|
12
|
+
* Wrapper for FireBug's console.log
|
13
|
+
*/
|
14
|
+
function log(){
|
15
|
+
if (typeof(console) != 'undefined' && typeof(console.log) == 'function'){
|
16
|
+
Array.prototype.unshift.call(arguments, '[Ajax Upload]');
|
17
|
+
console.log( Array.prototype.join.call(arguments, ' '));
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Attaches event to a dom element.
|
23
|
+
* @param {Element} el
|
24
|
+
* @param type event name
|
25
|
+
* @param fn callback This refers to the passed element
|
26
|
+
*/
|
27
|
+
function addEvent(el, type, fn){
|
28
|
+
if (el.addEventListener) {
|
29
|
+
el.addEventListener(type, fn, false);
|
30
|
+
} else if (el.attachEvent) {
|
31
|
+
el.attachEvent('on' + type, function(){
|
32
|
+
fn.call(el);
|
33
|
+
});
|
34
|
+
} else {
|
35
|
+
throw new Error('not supported or DOM not loaded');
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Attaches resize event to a window, limiting
|
41
|
+
* number of event fired. Fires only when encounteres
|
42
|
+
* delay of 100 after series of events.
|
43
|
+
*
|
44
|
+
* Some browsers fire event multiple times when resizing
|
45
|
+
* http://www.quirksmode.org/dom/events/resize.html
|
46
|
+
*
|
47
|
+
* @param fn callback This refers to the passed element
|
48
|
+
*/
|
49
|
+
function addResizeEvent(fn){
|
50
|
+
var timeout;
|
51
|
+
|
52
|
+
addEvent(window, 'resize', function(){
|
53
|
+
if (timeout){
|
54
|
+
clearTimeout(timeout);
|
55
|
+
}
|
56
|
+
timeout = setTimeout(fn, 100);
|
57
|
+
});
|
58
|
+
}
|
59
|
+
|
60
|
+
// Needs more testing, will be rewriten for next version
|
61
|
+
// getOffset function copied from jQuery lib (http://jquery.com/)
|
62
|
+
if (document.documentElement.getBoundingClientRect){
|
63
|
+
// Get Offset using getBoundingClientRect
|
64
|
+
// http://ejohn.org/blog/getboundingclientrect-is-awesome/
|
65
|
+
var getOffset = function(el){
|
66
|
+
var box = el.getBoundingClientRect();
|
67
|
+
var doc = el.ownerDocument;
|
68
|
+
var body = doc.body;
|
69
|
+
var docElem = doc.documentElement; // for ie
|
70
|
+
var clientTop = docElem.clientTop || body.clientTop || 0;
|
71
|
+
var clientLeft = docElem.clientLeft || body.clientLeft || 0;
|
72
|
+
|
73
|
+
// In Internet Explorer 7 getBoundingClientRect property is treated as physical,
|
74
|
+
// while others are logical. Make all logical, like in IE8.
|
75
|
+
var zoom = 1;
|
76
|
+
if (body.getBoundingClientRect) {
|
77
|
+
var bound = body.getBoundingClientRect();
|
78
|
+
zoom = (bound.right - bound.left) / body.clientWidth;
|
79
|
+
}
|
80
|
+
|
81
|
+
if (zoom > 1) {
|
82
|
+
clientTop = 0;
|
83
|
+
clientLeft = 0;
|
84
|
+
}
|
85
|
+
|
86
|
+
var top = box.top / zoom + (window.pageYOffset || docElem && docElem.scrollTop / zoom || body.scrollTop / zoom) - clientTop, left = box.left / zoom + (window.pageXOffset || docElem && docElem.scrollLeft / zoom || body.scrollLeft / zoom) - clientLeft;
|
87
|
+
|
88
|
+
return {
|
89
|
+
top: top,
|
90
|
+
left: left
|
91
|
+
};
|
92
|
+
};
|
93
|
+
} else {
|
94
|
+
// Get offset adding all offsets
|
95
|
+
var getOffset = function(el){
|
96
|
+
var top = 0, left = 0;
|
97
|
+
do {
|
98
|
+
top += el.offsetTop || 0;
|
99
|
+
left += el.offsetLeft || 0;
|
100
|
+
el = el.offsetParent;
|
101
|
+
} while (el);
|
102
|
+
|
103
|
+
return {
|
104
|
+
left: left,
|
105
|
+
top: top
|
106
|
+
};
|
107
|
+
};
|
108
|
+
}
|
109
|
+
|
110
|
+
/**
|
111
|
+
* Returns left, top, right and bottom properties describing the border-box,
|
112
|
+
* in pixels, with the top-left relative to the body
|
113
|
+
* @param {Element} el
|
114
|
+
* @return {Object} Contains left, top, right,bottom
|
115
|
+
*/
|
116
|
+
function getBox(el){
|
117
|
+
var left, right, top, bottom;
|
118
|
+
var offset = getOffset(el);
|
119
|
+
left = offset.left;
|
120
|
+
top = offset.top;
|
121
|
+
|
122
|
+
right = left + el.offsetWidth;
|
123
|
+
bottom = top + el.offsetHeight;
|
124
|
+
|
125
|
+
return {
|
126
|
+
left: left,
|
127
|
+
right: right,
|
128
|
+
top: top,
|
129
|
+
bottom: bottom
|
130
|
+
};
|
131
|
+
}
|
132
|
+
|
133
|
+
/**
|
134
|
+
* Helper that takes object literal
|
135
|
+
* and add all properties to element.style
|
136
|
+
* @param {Element} el
|
137
|
+
* @param {Object} styles
|
138
|
+
*/
|
139
|
+
function addStyles(el, styles){
|
140
|
+
for (var name in styles) {
|
141
|
+
if (styles.hasOwnProperty(name)) {
|
142
|
+
el.style[name] = styles[name];
|
143
|
+
}
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
/**
|
148
|
+
* Function places an absolutely positioned
|
149
|
+
* element on top of the specified element
|
150
|
+
* copying position and dimentions.
|
151
|
+
* @param {Element} from
|
152
|
+
* @param {Element} to
|
153
|
+
*/
|
154
|
+
function copyLayout(from, to){
|
155
|
+
var box = getBox(from);
|
156
|
+
|
157
|
+
addStyles(to, {
|
158
|
+
position: 'absolute',
|
159
|
+
left : box.left + 'px',
|
160
|
+
top : box.top + 'px',
|
161
|
+
width : from.offsetWidth + 'px',
|
162
|
+
height : from.offsetHeight + 'px'
|
163
|
+
});
|
164
|
+
}
|
165
|
+
|
166
|
+
/**
|
167
|
+
* Creates and returns element from html chunk
|
168
|
+
* Uses innerHTML to create an element
|
169
|
+
*/
|
170
|
+
var toElement = (function(){
|
171
|
+
var div = document.createElement('div');
|
172
|
+
return function(html){
|
173
|
+
div.innerHTML = html;
|
174
|
+
var el = div.firstChild;
|
175
|
+
return div.removeChild(el);
|
176
|
+
};
|
177
|
+
})();
|
178
|
+
|
179
|
+
/**
|
180
|
+
* Function generates unique id
|
181
|
+
* @return unique id
|
182
|
+
*/
|
183
|
+
var getUID = (function(){
|
184
|
+
var id = 0;
|
185
|
+
return function(){
|
186
|
+
return 'ValumsAjaxUpload' + id++;
|
187
|
+
};
|
188
|
+
})();
|
189
|
+
|
190
|
+
/**
|
191
|
+
* Get file name from path
|
192
|
+
* @param {String} file path to file
|
193
|
+
* @return filename
|
194
|
+
*/
|
195
|
+
function fileFromPath(file){
|
196
|
+
return file.replace(/.*(\/|\\)/, "");
|
197
|
+
}
|
198
|
+
|
199
|
+
/**
|
200
|
+
* Get file extension lowercase
|
201
|
+
* @param {String} file name
|
202
|
+
* @return file extenstion
|
203
|
+
*/
|
204
|
+
function getExt(file){
|
205
|
+
return (-1 !== file.indexOf('.')) ? file.replace(/.*[.]/, '') : '';
|
206
|
+
}
|
207
|
+
|
208
|
+
function hasClass(el, name){
|
209
|
+
var re = new RegExp('\\b' + name + '\\b');
|
210
|
+
return re.test(el.className);
|
211
|
+
}
|
212
|
+
function addClass(el, name){
|
213
|
+
if ( ! hasClass(el, name)){
|
214
|
+
el.className += ' ' + name;
|
215
|
+
}
|
216
|
+
}
|
217
|
+
function removeClass(el, name){
|
218
|
+
var re = new RegExp('\\b' + name + '\\b');
|
219
|
+
el.className = el.className.replace(re, '');
|
220
|
+
}
|
221
|
+
|
222
|
+
function removeNode(el){
|
223
|
+
el.parentNode.removeChild(el);
|
224
|
+
}
|
225
|
+
|
226
|
+
/**
|
227
|
+
* Easy styling and uploading
|
228
|
+
* @constructor
|
229
|
+
* @param button An element you want convert to
|
230
|
+
* upload button. Tested dimentions up to 500x500px
|
231
|
+
* @param {Object} options See defaults below.
|
232
|
+
*/
|
233
|
+
window.AjaxUpload = function(button, options){
|
234
|
+
this._settings = {
|
235
|
+
// Location of the server-side upload script
|
236
|
+
action: 'upload.php',
|
237
|
+
// File upload name
|
238
|
+
name: 'userfile',
|
239
|
+
// Additional data to send
|
240
|
+
data: {},
|
241
|
+
// Submit file as soon as it's selected
|
242
|
+
autoSubmit: true,
|
243
|
+
// The type of data that you're expecting back from the server.
|
244
|
+
// html and xml are detected automatically.
|
245
|
+
// Only useful when you are using json data as a response.
|
246
|
+
// Set to "json" in that case.
|
247
|
+
responseType: false,
|
248
|
+
// Class applied to button when mouse is hovered
|
249
|
+
hoverClass: 'hover',
|
250
|
+
// Class applied to button when AU is disabled
|
251
|
+
disabledClass: 'disabled',
|
252
|
+
// When user selects a file, useful with autoSubmit disabled
|
253
|
+
// You can return false to cancel upload
|
254
|
+
onChange: function(file, extension){
|
255
|
+
},
|
256
|
+
// Callback to fire before file is uploaded
|
257
|
+
// You can return false to cancel upload
|
258
|
+
onSubmit: function(file, extension){
|
259
|
+
},
|
260
|
+
// Fired when file upload is completed
|
261
|
+
// WARNING! DO NOT USE "FALSE" STRING AS A RESPONSE!
|
262
|
+
onComplete: function(file, response){
|
263
|
+
}
|
264
|
+
};
|
265
|
+
|
266
|
+
// Merge the users options with our defaults
|
267
|
+
for (var i in options) {
|
268
|
+
if (options.hasOwnProperty(i)){
|
269
|
+
this._settings[i] = options[i];
|
270
|
+
}
|
271
|
+
}
|
272
|
+
|
273
|
+
// button isn't necessary a dom element
|
274
|
+
if (button.jquery){
|
275
|
+
// jQuery object was passed
|
276
|
+
button = button[0];
|
277
|
+
} else if (typeof button == "string") {
|
278
|
+
if (/^#.*/.test(button)){
|
279
|
+
// If jQuery user passes #elementId don't break it
|
280
|
+
button = button.slice(1);
|
281
|
+
}
|
282
|
+
|
283
|
+
button = document.getElementById(button);
|
284
|
+
}
|
285
|
+
|
286
|
+
if ( ! button || button.nodeType !== 1){
|
287
|
+
throw new Error("Please make sure that you're passing a valid element");
|
288
|
+
}
|
289
|
+
|
290
|
+
if ( button.nodeName.toUpperCase() == 'A'){
|
291
|
+
// disable link
|
292
|
+
addEvent(button, 'click', function(e){
|
293
|
+
if (e && e.preventDefault){
|
294
|
+
e.preventDefault();
|
295
|
+
} else if (window.event){
|
296
|
+
window.event.returnValue = false;
|
297
|
+
}
|
298
|
+
});
|
299
|
+
}
|
300
|
+
|
301
|
+
// DOM element
|
302
|
+
this._button = button;
|
303
|
+
// DOM element
|
304
|
+
this._input = null;
|
305
|
+
// If disabled clicking on button won't do anything
|
306
|
+
this._disabled = false;
|
307
|
+
|
308
|
+
// if the button was disabled before refresh if will remain
|
309
|
+
// disabled in FireFox, let's fix it
|
310
|
+
this.enable();
|
311
|
+
|
312
|
+
this._rerouteClicks();
|
313
|
+
};
|
314
|
+
|
315
|
+
// assigning methods to our class
|
316
|
+
AjaxUpload.prototype = {
|
317
|
+
setData: function(data){
|
318
|
+
this._settings.data = data;
|
319
|
+
},
|
320
|
+
disable: function(){
|
321
|
+
addClass(this._button, this._settings.disabledClass);
|
322
|
+
this._disabled = true;
|
323
|
+
|
324
|
+
var nodeName = this._button.nodeName.toUpperCase();
|
325
|
+
if (nodeName == 'INPUT' || nodeName == 'BUTTON'){
|
326
|
+
this._button.setAttribute('disabled', 'disabled');
|
327
|
+
}
|
328
|
+
|
329
|
+
// hide input
|
330
|
+
if (this._input){
|
331
|
+
// We use visibility instead of display to fix problem with Safari 4
|
332
|
+
// The problem is that the value of input doesn't change if it
|
333
|
+
// has display none when user selects a file
|
334
|
+
this._input.parentNode.style.visibility = 'hidden';
|
335
|
+
}
|
336
|
+
},
|
337
|
+
enable: function(){
|
338
|
+
removeClass(this._button, this._settings.disabledClass);
|
339
|
+
this._button.removeAttribute('disabled');
|
340
|
+
this._disabled = false;
|
341
|
+
|
342
|
+
},
|
343
|
+
/**
|
344
|
+
* Creates invisible file input
|
345
|
+
* that will hover above the button
|
346
|
+
* <div><input type='file' /></div>
|
347
|
+
*/
|
348
|
+
_createInput: function(){
|
349
|
+
var self = this;
|
350
|
+
|
351
|
+
var input = document.createElement("input");
|
352
|
+
input.setAttribute('type', 'file');
|
353
|
+
input.setAttribute('name', this._settings.name);
|
354
|
+
|
355
|
+
addStyles(input, {
|
356
|
+
'position' : 'absolute',
|
357
|
+
// in Opera only 'browse' button
|
358
|
+
// is clickable and it is located at
|
359
|
+
// the right side of the input
|
360
|
+
'right' : 0,
|
361
|
+
'margin' : 0,
|
362
|
+
'padding' : 0,
|
363
|
+
'fontSize' : '480px',
|
364
|
+
'cursor' : 'pointer'
|
365
|
+
});
|
366
|
+
|
367
|
+
var div = document.createElement("div");
|
368
|
+
addStyles(div, {
|
369
|
+
'display' : 'block',
|
370
|
+
'position' : 'absolute',
|
371
|
+
'overflow' : 'hidden',
|
372
|
+
'margin' : 0,
|
373
|
+
'padding' : 0,
|
374
|
+
'opacity' : 0,
|
375
|
+
// Make sure browse button is in the right side
|
376
|
+
// in Internet Explorer
|
377
|
+
'direction' : 'ltr',
|
378
|
+
//Max zIndex supported by Opera 9.0-9.2
|
379
|
+
'zIndex': 2147483583
|
380
|
+
});
|
381
|
+
|
382
|
+
// Make sure that element opacity exists.
|
383
|
+
// Otherwise use IE filter
|
384
|
+
if ( div.style.opacity !== "0") {
|
385
|
+
if (typeof(div.filters) == 'undefined'){
|
386
|
+
throw new Error('Opacity not supported by the browser');
|
387
|
+
}
|
388
|
+
div.style.filter = "alpha(opacity=0)";
|
389
|
+
}
|
390
|
+
|
391
|
+
addEvent(input, 'change', function(){
|
392
|
+
|
393
|
+
if ( ! input || input.value === ''){
|
394
|
+
return;
|
395
|
+
}
|
396
|
+
|
397
|
+
// Get filename from input, required
|
398
|
+
// as some browsers have path instead of it
|
399
|
+
var file = fileFromPath(input.value);
|
400
|
+
|
401
|
+
if (false === self._settings.onChange.call(self, file, getExt(file))){
|
402
|
+
self._clearInput();
|
403
|
+
return;
|
404
|
+
}
|
405
|
+
|
406
|
+
// Submit form when value is changed
|
407
|
+
if (self._settings.autoSubmit) {
|
408
|
+
self.submit();
|
409
|
+
}
|
410
|
+
});
|
411
|
+
|
412
|
+
addEvent(input, 'mouseover', function(){
|
413
|
+
addClass(self._button, self._settings.hoverClass);
|
414
|
+
});
|
415
|
+
|
416
|
+
addEvent(input, 'mouseout', function(){
|
417
|
+
removeClass(self._button, self._settings.hoverClass);
|
418
|
+
|
419
|
+
// We use visibility instead of display to fix problem with Safari 4
|
420
|
+
// The problem is that the value of input doesn't change if it
|
421
|
+
// has display none when user selects a file
|
422
|
+
input.parentNode.style.visibility = 'hidden';
|
423
|
+
|
424
|
+
});
|
425
|
+
|
426
|
+
div.appendChild(input);
|
427
|
+
document.body.appendChild(div);
|
428
|
+
|
429
|
+
this._input = input;
|
430
|
+
},
|
431
|
+
_clearInput : function(){
|
432
|
+
if (!this._input){
|
433
|
+
return;
|
434
|
+
}
|
435
|
+
|
436
|
+
// this._input.value = ''; Doesn't work in IE6
|
437
|
+
removeNode(this._input.parentNode);
|
438
|
+
this._input = null;
|
439
|
+
this._createInput();
|
440
|
+
|
441
|
+
removeClass(this._button, this._settings.hoverClass);
|
442
|
+
},
|
443
|
+
/**
|
444
|
+
* Function makes sure that when user clicks upload button,
|
445
|
+
* the this._input is clicked instead
|
446
|
+
*/
|
447
|
+
_rerouteClicks: function(){
|
448
|
+
var self = this;
|
449
|
+
|
450
|
+
// IE will later display 'access denied' error
|
451
|
+
// if you use using self._input.click()
|
452
|
+
// other browsers just ignore click()
|
453
|
+
|
454
|
+
addEvent(self._button, 'mouseover', function(){
|
455
|
+
if (self._disabled){
|
456
|
+
return;
|
457
|
+
}
|
458
|
+
|
459
|
+
if ( ! self._input){
|
460
|
+
self._createInput();
|
461
|
+
}
|
462
|
+
|
463
|
+
var div = self._input.parentNode;
|
464
|
+
copyLayout(self._button, div);
|
465
|
+
div.style.visibility = 'visible';
|
466
|
+
|
467
|
+
});
|
468
|
+
|
469
|
+
|
470
|
+
// commented because we now hide input on mouseleave
|
471
|
+
/**
|
472
|
+
* When the window is resized the elements
|
473
|
+
* can be misaligned if button position depends
|
474
|
+
* on window size
|
475
|
+
*/
|
476
|
+
//addResizeEvent(function(){
|
477
|
+
// if (self._input){
|
478
|
+
// copyLayout(self._button, self._input.parentNode);
|
479
|
+
// }
|
480
|
+
//});
|
481
|
+
|
482
|
+
},
|
483
|
+
/**
|
484
|
+
* Creates iframe with unique name
|
485
|
+
* @return {Element} iframe
|
486
|
+
*/
|
487
|
+
_createIframe: function(){
|
488
|
+
// We can't use getTime, because it sometimes return
|
489
|
+
// same value in safari :(
|
490
|
+
var id = getUID();
|
491
|
+
|
492
|
+
// We can't use following code as the name attribute
|
493
|
+
// won't be properly registered in IE6, and new window
|
494
|
+
// on form submit will open
|
495
|
+
// var iframe = document.createElement('iframe');
|
496
|
+
// iframe.setAttribute('name', id);
|
497
|
+
|
498
|
+
var iframe = toElement('<iframe src="javascript:false;" name="' + id + '" />');
|
499
|
+
// src="javascript:false; was added
|
500
|
+
// because it possibly removes ie6 prompt
|
501
|
+
// "This page contains both secure and nonsecure items"
|
502
|
+
// Anyway, it doesn't do any harm.
|
503
|
+
iframe.setAttribute('id', id);
|
504
|
+
|
505
|
+
iframe.style.display = 'none';
|
506
|
+
document.body.appendChild(iframe);
|
507
|
+
|
508
|
+
return iframe;
|
509
|
+
},
|
510
|
+
/**
|
511
|
+
* Creates form, that will be submitted to iframe
|
512
|
+
* @param {Element} iframe Where to submit
|
513
|
+
* @return {Element} form
|
514
|
+
*/
|
515
|
+
_createForm: function(iframe){
|
516
|
+
var settings = this._settings;
|
517
|
+
|
518
|
+
// We can't use the following code in IE6
|
519
|
+
// var form = document.createElement('form');
|
520
|
+
// form.setAttribute('method', 'post');
|
521
|
+
// form.setAttribute('enctype', 'multipart/form-data');
|
522
|
+
// Because in this case file won't be attached to request
|
523
|
+
var form = toElement('<form method="post" enctype="multipart/form-data"></form>');
|
524
|
+
|
525
|
+
form.setAttribute('action', settings.action);
|
526
|
+
form.setAttribute('target', iframe.name);
|
527
|
+
form.style.display = 'none';
|
528
|
+
document.body.appendChild(form);
|
529
|
+
|
530
|
+
// Create hidden input element for each data key
|
531
|
+
for (var prop in settings.data) {
|
532
|
+
if (settings.data.hasOwnProperty(prop)){
|
533
|
+
var el = document.createElement("input");
|
534
|
+
el.setAttribute('type', 'hidden');
|
535
|
+
el.setAttribute('name', prop);
|
536
|
+
el.setAttribute('value', settings.data[prop]);
|
537
|
+
form.appendChild(el);
|
538
|
+
}
|
539
|
+
}
|
540
|
+
return form;
|
541
|
+
},
|
542
|
+
/**
|
543
|
+
* Gets response from iframe and fires onComplete event when ready
|
544
|
+
* @param iframe
|
545
|
+
* @param file Filename to use in onComplete callback
|
546
|
+
*/
|
547
|
+
_getResponse : function(iframe, file){
|
548
|
+
// getting response
|
549
|
+
var toDeleteFlag = false, self = this, settings = this._settings;
|
550
|
+
|
551
|
+
addEvent(iframe, 'load', function(){
|
552
|
+
|
553
|
+
if (// For Safari
|
554
|
+
iframe.src == "javascript:'%3Chtml%3E%3C/html%3E';" ||
|
555
|
+
// For FF, IE
|
556
|
+
iframe.src == "javascript:'<html></html>';"){
|
557
|
+
// First time around, do not delete.
|
558
|
+
// We reload to blank page, so that reloading main page
|
559
|
+
// does not re-submit the post.
|
560
|
+
|
561
|
+
if (toDeleteFlag) {
|
562
|
+
// Fix busy state in FF3
|
563
|
+
setTimeout(function(){
|
564
|
+
removeNode(iframe);
|
565
|
+
}, 0);
|
566
|
+
}
|
567
|
+
|
568
|
+
return;
|
569
|
+
}
|
570
|
+
|
571
|
+
var doc = iframe.contentDocument ? iframe.contentDocument : window.frames[iframe.id].document;
|
572
|
+
|
573
|
+
// fixing Opera 9.26,10.00
|
574
|
+
if (doc.readyState && doc.readyState != 'complete') {
|
575
|
+
// Opera fires load event multiple times
|
576
|
+
// Even when the DOM is not ready yet
|
577
|
+
// this fix should not affect other browsers
|
578
|
+
return;
|
579
|
+
}
|
580
|
+
|
581
|
+
// fixing Opera 9.64
|
582
|
+
if (doc.body && doc.body.innerHTML == "false") {
|
583
|
+
// In Opera 9.64 event was fired second time
|
584
|
+
// when body.innerHTML changed from false
|
585
|
+
// to server response approx. after 1 sec
|
586
|
+
return;
|
587
|
+
}
|
588
|
+
|
589
|
+
var response;
|
590
|
+
|
591
|
+
if (doc.XMLDocument) {
|
592
|
+
// response is a xml document Internet Explorer property
|
593
|
+
response = doc.XMLDocument;
|
594
|
+
} else if (doc.body){
|
595
|
+
// response is html document or plain text
|
596
|
+
response = doc.body.innerHTML;
|
597
|
+
|
598
|
+
if (settings.responseType && settings.responseType.toLowerCase() == 'json') {
|
599
|
+
// If the document was sent as 'application/javascript' or
|
600
|
+
// 'text/javascript', then the browser wraps the text in a <pre>
|
601
|
+
// tag and performs html encoding on the contents. In this case,
|
602
|
+
// we need to pull the original text content from the text node's
|
603
|
+
// nodeValue property to retrieve the unmangled content.
|
604
|
+
// Note that IE6 only understands text/html
|
605
|
+
if (doc.body.firstChild && doc.body.firstChild.nodeName.toUpperCase() == 'PRE') {
|
606
|
+
response = doc.body.firstChild.firstChild.nodeValue;
|
607
|
+
}
|
608
|
+
|
609
|
+
if (response) {
|
610
|
+
response = eval("(" + response + ")");
|
611
|
+
} else {
|
612
|
+
response = {};
|
613
|
+
}
|
614
|
+
}
|
615
|
+
} else {
|
616
|
+
// response is a xml document
|
617
|
+
response = doc;
|
618
|
+
}
|
619
|
+
|
620
|
+
settings.onComplete.call(self, file, response);
|
621
|
+
|
622
|
+
// Reload blank page, so that reloading main page
|
623
|
+
// does not re-submit the post. Also, remember to
|
624
|
+
// delete the frame
|
625
|
+
toDeleteFlag = true;
|
626
|
+
|
627
|
+
// Fix IE mixed content issue
|
628
|
+
iframe.src = "javascript:'<html></html>';";
|
629
|
+
});
|
630
|
+
},
|
631
|
+
/**
|
632
|
+
* Upload file contained in this._input
|
633
|
+
*/
|
634
|
+
submit: function(){
|
635
|
+
var self = this, settings = this._settings;
|
636
|
+
|
637
|
+
if ( ! this._input || this._input.value === ''){
|
638
|
+
return;
|
639
|
+
}
|
640
|
+
|
641
|
+
var file = fileFromPath(this._input.value);
|
642
|
+
|
643
|
+
// user returned false to cancel upload
|
644
|
+
if (false === settings.onSubmit.call(this, file, getExt(file))){
|
645
|
+
this._clearInput();
|
646
|
+
return;
|
647
|
+
}
|
648
|
+
|
649
|
+
// sending request
|
650
|
+
var iframe = this._createIframe();
|
651
|
+
var form = this._createForm(iframe);
|
652
|
+
|
653
|
+
// assuming following structure
|
654
|
+
// div -> input type='file'
|
655
|
+
removeNode(this._input.parentNode);
|
656
|
+
removeClass(self._button, self._settings.hoverClass);
|
657
|
+
|
658
|
+
form.appendChild(this._input);
|
659
|
+
|
660
|
+
form.submit();
|
661
|
+
|
662
|
+
// request set, clean up
|
663
|
+
removeNode(form); form = null;
|
664
|
+
removeNode(this._input); this._input = null;
|
665
|
+
|
666
|
+
// Get response from iframe and fire onComplete event when ready
|
667
|
+
this._getResponse(iframe, file);
|
668
|
+
|
669
|
+
// get ready for next request
|
670
|
+
this._createInput();
|
671
|
+
}
|
672
|
+
};
|
673
|
+
})();
|