| '+a.addText+""),m=b.find("tr:last a")):(d.filter(":last").after('"),m=d.filter(":last").next().find("a")));m.click(function(b){b.preventDefault();b=c("#"+a.prefix+"-empty");
3 | var f=b.clone(!0);f.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+l);f.is("tr")?f.children(":last").append('"):f.is("ul")||f.is("ol")?f.append(''+a.deleteText+""):f.children(":first").append(''+a.deleteText+"");f.find("*").each(function(){k(this,a.prefix,e.val())});f.insertBefore(c(b));
4 | c(e).val(parseInt(e.val(),10)+1);l+=1;""!==g.val()&&0>=g.val()-e.val()&&m.parent().hide();f.find("a."+a.deleteCssClass).click(function(b){b.preventDefault();f.remove();--l;a.removed&&a.removed(f);c(document).trigger("formset:removed",[f,a.prefix]);b=c("."+a.formCssClass);c("#id_"+a.prefix+"-TOTAL_FORMS").val(b.length);(""===g.val()||0 {
11 | const message = error.message;
12 | if (error.location == null) {
13 | const text = [file || "", message].join(":");
14 | return { message, text };
15 | }
16 | else {
17 | const location = error.location;
18 | const line = location.first_line + 1;
19 | const column = location.first_column + 1;
20 | const text = [file || "", line, column, message].join(":");
21 | let markerLen = 2;
22 | if (location.first_line === location.last_line)
23 | markerLen += location.last_column - location.first_column;
24 | const extract = error.code.split('\n')[line - 1];
25 | const annotated = [
26 | text,
27 | " " + extract,
28 | " " + Array(column).join(' ') + Array(markerLen).join('^'),
29 | ].join('\n');
30 | return { message, line, column, text, extract, annotated };
31 | }
32 | };
33 | const mkLessError = (error, file) => {
34 | const message = error.message;
35 | const line = error.line;
36 | const column = error.column + 1;
37 | const text = [file || "", line, column, message].join(":");
38 | const extract = error.extract[line];
39 | const annotated = [text, " " + extract].join("\n");
40 | return { message, line, column, text, extract, annotated };
41 | };
42 | const mkTypeScriptError = (diagnostic) => {
43 | let { line, character: column } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
44 | line += 1;
45 | column += 1;
46 | const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
47 | const text = [diagnostic.file.fileName, line, column, message].join(":");
48 | return { message, line, column, text };
49 | };
50 | const reply = (data) => {
51 | process.stdout.write(JSON.stringify(data));
52 | process.stdout.write("\n");
53 | };
54 | const compile_and_resolve_deps = (input) => {
55 | let code;
56 | switch (input.lang) {
57 | case "coffeescript":
58 | try {
59 | code = coffee.compile(input.code, { bare: true, shiftLine: true });
60 | }
61 | catch (error) {
62 | return reply({ error: mkCoffeescriptError(error, input.file) });
63 | }
64 | break;
65 | case "javascript":
66 | case "typescript":
67 | code = input.code;
68 | break;
69 | case "less":
70 | const options = {
71 | paths: [path.dirname(input.file)],
72 | compress: true,
73 | ieCompat: false,
74 | };
75 | less.render(input.code, options, (error, output) => {
76 | if (error != null)
77 | reply({ error: mkLessError(error, input.file) });
78 | else
79 | reply({ code: output.css });
80 | });
81 | return;
82 | default:
83 | throw new Error(`unsupported input type: ${input.lang}`);
84 | }
85 | const result = ts.transpileModule(code, {
86 | fileName: input.file,
87 | reportDiagnostics: true,
88 | compilerOptions: {
89 | noEmitOnError: false,
90 | noImplicitAny: false,
91 | target: ts.ScriptTarget.ES5,
92 | module: ts.ModuleKind.CommonJS,
93 | jsx: ts.JsxEmit.React,
94 | reactNamespace: "DOM",
95 | },
96 | });
97 | if (result.diagnostics != null && result.diagnostics.length > 0) {
98 | const diagnostic = result.diagnostics[0];
99 | return reply({ error: mkTypeScriptError(diagnostic) });
100 | }
101 | const source = result.outputText;
102 | try {
103 | const deps = detective(source);
104 | return reply({ code: source, deps: deps });
105 | }
106 | catch (error) {
107 | return reply({ error: error });
108 | }
109 | };
110 | if (yargs_1.argv.file != null) {
111 | const input = {
112 | code: fs.readFileSync(yargs_1.argv.file, "utf-8"),
113 | lang: yargs_1.argv.lang || "coffeescript",
114 | file: yargs_1.argv.file,
115 | };
116 | compile_and_resolve_deps(input);
117 | }
118 | else {
119 | const stdin = process.stdin;
120 | stdin.resume();
121 | stdin.setEncoding("utf-8");
122 | let data = "";
123 | stdin.on("data", (chunk) => data += chunk);
124 | stdin.on("end", () => compile_and_resolve_deps(JSON.parse(data)));
125 | }
126 |
--------------------------------------------------------------------------------
/static/admin/css/rtl.css:
--------------------------------------------------------------------------------
1 | body {
2 | direction: rtl;
3 | }
4 |
5 | /* LOGIN */
6 |
7 | .login .form-row {
8 | float: right;
9 | }
10 |
11 | .login .form-row label {
12 | float: right;
13 | padding-left: 0.5em;
14 | padding-right: 0;
15 | text-align: left;
16 | }
17 |
18 | .login .submit-row {
19 | clear: both;
20 | padding: 1em 9.4em 0 0;
21 | }
22 |
23 | /* GLOBAL */
24 |
25 | th {
26 | text-align: right;
27 | }
28 |
29 | .module h2, .module caption {
30 | text-align: right;
31 | }
32 |
33 | .module ul, .module ol {
34 | margin-left: 0;
35 | margin-right: 1.5em;
36 | }
37 |
38 | .addlink, .changelink {
39 | padding-left: 0;
40 | padding-right: 16px;
41 | background-position: 100% 1px;
42 | }
43 |
44 | .deletelink {
45 | padding-left: 0;
46 | padding-right: 16px;
47 | background-position: 100% 1px;
48 | }
49 |
50 | .object-tools {
51 | float: left;
52 | }
53 |
54 | thead th:first-child,
55 | tfoot td:first-child {
56 | border-left: none;
57 | }
58 |
59 | /* LAYOUT */
60 |
61 | #user-tools {
62 | right: auto;
63 | left: 0;
64 | text-align: left;
65 | }
66 |
67 | div.breadcrumbs {
68 | text-align: right;
69 | }
70 |
71 | #content-main {
72 | float: right;
73 | }
74 |
75 | #content-related {
76 | float: left;
77 | margin-left: -300px;
78 | margin-right: auto;
79 | }
80 |
81 | .colMS {
82 | margin-left: 300px;
83 | margin-right: 0;
84 | }
85 |
86 | /* SORTABLE TABLES */
87 |
88 | table thead th.sorted .sortoptions {
89 | float: left;
90 | }
91 |
92 | thead th.sorted .text {
93 | padding-right: 0;
94 | padding-left: 42px;
95 | }
96 |
97 | /* dashboard styles */
98 |
99 | .dashboard .module table td a {
100 | padding-left: .6em;
101 | padding-right: 16px;
102 | }
103 |
104 | /* changelists styles */
105 |
106 | .change-list .filtered table {
107 | border-left: none;
108 | border-right: 0px none;
109 | }
110 |
111 | #changelist-filter {
112 | right: auto;
113 | left: 0;
114 | border-left: none;
115 | border-right: none;
116 | }
117 |
118 | .change-list .filtered .results, .change-list .filtered .paginator, .filtered #toolbar, .filtered div.xfull {
119 | margin-right: 0;
120 | margin-left: 280px;
121 | }
122 |
123 | #changelist-filter li.selected {
124 | border-left: none;
125 | padding-left: 10px;
126 | margin-left: 0;
127 | border-right: 5px solid #eaeaea;
128 | padding-right: 10px;
129 | margin-right: -15px;
130 | }
131 |
132 | .filtered .actions {
133 | margin-left: 280px;
134 | margin-right: 0;
135 | }
136 |
137 | #changelist table tbody td:first-child, #changelist table tbody th:first-child {
138 | border-right: none;
139 | border-left: none;
140 | }
141 |
142 | /* FORMS */
143 |
144 | .aligned label {
145 | padding: 0 0 3px 1em;
146 | float: right;
147 | }
148 |
149 | .submit-row {
150 | text-align: left
151 | }
152 |
153 | .submit-row p.deletelink-box {
154 | float: right;
155 | }
156 |
157 | .submit-row input.default {
158 | margin-left: 0;
159 | }
160 |
161 | .vDateField, .vTimeField {
162 | margin-left: 2px;
163 | }
164 |
165 | .aligned .form-row input {
166 | margin-left: 5px;
167 | }
168 |
169 | form .aligned p.help, form .aligned div.help {
170 | clear: right;
171 | }
172 |
173 | form ul.inline li {
174 | float: right;
175 | padding-right: 0;
176 | padding-left: 7px;
177 | }
178 |
179 | input[type=submit].default, .submit-row input.default {
180 | float: left;
181 | }
182 |
183 | fieldset .field-box {
184 | float: right;
185 | margin-left: 20px;
186 | margin-right: 0;
187 | }
188 |
189 | .errorlist li {
190 | background-position: 100% 12px;
191 | padding: 0;
192 | }
193 |
194 | .errornote {
195 | background-position: 100% 12px;
196 | padding: 10px 12px;
197 | }
198 |
199 | /* WIDGETS */
200 |
201 | .calendarnav-previous {
202 | top: 0;
203 | left: auto;
204 | right: 10px;
205 | }
206 |
207 | .calendarnav-next {
208 | top: 0;
209 | right: auto;
210 | left: 10px;
211 | }
212 |
213 | .calendar caption, .calendarbox h2 {
214 | text-align: center;
215 | }
216 |
217 | .selector {
218 | float: right;
219 | }
220 |
221 | .selector .selector-filter {
222 | text-align: right;
223 | }
224 |
225 | .inline-deletelink {
226 | float: left;
227 | }
228 |
229 | form .form-row p.datetime {
230 | overflow: hidden;
231 | }
232 |
233 | .related-widget-wrapper {
234 | float: right;
235 | }
236 |
237 | /* MISC */
238 |
239 | .inline-related h2, .inline-group h2 {
240 | text-align: right
241 | }
242 |
243 | .inline-related h3 span.delete {
244 | padding-right: 20px;
245 | padding-left: inherit;
246 | left: 10px;
247 | right: inherit;
248 | float:left;
249 | }
250 |
251 | .inline-related h3 span.delete label {
252 | margin-left: inherit;
253 | margin-right: 2px;
254 | }
255 |
256 | /* IE7 specific bug fixes */
257 |
258 | div.colM {
259 | position: relative;
260 | }
261 |
262 | .submit-row input {
263 | float: left;
264 | }
265 |
--------------------------------------------------------------------------------
/static/js/bokeh.json:
--------------------------------------------------------------------------------
1 | ["base","client/connection","client/session","core/bokeh_events","core/build_views","core/dom","core/dom_view","core/enums","core/has_props","core/hittest","core/layout/layout_canvas","core/layout/side_panel","core/layout/solver","core/logging","core/properties","core/property_mixins","core/selection_manager","core/selector","core/settings","core/signaling","core/ui_events","core/util/array","core/util/bbox","core/util/callback","core/util/canvas","core/util/color","core/util/data_structures","core/util/eq","core/util/math","core/util/object","core/util/proj4","core/util/projections","core/util/refs","core/util/selection","core/util/serialization","core/util/spatial","core/util/string","core/util/svg_colors","core/util/templating","core/util/text","core/util/throttle","core/util/types","core/util/wheel","core/util/zoom","core/view","core/visuals","document","embed","main","model","models/annotations/annotation","models/annotations/arrow","models/annotations/arrow_head","models/annotations/band","models/annotations/box_annotation","models/annotations/color_bar","models/annotations/index","models/annotations/label","models/annotations/label_set","models/annotations/legend","models/annotations/legend_item","models/annotations/poly_annotation","models/annotations/span","models/annotations/text_annotation","models/annotations/title","models/annotations/tooltip","models/annotations/whisker","models/axes/axis","models/axes/categorical_axis","models/axes/continuous_axis","models/axes/datetime_axis","models/axes/index","models/axes/linear_axis","models/axes/log_axis","models/callbacks/customjs","models/callbacks/index","models/callbacks/open_url","models/canvas/canvas","models/canvas/cartesian_frame","models/canvas/index","models/expressions/expression","models/expressions/index","models/expressions/stack","models/filters/boolean_filter","models/filters/customjs_filter","models/filters/filter","models/filters/group_filter","models/filters/index","models/filters/index_filter","models/formatters/basic_tick_formatter","models/formatters/categorical_tick_formatter","models/formatters/datetime_tick_formatter","models/formatters/func_tick_formatter","models/formatters/index","models/formatters/log_tick_formatter","models/formatters/mercator_tick_formatter","models/formatters/numeral_tick_formatter","models/formatters/printf_tick_formatter","models/formatters/tick_formatter","models/glyphs/annular_wedge","models/glyphs/annulus","models/glyphs/arc","models/glyphs/bezier","models/glyphs/box","models/glyphs/circle","models/glyphs/ellipse","models/glyphs/glyph","models/glyphs/hbar","models/glyphs/image","models/glyphs/image_rgba","models/glyphs/image_url","models/glyphs/index","models/glyphs/line","models/glyphs/multi_line","models/glyphs/oval","models/glyphs/patch","models/glyphs/patches","models/glyphs/quad","models/glyphs/quadratic","models/glyphs/ray","models/glyphs/rect","models/glyphs/segment","models/glyphs/text","models/glyphs/vbar","models/glyphs/wedge","models/glyphs/xy_glyph","models/graphs/graph_hit_test_policy","models/graphs/index","models/graphs/layout_provider","models/graphs/static_layout_provider","models/grids/grid","models/grids/index","models/index","models/layouts/box","models/layouts/column","models/layouts/index","models/layouts/layout_dom","models/layouts/row","models/layouts/spacer","models/layouts/widget_box","models/mappers/categorical_color_mapper","models/mappers/color_mapper","models/mappers/index","models/mappers/linear_color_mapper","models/mappers/log_color_mapper","models/markers/index","models/markers/marker","models/plots/gmap_plot","models/plots/gmap_plot_canvas","models/plots/index","models/plots/plot","models/plots/plot_canvas","models/ranges/data_range","models/ranges/data_range1d","models/ranges/factor_range","models/ranges/index","models/ranges/range","models/ranges/range1d","models/renderers/glyph_renderer","models/renderers/graph_renderer","models/renderers/guide_renderer","models/renderers/index","models/renderers/renderer","models/scales/categorical_scale","models/scales/index","models/scales/linear_scale","models/scales/log_scale","models/scales/scale","models/sources/ajax_data_source","models/sources/cds_view","models/sources/column_data_source","models/sources/columnar_data_source","models/sources/data_source","models/sources/geojson_data_source","models/sources/index","models/sources/remote_data_source","models/tickers/adaptive_ticker","models/tickers/basic_ticker","models/tickers/categorical_ticker","models/tickers/composite_ticker","models/tickers/continuous_ticker","models/tickers/datetime_ticker","models/tickers/days_ticker","models/tickers/fixed_ticker","models/tickers/index","models/tickers/log_ticker","models/tickers/mercator_ticker","models/tickers/months_ticker","models/tickers/single_interval_ticker","models/tickers/ticker","models/tickers/util","models/tickers/years_ticker","models/tiles/bbox_tile_source","models/tiles/dynamic_image_renderer","models/tiles/image_pool","models/tiles/image_source","models/tiles/index","models/tiles/mercator_tile_source","models/tiles/quadkey_tile_source","models/tiles/tile_renderer","models/tiles/tile_source","models/tiles/tile_utils","models/tiles/tms_tile_source","models/tiles/wmts_tile_source","models/tools/actions/action_tool","models/tools/actions/help_tool","models/tools/actions/redo_tool","models/tools/actions/reset_tool","models/tools/actions/save_tool","models/tools/actions/undo_tool","models/tools/actions/zoom_in_tool","models/tools/actions/zoom_out_tool","models/tools/button_tool","models/tools/gestures/box_select_tool","models/tools/gestures/box_zoom_tool","models/tools/gestures/gesture_tool","models/tools/gestures/lasso_select_tool","models/tools/gestures/pan_tool","models/tools/gestures/poly_select_tool","models/tools/gestures/select_tool","models/tools/gestures/tap_tool","models/tools/gestures/wheel_pan_tool","models/tools/gestures/wheel_zoom_tool","models/tools/index","models/tools/inspectors/crosshair_tool","models/tools/inspectors/hover_tool","models/tools/inspectors/inspect_tool","models/tools/on_off_button","models/tools/tool","models/tools/tool_proxy","models/tools/toolbar","models/tools/toolbar_base","models/tools/toolbar_box","models/transforms/customjs_transform","models/transforms/dodge","models/transforms/index","models/transforms/interpolator","models/transforms/jitter","models/transforms/linear_interpolator","models/transforms/step_interpolator","models/transforms/transform","polyfill","protocol/message","protocol/receiver","safely","version"]
--------------------------------------------------------------------------------
/static/admin/js/SelectBox.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | 'use strict';
3 | var SelectBox = {
4 | cache: {},
5 | init: function(id) {
6 | var box = document.getElementById(id);
7 | var node;
8 | SelectBox.cache[id] = [];
9 | var cache = SelectBox.cache[id];
10 | var boxOptions = box.options;
11 | var boxOptionsLength = boxOptions.length;
12 | for (var i = 0, j = boxOptionsLength; i < j; i++) {
13 | node = boxOptions[i];
14 | cache.push({value: node.value, text: node.text, displayed: 1});
15 | }
16 | },
17 | redisplay: function(id) {
18 | // Repopulate HTML select box from cache
19 | var box = document.getElementById(id);
20 | var node;
21 | $(box).empty(); // clear all options
22 | var new_options = box.outerHTML.slice(0, -9); // grab just the opening tag
23 | var cache = SelectBox.cache[id];
24 | for (var i = 0, j = cache.length; i < j; i++) {
25 | node = cache[i];
26 | if (node.displayed) {
27 | var new_option = new Option(node.text, node.value, false, false);
28 | // Shows a tooltip when hovering over the option
29 | new_option.setAttribute("title", node.text);
30 | new_options += new_option.outerHTML;
31 | }
32 | }
33 | new_options += '';
34 | box.outerHTML = new_options;
35 | },
36 | filter: function(id, text) {
37 | // Redisplay the HTML select box, displaying only the choices containing ALL
38 | // the words in text. (It's an AND search.)
39 | var tokens = text.toLowerCase().split(/\s+/);
40 | var node, token;
41 | var cache = SelectBox.cache[id];
42 | for (var i = 0, j = cache.length; i < j; i++) {
43 | node = cache[i];
44 | node.displayed = 1;
45 | var node_text = node.text.toLowerCase();
46 | var numTokens = tokens.length;
47 | for (var k = 0; k < numTokens; k++) {
48 | token = tokens[k];
49 | if (node_text.indexOf(token) === -1) {
50 | node.displayed = 0;
51 | break; // Once the first token isn't found we're done
52 | }
53 | }
54 | }
55 | SelectBox.redisplay(id);
56 | },
57 | delete_from_cache: function(id, value) {
58 | var node, delete_index = null;
59 | var cache = SelectBox.cache[id];
60 | for (var i = 0, j = cache.length; i < j; i++) {
61 | node = cache[i];
62 | if (node.value === value) {
63 | delete_index = i;
64 | break;
65 | }
66 | }
67 | cache.splice(delete_index, 1);
68 | },
69 | add_to_cache: function(id, option) {
70 | SelectBox.cache[id].push({value: option.value, text: option.text, displayed: 1});
71 | },
72 | cache_contains: function(id, value) {
73 | // Check if an item is contained in the cache
74 | var node;
75 | var cache = SelectBox.cache[id];
76 | for (var i = 0, j = cache.length; i < j; i++) {
77 | node = cache[i];
78 | if (node.value === value) {
79 | return true;
80 | }
81 | }
82 | return false;
83 | },
84 | move: function(from, to) {
85 | var from_box = document.getElementById(from);
86 | var option;
87 | var boxOptions = from_box.options;
88 | var boxOptionsLength = boxOptions.length;
89 | for (var i = 0, j = boxOptionsLength; i < j; i++) {
90 | option = boxOptions[i];
91 | var option_value = option.value;
92 | if (option.selected && SelectBox.cache_contains(from, option_value)) {
93 | SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1});
94 | SelectBox.delete_from_cache(from, option_value);
95 | }
96 | }
97 | SelectBox.redisplay(from);
98 | SelectBox.redisplay(to);
99 | },
100 | move_all: function(from, to) {
101 | var from_box = document.getElementById(from);
102 | var option;
103 | var boxOptions = from_box.options;
104 | var boxOptionsLength = boxOptions.length;
105 | for (var i = 0, j = boxOptionsLength; i < j; i++) {
106 | option = boxOptions[i];
107 | var option_value = option.value;
108 | if (SelectBox.cache_contains(from, option_value)) {
109 | SelectBox.add_to_cache(to, {value: option_value, text: option.text, displayed: 1});
110 | SelectBox.delete_from_cache(from, option_value);
111 | }
112 | }
113 | SelectBox.redisplay(from);
114 | SelectBox.redisplay(to);
115 | },
116 | sort: function(id) {
117 | SelectBox.cache[id].sort(function(a, b) {
118 | a = a.text.toLowerCase();
119 | b = b.text.toLowerCase();
120 | try {
121 | if (a > b) {
122 | return 1;
123 | }
124 | if (a < b) {
125 | return -1;
126 | }
127 | }
128 | catch (e) {
129 | // silently fail on IE 'unknown' exception
130 | }
131 | return 0;
132 | } );
133 | },
134 | select_all: function(id) {
135 | var box = document.getElementById(id);
136 | var boxOptions = box.options;
137 | var boxOptionsLength = boxOptions.length;
138 | for (var i = 0; i < boxOptionsLength; i++) {
139 | boxOptions[i].selected = 'selected';
140 | }
141 | }
142 | };
143 | window.SelectBox = SelectBox;
144 | })(django.jQuery);
145 |
--------------------------------------------------------------------------------
/documentation/Linode_Instructions.txt:
--------------------------------------------------------------------------------
1 | ########################################
2 | # Secure the Server
3 | ########################################
4 |
5 | # If you decide to host this via Linode and don't already have a Linode account,
6 | # please consider using the following referral link to register. This gives me a modest amount of Linode "credit"
7 | # that will allow me to keep the demo site up and running at http://45.33.6.39
8 |
9 | # https://www.linode.com/?r=8b4f4ca1bfe379f086fbacc8dde3a5c138296e48
10 | # referral code: 8b4f4ca1bfe379f086fbacc8dde3a5c138296e48
11 |
12 | # Rebuild as Ubuntu 16.04 LTS
13 | # Boot
14 | # Log in as root
15 |
16 | # update the machine
17 | apt-get update
18 | apt-get upgrade
19 | apt-get install fail2ban
20 | vi /etc/ssh/sshd_config
21 | PermitRootLogin no
22 | PasswordAuthentication no
23 | UsePAM no
24 | apt-get install git-core
25 | apt-get install virtualenv
26 | apt-get install gunicorn
27 | apt-get install nginx
28 |
29 | ########################################
30 | # Setting up the user
31 | ########################################/
32 |
33 | # create the new deployers group
34 | /usr/sbin/groupadd deployers
35 | # back up the sudoers file
36 | cp /etc/sudoers /etc/sudoers-backup
37 | # modify the sudo list so the deployers group has sudo privileges
38 | (cat /etc/sudoers-backup ; echo "%deployers ALL=(ALL) ALL") > /etc/sudoers
39 | # ensure the appropriate permissions are on the sudoers file
40 | chmod 0440 /etc/sudoers
41 |
42 | # create the new user. be sure to use your own name here
43 | /usr/sbin/useradd -c "Jonathan Bennett" -m -g deployers deployer1
44 | # set up a password for the new user. you'll need the password to run sudo commands
45 | /usr/bin/passwd deployer1
46 |
47 | # add the deployer user to the deployers group
48 | /usr/sbin/usermod -a -G deployers deployer1
49 | # create a directory for the deployer's public key and authorized_keys file
50 | mkdir /home/deployer1/.ssh
51 |
52 | # create a public/private key pair on your local computer
53 | # create the authorized_keys file (the public key) on your webserver
54 | vi /home/deployer1/.ssh/authorized_keys
55 |
56 | # change the owner and group of the .ssh directory to deployer
57 | # and deployers, respectively
58 | chown -R deployer1 /home/deployer1/.ssh
59 | chgrp -R deployers /home/deployer1/.ssh
60 |
61 | # restart SSH Service
62 | service ssh reload
63 |
64 | # before logging out as root, check to make sure you can log in as deployer
65 | # if it works log out of the root SSH session
66 |
67 | # from now on, log in as deployer1
68 |
69 | # create a deploy key
70 | mkdir ~/deploy_key
71 | cd ~/deploy_key
72 | ssh-keygen -t rsa -b 2048
73 |
74 | # save the private key in our current directory ./deploy_key
75 | # Press enter twice when prompted for a passphrase. We will not use a passphrase on the deploy key.
76 |
77 | # add the deploy_key.pub to github
78 |
79 |
80 | ########################################
81 | # Source Control
82 | ########################################
83 |
84 | # make sure the deploy key is set up on your github repository
85 | cd /home/deployer1
86 | # the next line must point to your personal repository, so replace jonolsu with your github ID
87 | ssh-agent bash -c 'ssh-add /home/deployer1/deploy_key/deploy_key; git clone git@github.com:jonolsu/BokehDjango.git'
88 |
89 |
90 | ########################################
91 | # Create the Environment
92 | ########################################
93 |
94 | mkdir /home/deployer1/envs/
95 | # determine which version of python3 is installed and name the environment appropriately
96 | python3 --version
97 | virtualenv -p python3 /home/deployer1/envs/BokehDjango352
98 | /bin/bash
99 | source /home/deployer1/envs/BokehDjango352/bin/activate
100 | pip install -r /home/deployer1/BokehDjango/documentation/config_files/requirements.txt
101 |
102 |
103 | ########################################
104 | # Setting up the Web Server
105 | # https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-16-04
106 | ########################################
107 |
108 | # set up the firewall
109 | sudo ufw allow 22
110 | sudo ufw allow 80
111 | sudo ufw enable
112 |
113 |
114 | # create gunicorn.service file (gunicorn.service is located in the config_files directory of the repository)
115 | sudo vi /etc/systemd/system/gunicorn.service
116 |
117 | # start the Gunicorn service we created and enable it so that it starts at boot:
118 | sudo systemctl start gunicorn
119 | sudo systemctl enable gunicorn
120 |
121 | # Configure Nginx to Proxy Pass to Gunicorn (mysite is located in the config_files directory of the repository)
122 | # replace the up address with your server's ip address
123 | sudo vi /etc/nginx/sites-available/mysite
124 |
125 | # enable the file
126 | sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled
127 |
128 | #
129 | sudo ufw allow 'Nginx Full'
130 |
131 | # restart Nginx
132 | sudo systemctl restart nginx
133 |
134 |
135 | ########################################
136 | # Configuring the Bokeh Server
137 | ########################################
138 |
139 | # create bokehserver.service file (bokehserver.service is located in the config_files directory of the repository)
140 | sudo vi /etc/systemd/system/bokehserver.service
141 |
142 | # start the bokehserver service we created and enable it so that it starts at boot:
143 | sudo systemctl start bokehserver
144 | sudo systemctl enable bokehserver
145 |
146 | ########################################
147 | # Ready to Go
148 | ########################################
149 |
150 | # reboot your system and navigate to your website
151 | sudo shutdown -r now
152 | # use the browser to navigate to your website http://45.33.6.39
153 | # userid is regularuser
154 | # password is 1234password1234
155 | # administrator id is admin
156 | # password is 1234password1234
157 |
158 | ########################################
159 | # Other
160 | ########################################
161 |
162 | # if you update your Django Application
163 | cd /home/deployer1/BokehDjango
164 | ssh-agent bash -c 'ssh-add /home/deployer1/deploy_key/deploy_key; git pull origin master'
165 | sudo systemctl restart gunicorn
166 |
167 | # if you change gunicorn service
168 | sudo systemctl daemon-reload
169 | sudo systemctl restart gunicorn
170 |
171 | # if you change bokehserver service
172 | sudo systemctl daemon-reload
173 | sudo systemctl restart bokehserver
174 |
175 | # if you change nginx
176 | sudo nginx -t && sudo systemctl restart nginx
177 |
--------------------------------------------------------------------------------
/static/admin/js/actions.js:
--------------------------------------------------------------------------------
1 | /*global gettext, interpolate, ngettext*/
2 | (function($) {
3 | 'use strict';
4 | var lastChecked;
5 |
6 | $.fn.actions = function(opts) {
7 | var options = $.extend({}, $.fn.actions.defaults, opts);
8 | var actionCheckboxes = $(this);
9 | var list_editable_changed = false;
10 | var showQuestion = function() {
11 | $(options.acrossClears).hide();
12 | $(options.acrossQuestions).show();
13 | $(options.allContainer).hide();
14 | },
15 | showClear = function() {
16 | $(options.acrossClears).show();
17 | $(options.acrossQuestions).hide();
18 | $(options.actionContainer).toggleClass(options.selectedClass);
19 | $(options.allContainer).show();
20 | $(options.counterContainer).hide();
21 | },
22 | reset = function() {
23 | $(options.acrossClears).hide();
24 | $(options.acrossQuestions).hide();
25 | $(options.allContainer).hide();
26 | $(options.counterContainer).show();
27 | },
28 | clearAcross = function() {
29 | reset();
30 | $(options.acrossInput).val(0);
31 | $(options.actionContainer).removeClass(options.selectedClass);
32 | },
33 | checker = function(checked) {
34 | if (checked) {
35 | showQuestion();
36 | } else {
37 | reset();
38 | }
39 | $(actionCheckboxes).prop("checked", checked)
40 | .parent().parent().toggleClass(options.selectedClass, checked);
41 | },
42 | updateCounter = function() {
43 | var sel = $(actionCheckboxes).filter(":checked").length;
44 | // data-actions-icnt is defined in the generated HTML
45 | // and contains the total amount of objects in the queryset
46 | var actions_icnt = $('.action-counter').data('actionsIcnt');
47 | $(options.counterContainer).html(interpolate(
48 | ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
49 | sel: sel,
50 | cnt: actions_icnt
51 | }, true));
52 | $(options.allToggle).prop("checked", function() {
53 | var value;
54 | if (sel === actionCheckboxes.length) {
55 | value = true;
56 | showQuestion();
57 | } else {
58 | value = false;
59 | clearAcross();
60 | }
61 | return value;
62 | });
63 | };
64 | // Show counter by default
65 | $(options.counterContainer).show();
66 | // Check state of checkboxes and reinit state if needed
67 | $(this).filter(":checked").each(function(i) {
68 | $(this).parent().parent().toggleClass(options.selectedClass);
69 | updateCounter();
70 | if ($(options.acrossInput).val() === 1) {
71 | showClear();
72 | }
73 | });
74 | $(options.allToggle).show().click(function() {
75 | checker($(this).prop("checked"));
76 | updateCounter();
77 | });
78 | $("a", options.acrossQuestions).click(function(event) {
79 | event.preventDefault();
80 | $(options.acrossInput).val(1);
81 | showClear();
82 | });
83 | $("a", options.acrossClears).click(function(event) {
84 | event.preventDefault();
85 | $(options.allToggle).prop("checked", false);
86 | clearAcross();
87 | checker(0);
88 | updateCounter();
89 | });
90 | lastChecked = null;
91 | $(actionCheckboxes).click(function(event) {
92 | if (!event) { event = window.event; }
93 | var target = event.target ? event.target : event.srcElement;
94 | if (lastChecked && $.data(lastChecked) !== $.data(target) && event.shiftKey === true) {
95 | var inrange = false;
96 | $(lastChecked).prop("checked", target.checked)
97 | .parent().parent().toggleClass(options.selectedClass, target.checked);
98 | $(actionCheckboxes).each(function() {
99 | if ($.data(this) === $.data(lastChecked) || $.data(this) === $.data(target)) {
100 | inrange = (inrange) ? false : true;
101 | }
102 | if (inrange) {
103 | $(this).prop("checked", target.checked)
104 | .parent().parent().toggleClass(options.selectedClass, target.checked);
105 | }
106 | });
107 | }
108 | $(target).parent().parent().toggleClass(options.selectedClass, target.checked);
109 | lastChecked = target;
110 | updateCounter();
111 | });
112 | $('form#changelist-form table#result_list tr').find('td:gt(0) :input').change(function() {
113 | list_editable_changed = true;
114 | });
115 | $('form#changelist-form button[name="index"]').click(function(event) {
116 | if (list_editable_changed) {
117 | return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
118 | }
119 | });
120 | $('form#changelist-form input[name="_save"]').click(function(event) {
121 | var action_changed = false;
122 | $('select option:selected', options.actionContainer).each(function() {
123 | if ($(this).val()) {
124 | action_changed = true;
125 | }
126 | });
127 | if (action_changed) {
128 | if (list_editable_changed) {
129 | return confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action."));
130 | } else {
131 | return confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."));
132 | }
133 | }
134 | });
135 | };
136 | /* Setup plugin defaults */
137 | $.fn.actions.defaults = {
138 | actionContainer: "div.actions",
139 | counterContainer: "span.action-counter",
140 | allContainer: "div.actions span.all",
141 | acrossInput: "div.actions input.select-across",
142 | acrossQuestions: "div.actions span.question",
143 | acrossClears: "div.actions span.clear",
144 | allToggle: "#action-toggle",
145 | selectedClass: "selected"
146 | };
147 | $(document).ready(function() {
148 | var $actionsEls = $('tr input.action-select');
149 | if ($actionsEls.length > 0) {
150 | $actionsEls.actions();
151 | }
152 | });
153 | })(django.jQuery);
154 |
--------------------------------------------------------------------------------
/static/admin/js/admin/RelatedObjectLookups.js:
--------------------------------------------------------------------------------
1 | /*global SelectBox, interpolate*/
2 | // Handles related-objects functionality: lookup link for raw_id_fields
3 | // and Add Another links.
4 |
5 | (function($) {
6 | 'use strict';
7 |
8 | // IE doesn't accept periods or dashes in the window name, but the element IDs
9 | // we use to generate popup window names may contain them, therefore we map them
10 | // to allowed characters in a reversible way so that we can locate the correct
11 | // element when the popup window is dismissed.
12 | function id_to_windowname(text) {
13 | text = text.replace(/\./g, '__dot__');
14 | text = text.replace(/\-/g, '__dash__');
15 | return text;
16 | }
17 |
18 | function windowname_to_id(text) {
19 | text = text.replace(/__dot__/g, '.');
20 | text = text.replace(/__dash__/g, '-');
21 | return text;
22 | }
23 |
24 | function showAdminPopup(triggeringLink, name_regexp, add_popup) {
25 | var name = triggeringLink.id.replace(name_regexp, '');
26 | name = id_to_windowname(name);
27 | var href = triggeringLink.href;
28 | if (add_popup) {
29 | if (href.indexOf('?') === -1) {
30 | href += '?_popup=1';
31 | } else {
32 | href += '&_popup=1';
33 | }
34 | }
35 | var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
36 | win.focus();
37 | return false;
38 | }
39 |
40 | function showRelatedObjectLookupPopup(triggeringLink) {
41 | return showAdminPopup(triggeringLink, /^lookup_/, true);
42 | }
43 |
44 | function dismissRelatedLookupPopup(win, chosenId) {
45 | var name = windowname_to_id(win.name);
46 | var elem = document.getElementById(name);
47 | if (elem.className.indexOf('vManyToManyRawIdAdminField') !== -1 && elem.value) {
48 | elem.value += ',' + chosenId;
49 | } else {
50 | document.getElementById(name).value = chosenId;
51 | }
52 | win.close();
53 | }
54 |
55 | function showRelatedObjectPopup(triggeringLink) {
56 | return showAdminPopup(triggeringLink, /^(change|add|delete)_/, false);
57 | }
58 |
59 | function updateRelatedObjectLinks(triggeringLink) {
60 | var $this = $(triggeringLink);
61 | var siblings = $this.nextAll('.change-related, .delete-related');
62 | if (!siblings.length) {
63 | return;
64 | }
65 | var value = $this.val();
66 | if (value) {
67 | siblings.each(function() {
68 | var elm = $(this);
69 | elm.attr('href', elm.attr('data-href-template').replace('__fk__', value));
70 | });
71 | } else {
72 | siblings.removeAttr('href');
73 | }
74 | }
75 |
76 | function dismissAddRelatedObjectPopup(win, newId, newRepr) {
77 | var name = windowname_to_id(win.name);
78 | var elem = document.getElementById(name);
79 | if (elem) {
80 | var elemName = elem.nodeName.toUpperCase();
81 | if (elemName === 'SELECT') {
82 | elem.options[elem.options.length] = new Option(newRepr, newId, true, true);
83 | } else if (elemName === 'INPUT') {
84 | if (elem.className.indexOf('vManyToManyRawIdAdminField') !== -1 && elem.value) {
85 | elem.value += ',' + newId;
86 | } else {
87 | elem.value = newId;
88 | }
89 | }
90 | // Trigger a change event to update related links if required.
91 | $(elem).trigger('change');
92 | } else {
93 | var toId = name + "_to";
94 | var o = new Option(newRepr, newId);
95 | SelectBox.add_to_cache(toId, o);
96 | SelectBox.redisplay(toId);
97 | }
98 | win.close();
99 | }
100 |
101 | function dismissChangeRelatedObjectPopup(win, objId, newRepr, newId) {
102 | var id = windowname_to_id(win.name).replace(/^edit_/, '');
103 | var selectsSelector = interpolate('#%s, #%s_from, #%s_to', [id, id, id]);
104 | var selects = $(selectsSelector);
105 | selects.find('option').each(function() {
106 | if (this.value === objId) {
107 | this.textContent = newRepr;
108 | this.value = newId;
109 | }
110 | });
111 | win.close();
112 | }
113 |
114 | function dismissDeleteRelatedObjectPopup(win, objId) {
115 | var id = windowname_to_id(win.name).replace(/^delete_/, '');
116 | var selectsSelector = interpolate('#%s, #%s_from, #%s_to', [id, id, id]);
117 | var selects = $(selectsSelector);
118 | selects.find('option').each(function() {
119 | if (this.value === objId) {
120 | $(this).remove();
121 | }
122 | }).trigger('change');
123 | win.close();
124 | }
125 |
126 | // Global for testing purposes
127 | window.id_to_windowname = id_to_windowname;
128 | window.windowname_to_id = windowname_to_id;
129 |
130 | window.showRelatedObjectLookupPopup = showRelatedObjectLookupPopup;
131 | window.dismissRelatedLookupPopup = dismissRelatedLookupPopup;
132 | window.showRelatedObjectPopup = showRelatedObjectPopup;
133 | window.updateRelatedObjectLinks = updateRelatedObjectLinks;
134 | window.dismissAddRelatedObjectPopup = dismissAddRelatedObjectPopup;
135 | window.dismissChangeRelatedObjectPopup = dismissChangeRelatedObjectPopup;
136 | window.dismissDeleteRelatedObjectPopup = dismissDeleteRelatedObjectPopup;
137 |
138 | // Kept for backward compatibility
139 | window.showAddAnotherPopup = showRelatedObjectPopup;
140 | window.dismissAddAnotherPopup = dismissAddRelatedObjectPopup;
141 |
142 | $(document).ready(function() {
143 | $("a[data-popup-opener]").click(function(event) {
144 | event.preventDefault();
145 | opener.dismissRelatedLookupPopup(window, $(this).data("popup-opener"));
146 | });
147 | $('body').on('click', '.related-widget-wrapper-link', function(e) {
148 | e.preventDefault();
149 | if (this.href) {
150 | var event = $.Event('django:show-related', {href: this.href});
151 | $(this).trigger(event);
152 | if (!event.isDefaultPrevented()) {
153 | showRelatedObjectPopup(this);
154 | }
155 | }
156 | });
157 | $('body').on('change', '.related-widget-wrapper select', function(e) {
158 | var event = $.Event('django:update-related');
159 | $(this).trigger(event);
160 | if (!event.isDefaultPrevented()) {
161 | updateRelatedObjectLinks(this);
162 | }
163 | });
164 | $('.related-widget-wrapper select').trigger('change');
165 | $('body').on('click', '.related-lookup', function(e) {
166 | e.preventDefault();
167 | var event = $.Event('django:lookup-related');
168 | $(this).trigger(event);
169 | if (!event.isDefaultPrevented()) {
170 | showRelatedObjectLookupPopup(this);
171 | }
172 | });
173 | });
174 |
175 | })(django.jQuery);
176 |
--------------------------------------------------------------------------------
/static/admin/css/changelists.css:
--------------------------------------------------------------------------------
1 | /* CHANGELISTS */
2 |
3 | #changelist {
4 | position: relative;
5 | width: 100%;
6 | }
7 |
8 | #changelist table {
9 | width: 100%;
10 | }
11 |
12 | .change-list .hiddenfields { display:none; }
13 |
14 | .change-list .filtered table {
15 | border-right: none;
16 | }
17 |
18 | .change-list .filtered {
19 | min-height: 400px;
20 | }
21 |
22 | .change-list .filtered .results, .change-list .filtered .paginator,
23 | .filtered #toolbar, .filtered div.xfull {
24 | margin-right: 280px;
25 | width: auto;
26 | }
27 |
28 | .change-list .filtered table tbody th {
29 | padding-right: 1em;
30 | }
31 |
32 | #changelist-form .results {
33 | overflow-x: auto;
34 | }
35 |
36 | #changelist .toplinks {
37 | border-bottom: 1px solid #ddd;
38 | }
39 |
40 | #changelist .paginator {
41 | color: #666;
42 | border-bottom: 1px solid #eee;
43 | background: #fff;
44 | overflow: hidden;
45 | }
46 |
47 | /* CHANGELIST TABLES */
48 |
49 | #changelist table thead th {
50 | padding: 0;
51 | white-space: nowrap;
52 | vertical-align: middle;
53 | }
54 |
55 | #changelist table thead th.action-checkbox-column {
56 | width: 1.5em;
57 | text-align: center;
58 | }
59 |
60 | #changelist table tbody td.action-checkbox {
61 | text-align: center;
62 | }
63 |
64 | #changelist table tfoot {
65 | color: #666;
66 | }
67 |
68 | /* TOOLBAR */
69 |
70 | #changelist #toolbar {
71 | padding: 8px 10px;
72 | margin-bottom: 15px;
73 | border-top: 1px solid #eee;
74 | border-bottom: 1px solid #eee;
75 | background: #f8f8f8;
76 | color: #666;
77 | }
78 |
79 | #changelist #toolbar form input {
80 | border-radius: 4px;
81 | font-size: 14px;
82 | padding: 5px;
83 | color: #333;
84 | }
85 |
86 | #changelist #toolbar form #searchbar {
87 | height: 19px;
88 | border: 1px solid #ccc;
89 | padding: 2px 5px;
90 | margin: 0;
91 | vertical-align: top;
92 | font-size: 13px;
93 | }
94 |
95 | #changelist #toolbar form #searchbar:focus {
96 | border-color: #999;
97 | }
98 |
99 | #changelist #toolbar form input[type="submit"] {
100 | border: 1px solid #ccc;
101 | padding: 2px 10px;
102 | margin: 0;
103 | vertical-align: middle;
104 | background: #fff;
105 | box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
106 | cursor: pointer;
107 | color: #333;
108 | }
109 |
110 | #changelist #toolbar form input[type="submit"]:focus,
111 | #changelist #toolbar form input[type="submit"]:hover {
112 | border-color: #999;
113 | }
114 |
115 | #changelist #changelist-search img {
116 | vertical-align: middle;
117 | margin-right: 4px;
118 | }
119 |
120 | /* FILTER COLUMN */
121 |
122 | #changelist-filter {
123 | position: absolute;
124 | top: 0;
125 | right: 0;
126 | z-index: 1000;
127 | width: 240px;
128 | background: #f8f8f8;
129 | border-left: none;
130 | margin: 0;
131 | }
132 |
133 | #changelist-filter h2 {
134 | font-size: 14px;
135 | text-transform: uppercase;
136 | letter-spacing: 0.5px;
137 | padding: 5px 15px;
138 | margin-bottom: 12px;
139 | border-bottom: none;
140 | }
141 |
142 | #changelist-filter h3 {
143 | font-weight: 400;
144 | font-size: 14px;
145 | padding: 0 15px;
146 | margin-bottom: 10px;
147 | }
148 |
149 | #changelist-filter ul {
150 | margin: 5px 0;
151 | padding: 0 15px 15px;
152 | border-bottom: 1px solid #eaeaea;
153 | }
154 |
155 | #changelist-filter ul:last-child {
156 | border-bottom: none;
157 | padding-bottom: none;
158 | }
159 |
160 | #changelist-filter li {
161 | list-style-type: none;
162 | margin-left: 0;
163 | padding-left: 0;
164 | }
165 |
166 | #changelist-filter a {
167 | display: block;
168 | color: #999;
169 | text-overflow: ellipsis;
170 | overflow-x: hidden;
171 | }
172 |
173 | #changelist-filter li.selected {
174 | border-left: 5px solid #eaeaea;
175 | padding-left: 10px;
176 | margin-left: -15px;
177 | }
178 |
179 | #changelist-filter li.selected a {
180 | color: #5b80b2;
181 | }
182 |
183 | #changelist-filter a:focus, #changelist-filter a:hover,
184 | #changelist-filter li.selected a:focus,
185 | #changelist-filter li.selected a:hover {
186 | color: #036;
187 | }
188 |
189 | /* DATE DRILLDOWN */
190 |
191 | .change-list ul.toplinks {
192 | display: block;
193 | float: left;
194 | padding: 0;
195 | margin: 0;
196 | width: 100%;
197 | }
198 |
199 | .change-list ul.toplinks li {
200 | padding: 3px 6px;
201 | font-weight: bold;
202 | list-style-type: none;
203 | display: inline-block;
204 | }
205 |
206 | .change-list ul.toplinks .date-back a {
207 | color: #999;
208 | }
209 |
210 | .change-list ul.toplinks .date-back a:focus,
211 | .change-list ul.toplinks .date-back a:hover {
212 | color: #036;
213 | }
214 |
215 | /* PAGINATOR */
216 |
217 | .paginator {
218 | font-size: 13px;
219 | padding-top: 10px;
220 | padding-bottom: 10px;
221 | line-height: 22px;
222 | margin: 0;
223 | border-top: 1px solid #ddd;
224 | }
225 |
226 | .paginator a:link, .paginator a:visited {
227 | padding: 2px 6px;
228 | background: #79aec8;
229 | text-decoration: none;
230 | color: #fff;
231 | }
232 |
233 | .paginator a.showall {
234 | padding: 0;
235 | border: none;
236 | background: none;
237 | color: #5b80b2;
238 | }
239 |
240 | .paginator a.showall:focus, .paginator a.showall:hover {
241 | background: none;
242 | color: #036;
243 | }
244 |
245 | .paginator .end {
246 | margin-right: 6px;
247 | }
248 |
249 | .paginator .this-page {
250 | padding: 2px 6px;
251 | font-weight: bold;
252 | font-size: 13px;
253 | vertical-align: top;
254 | }
255 |
256 | .paginator a:focus, .paginator a:hover {
257 | color: white;
258 | background: #036;
259 | }
260 |
261 | /* ACTIONS */
262 |
263 | .filtered .actions {
264 | margin-right: 280px;
265 | border-right: none;
266 | }
267 |
268 | #changelist table input {
269 | margin: 0;
270 | vertical-align: baseline;
271 | }
272 |
273 | #changelist table tbody tr.selected {
274 | background-color: #FFFFCC;
275 | }
276 |
277 | #changelist .actions {
278 | padding: 10px;
279 | background: #fff;
280 | border-top: none;
281 | border-bottom: none;
282 | line-height: 24px;
283 | color: #999;
284 | }
285 |
286 | #changelist .actions.selected {
287 | background: #fffccf;
288 | border-top: 1px solid #fffee8;
289 | border-bottom: 1px solid #edecd6;
290 | }
291 |
292 | #changelist .actions span.all,
293 | #changelist .actions span.action-counter,
294 | #changelist .actions span.clear,
295 | #changelist .actions span.question {
296 | font-size: 13px;
297 | margin: 0 0.5em;
298 | display: none;
299 | }
300 |
301 | #changelist .actions:last-child {
302 | border-bottom: none;
303 | }
304 |
305 | #changelist .actions select {
306 | vertical-align: top;
307 | height: 24px;
308 | background: none;
309 | color: #000;
310 | border: 1px solid #ccc;
311 | border-radius: 4px;
312 | font-size: 14px;
313 | padding: 0 0 0 4px;
314 | margin: 0;
315 | margin-left: 10px;
316 | }
317 |
318 | #changelist .actions select:focus {
319 | border-color: #999;
320 | }
321 |
322 | #changelist .actions label {
323 | display: inline-block;
324 | vertical-align: middle;
325 | font-size: 13px;
326 | }
327 |
328 | #changelist .actions .button {
329 | font-size: 13px;
330 | border: 1px solid #ccc;
331 | border-radius: 4px;
332 | background: #fff;
333 | box-shadow: 0 -15px 20px -10px rgba(0, 0, 0, 0.15) inset;
334 | cursor: pointer;
335 | height: 24px;
336 | line-height: 1;
337 | padding: 4px 8px;
338 | margin: 0;
339 | color: #333;
340 | }
341 |
342 | #changelist .actions .button:focus, #changelist .actions .button:hover {
343 | border-color: #999;
344 | }
345 |
--------------------------------------------------------------------------------
/static/admin/js/urlify.js:
--------------------------------------------------------------------------------
1 | /*global XRegExp*/
2 | (function() {
3 | 'use strict';
4 |
5 | var LATIN_MAP = {
6 | 'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE',
7 | 'Ç': 'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I',
8 | 'Î': 'I', 'Ï': 'I', 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O',
9 | 'Õ': 'O', 'Ö': 'O', 'Ő': 'O', 'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U',
10 | 'Ü': 'U', 'Ű': 'U', 'Ý': 'Y', 'Þ': 'TH', 'Ÿ': 'Y', 'ß': 'ss', 'à': 'a',
11 | 'á': 'a', 'â': 'a', 'ã': 'a', 'ä': 'a', 'å': 'a', 'æ': 'ae', 'ç': 'c',
12 | 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e', 'ì': 'i', 'í': 'i', 'î': 'i',
13 | 'ï': 'i', 'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o',
14 | 'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 'ú': 'u', 'û': 'u', 'ü': 'u',
15 | 'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y'
16 | };
17 | var LATIN_SYMBOLS_MAP = {
18 | '©': '(c)'
19 | };
20 | var GREEK_MAP = {
21 | 'α': 'a', 'β': 'b', 'γ': 'g', 'δ': 'd', 'ε': 'e', 'ζ': 'z', 'η': 'h',
22 | 'θ': '8', 'ι': 'i', 'κ': 'k', 'λ': 'l', 'μ': 'm', 'ν': 'n', 'ξ': '3',
23 | 'ο': 'o', 'π': 'p', 'ρ': 'r', 'σ': 's', 'τ': 't', 'υ': 'y', 'φ': 'f',
24 | 'χ': 'x', 'ψ': 'ps', 'ω': 'w', 'ά': 'a', 'έ': 'e', 'ί': 'i', 'ό': 'o',
25 | 'ύ': 'y', 'ή': 'h', 'ώ': 'w', 'ς': 's', 'ϊ': 'i', 'ΰ': 'y', 'ϋ': 'y',
26 | 'ΐ': 'i', 'Α': 'A', 'Β': 'B', 'Γ': 'G', 'Δ': 'D', 'Ε': 'E', 'Ζ': 'Z',
27 | 'Η': 'H', 'Θ': '8', 'Ι': 'I', 'Κ': 'K', 'Λ': 'L', 'Μ': 'M', 'Ν': 'N',
28 | 'Ξ': '3', 'Ο': 'O', 'Π': 'P', 'Ρ': 'R', 'Σ': 'S', 'Τ': 'T', 'Υ': 'Y',
29 | 'Φ': 'F', 'Χ': 'X', 'Ψ': 'PS', 'Ω': 'W', 'Ά': 'A', 'Έ': 'E', 'Ί': 'I',
30 | 'Ό': 'O', 'Ύ': 'Y', 'Ή': 'H', 'Ώ': 'W', 'Ϊ': 'I', 'Ϋ': 'Y'
31 | };
32 | var TURKISH_MAP = {
33 | 'ş': 's', 'Ş': 'S', 'ı': 'i', 'İ': 'I', 'ç': 'c', 'Ç': 'C', 'ü': 'u',
34 | 'Ü': 'U', 'ö': 'o', 'Ö': 'O', 'ğ': 'g', 'Ğ': 'G'
35 | };
36 | var ROMANIAN_MAP = {
37 | 'ă': 'a', 'î': 'i', 'ș': 's', 'ț': 't', 'â': 'a',
38 | 'Ă': 'A', 'Î': 'I', 'Ș': 'S', 'Ț': 'T', 'Â': 'A'
39 | };
40 | var RUSSIAN_MAP = {
41 | 'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'yo',
42 | 'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'j', 'к': 'k', 'л': 'l', 'м': 'm',
43 | 'н': 'n', 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u',
44 | 'ф': 'f', 'х': 'h', 'ц': 'c', 'ч': 'ch', 'ш': 'sh', 'щ': 'sh', 'ъ': '',
45 | 'ы': 'y', 'ь': '', 'э': 'e', 'ю': 'yu', 'я': 'ya',
46 | 'А': 'A', 'Б': 'B', 'В': 'V', 'Г': 'G', 'Д': 'D', 'Е': 'E', 'Ё': 'Yo',
47 | 'Ж': 'Zh', 'З': 'Z', 'И': 'I', 'Й': 'J', 'К': 'K', 'Л': 'L', 'М': 'M',
48 | 'Н': 'N', 'О': 'O', 'П': 'P', 'Р': 'R', 'С': 'S', 'Т': 'T', 'У': 'U',
49 | 'Ф': 'F', 'Х': 'H', 'Ц': 'C', 'Ч': 'Ch', 'Ш': 'Sh', 'Щ': 'Sh', 'Ъ': '',
50 | 'Ы': 'Y', 'Ь': '', 'Э': 'E', 'Ю': 'Yu', 'Я': 'Ya'
51 | };
52 | var UKRAINIAN_MAP = {
53 | 'Є': 'Ye', 'І': 'I', 'Ї': 'Yi', 'Ґ': 'G', 'є': 'ye', 'і': 'i',
54 | 'ї': 'yi', 'ґ': 'g'
55 | };
56 | var CZECH_MAP = {
57 | 'č': 'c', 'ď': 'd', 'ě': 'e', 'ň': 'n', 'ř': 'r', 'š': 's', 'ť': 't',
58 | 'ů': 'u', 'ž': 'z', 'Č': 'C', 'Ď': 'D', 'Ě': 'E', 'Ň': 'N', 'Ř': 'R',
59 | 'Š': 'S', 'Ť': 'T', 'Ů': 'U', 'Ž': 'Z'
60 | };
61 | var POLISH_MAP = {
62 | 'ą': 'a', 'ć': 'c', 'ę': 'e', 'ł': 'l', 'ń': 'n', 'ó': 'o', 'ś': 's',
63 | 'ź': 'z', 'ż': 'z',
64 | 'Ą': 'A', 'Ć': 'C', 'Ę': 'E', 'Ł': 'L', 'Ń': 'N', 'Ó': 'O', 'Ś': 'S',
65 | 'Ź': 'Z', 'Ż': 'Z'
66 | };
67 | var LATVIAN_MAP = {
68 | 'ā': 'a', 'č': 'c', 'ē': 'e', 'ģ': 'g', 'ī': 'i', 'ķ': 'k', 'ļ': 'l',
69 | 'ņ': 'n', 'š': 's', 'ū': 'u', 'ž': 'z',
70 | 'Ā': 'A', 'Č': 'C', 'Ē': 'E', 'Ģ': 'G', 'Ī': 'I', 'Ķ': 'K', 'Ļ': 'L',
71 | 'Ņ': 'N', 'Š': 'S', 'Ū': 'U', 'Ž': 'Z'
72 | };
73 | var ARABIC_MAP = {
74 | 'أ': 'a', 'ب': 'b', 'ت': 't', 'ث': 'th', 'ج': 'g', 'ح': 'h', 'خ': 'kh', 'د': 'd',
75 | 'ذ': 'th', 'ر': 'r', 'ز': 'z', 'س': 's', 'ش': 'sh', 'ص': 's', 'ض': 'd', 'ط': 't',
76 | 'ظ': 'th', 'ع': 'aa', 'غ': 'gh', 'ف': 'f', 'ق': 'k', 'ك': 'k', 'ل': 'l', 'م': 'm',
77 | 'ن': 'n', 'ه': 'h', 'و': 'o', 'ي': 'y'
78 | };
79 | var LITHUANIAN_MAP = {
80 | 'ą': 'a', 'č': 'c', 'ę': 'e', 'ė': 'e', 'į': 'i', 'š': 's', 'ų': 'u',
81 | 'ū': 'u', 'ž': 'z',
82 | 'Ą': 'A', 'Č': 'C', 'Ę': 'E', 'Ė': 'E', 'Į': 'I', 'Š': 'S', 'Ų': 'U',
83 | 'Ū': 'U', 'Ž': 'Z'
84 | };
85 | var SERBIAN_MAP = {
86 | 'ђ': 'dj', 'ј': 'j', 'љ': 'lj', 'њ': 'nj', 'ћ': 'c', 'џ': 'dz',
87 | 'đ': 'dj', 'Ђ': 'Dj', 'Ј': 'j', 'Љ': 'Lj', 'Њ': 'Nj', 'Ћ': 'C',
88 | 'Џ': 'Dz', 'Đ': 'Dj'
89 | };
90 | var AZERBAIJANI_MAP = {
91 | 'ç': 'c', 'ə': 'e', 'ğ': 'g', 'ı': 'i', 'ö': 'o', 'ş': 's', 'ü': 'u',
92 | 'Ç': 'C', 'Ə': 'E', 'Ğ': 'G', 'İ': 'I', 'Ö': 'O', 'Ş': 'S', 'Ü': 'U'
93 | };
94 | var GEORGIAN_MAP = {
95 | 'ა': 'a', 'ბ': 'b', 'გ': 'g', 'დ': 'd', 'ე': 'e', 'ვ': 'v', 'ზ': 'z',
96 | 'თ': 't', 'ი': 'i', 'კ': 'k', 'ლ': 'l', 'მ': 'm', 'ნ': 'n', 'ო': 'o',
97 | 'პ': 'p', 'ჟ': 'j', 'რ': 'r', 'ს': 's', 'ტ': 't', 'უ': 'u', 'ფ': 'f',
98 | 'ქ': 'q', 'ღ': 'g', 'ყ': 'y', 'შ': 'sh', 'ჩ': 'ch', 'ც': 'c', 'ძ': 'dz',
99 | 'წ': 'w', 'ჭ': 'ch', 'ხ': 'x', 'ჯ': 'j', 'ჰ': 'h'
100 | };
101 |
102 | var ALL_DOWNCODE_MAPS = [
103 | LATIN_MAP,
104 | LATIN_SYMBOLS_MAP,
105 | GREEK_MAP,
106 | TURKISH_MAP,
107 | ROMANIAN_MAP,
108 | RUSSIAN_MAP,
109 | UKRAINIAN_MAP,
110 | CZECH_MAP,
111 | POLISH_MAP,
112 | LATVIAN_MAP,
113 | ARABIC_MAP,
114 | LITHUANIAN_MAP,
115 | SERBIAN_MAP,
116 | AZERBAIJANI_MAP,
117 | GEORGIAN_MAP
118 | ];
119 |
120 | var Downcoder = {
121 | 'Initialize': function() {
122 | if (Downcoder.map) { // already made
123 | return;
124 | }
125 | Downcoder.map = {};
126 | Downcoder.chars = [];
127 | for (var i = 0; i < ALL_DOWNCODE_MAPS.length; i++) {
128 | var lookup = ALL_DOWNCODE_MAPS[i];
129 | for (var c in lookup) {
130 | if (lookup.hasOwnProperty(c)) {
131 | Downcoder.map[c] = lookup[c];
132 | }
133 | }
134 | }
135 | for (var k in Downcoder.map) {
136 | if (Downcoder.map.hasOwnProperty(k)) {
137 | Downcoder.chars.push(k);
138 | }
139 | }
140 | Downcoder.regex = new RegExp(Downcoder.chars.join('|'), 'g');
141 | }
142 | };
143 |
144 | function downcode(slug) {
145 | Downcoder.Initialize();
146 | return slug.replace(Downcoder.regex, function(m) {
147 | return Downcoder.map[m];
148 | });
149 | }
150 |
151 |
152 | function URLify(s, num_chars, allowUnicode) {
153 | // changes, e.g., "Petty theft" to "petty-theft"
154 | // remove all these words from the string before urlifying
155 | if (!allowUnicode) {
156 | s = downcode(s);
157 | }
158 | var removelist = [
159 | "a", "an", "as", "at", "before", "but", "by", "for", "from", "is",
160 | "in", "into", "like", "of", "off", "on", "onto", "per", "since",
161 | "than", "the", "this", "that", "to", "up", "via", "with"
162 | ];
163 | var r = new RegExp('\\b(' + removelist.join('|') + ')\\b', 'gi');
164 | s = s.replace(r, '');
165 | // if downcode doesn't hit, the char will be stripped here
166 | if (allowUnicode) {
167 | // Keep Unicode letters including both lowercase and uppercase
168 | // characters, whitespace, and dash; remove other characters.
169 | s = XRegExp.replace(s, XRegExp('[^-_\\p{L}\\p{N}\\s]', 'g'), '');
170 | } else {
171 | s = s.replace(/[^-\w\s]/g, ''); // remove unneeded chars
172 | }
173 | s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces
174 | s = s.replace(/[-\s]+/g, '-'); // convert spaces to hyphens
175 | s = s.toLowerCase(); // convert to lowercase
176 | return s.substring(0, num_chars); // trim to first num_chars chars
177 | }
178 | window.URLify = URLify;
179 | })();
180 |
--------------------------------------------------------------------------------
/static/admin/js/calendar.js:
--------------------------------------------------------------------------------
1 | /*global gettext, pgettext, get_format, quickElement, removeChildren, addEvent*/
2 | /*
3 | calendar.js - Calendar functions by Adrian Holovaty
4 | depends on core.js for utility functions like removeChildren or quickElement
5 | */
6 |
7 | (function() {
8 | 'use strict';
9 | // CalendarNamespace -- Provides a collection of HTML calendar-related helper functions
10 | var CalendarNamespace = {
11 | monthsOfYear: [
12 | gettext('January'),
13 | gettext('February'),
14 | gettext('March'),
15 | gettext('April'),
16 | gettext('May'),
17 | gettext('June'),
18 | gettext('July'),
19 | gettext('August'),
20 | gettext('September'),
21 | gettext('October'),
22 | gettext('November'),
23 | gettext('December')
24 | ],
25 | daysOfWeek: [
26 | pgettext('one letter Sunday', 'S'),
27 | pgettext('one letter Monday', 'M'),
28 | pgettext('one letter Tuesday', 'T'),
29 | pgettext('one letter Wednesday', 'W'),
30 | pgettext('one letter Thursday', 'T'),
31 | pgettext('one letter Friday', 'F'),
32 | pgettext('one letter Saturday', 'S')
33 | ],
34 | firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')),
35 | isLeapYear: function(year) {
36 | return (((year % 4) === 0) && ((year % 100) !== 0 ) || ((year % 400) === 0));
37 | },
38 | getDaysInMonth: function(month, year) {
39 | var days;
40 | if (month === 1 || month === 3 || month === 5 || month === 7 || month === 8 || month === 10 || month === 12) {
41 | days = 31;
42 | }
43 | else if (month === 4 || month === 6 || month === 9 || month === 11) {
44 | days = 30;
45 | }
46 | else if (month === 2 && CalendarNamespace.isLeapYear(year)) {
47 | days = 29;
48 | }
49 | else {
50 | days = 28;
51 | }
52 | return days;
53 | },
54 | draw: function(month, year, div_id, callback, selected) { // month = 1-12, year = 1-9999
55 | var today = new Date();
56 | var todayDay = today.getDate();
57 | var todayMonth = today.getMonth() + 1;
58 | var todayYear = today.getFullYear();
59 | var todayClass = '';
60 |
61 | // Use UTC functions here because the date field does not contain time
62 | // and using the UTC function variants prevent the local time offset
63 | // from altering the date, specifically the day field. For example:
64 | //
65 | // ```
66 | // var x = new Date('2013-10-02');
67 | // var day = x.getDate();
68 | // ```
69 | //
70 | // The day variable above will be 1 instead of 2 in, say, US Pacific time
71 | // zone.
72 | var isSelectedMonth = false;
73 | if (typeof selected !== 'undefined') {
74 | isSelectedMonth = (selected.getUTCFullYear() === year && (selected.getUTCMonth() + 1) === month);
75 | }
76 |
77 | month = parseInt(month);
78 | year = parseInt(year);
79 | var calDiv = document.getElementById(div_id);
80 | removeChildren(calDiv);
81 | var calTable = document.createElement('table');
82 | quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month - 1] + ' ' + year);
83 | var tableBody = quickElement('tbody', calTable);
84 |
85 | // Draw days-of-week header
86 | var tableRow = quickElement('tr', tableBody);
87 | for (var i = 0; i < 7; i++) {
88 | quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]);
89 | }
90 |
91 | var startingPos = new Date(year, month - 1, 1 - CalendarNamespace.firstDayOfWeek).getDay();
92 | var days = CalendarNamespace.getDaysInMonth(month, year);
93 |
94 | var nonDayCell;
95 |
96 | // Draw blanks before first of month
97 | tableRow = quickElement('tr', tableBody);
98 | for (i = 0; i < startingPos; i++) {
99 | nonDayCell = quickElement('td', tableRow, ' ');
100 | nonDayCell.className = "nonday";
101 | }
102 |
103 | function calendarMonth(y, m) {
104 | function onClick(e) {
105 | e.preventDefault();
106 | callback(y, m, django.jQuery(this).text());
107 | }
108 | return onClick;
109 | }
110 |
111 | // Draw days of month
112 | var currentDay = 1;
113 | for (i = startingPos; currentDay <= days; i++) {
114 | if (i % 7 === 0 && currentDay !== 1) {
115 | tableRow = quickElement('tr', tableBody);
116 | }
117 | if ((currentDay === todayDay) && (month === todayMonth) && (year === todayYear)) {
118 | todayClass = 'today';
119 | } else {
120 | todayClass = '';
121 | }
122 |
123 | // use UTC function; see above for explanation.
124 | if (isSelectedMonth && currentDay === selected.getUTCDate()) {
125 | if (todayClass !== '') {
126 | todayClass += " ";
127 | }
128 | todayClass += "selected";
129 | }
130 |
131 | var cell = quickElement('td', tableRow, '', 'class', todayClass);
132 | var link = quickElement('a', cell, currentDay, 'href', '#');
133 | addEvent(link, 'click', calendarMonth(year, month));
134 | currentDay++;
135 | }
136 |
137 | // Draw blanks after end of month (optional, but makes for valid code)
138 | while (tableRow.childNodes.length < 7) {
139 | nonDayCell = quickElement('td', tableRow, ' ');
140 | nonDayCell.className = "nonday";
141 | }
142 |
143 | calDiv.appendChild(calTable);
144 | }
145 | };
146 |
147 | // Calendar -- A calendar instance
148 | function Calendar(div_id, callback, selected) {
149 | // div_id (string) is the ID of the element in which the calendar will
150 | // be displayed
151 | // callback (string) is the name of a JavaScript function that will be
152 | // called with the parameters (year, month, day) when a day in the
153 | // calendar is clicked
154 | this.div_id = div_id;
155 | this.callback = callback;
156 | this.today = new Date();
157 | this.currentMonth = this.today.getMonth() + 1;
158 | this.currentYear = this.today.getFullYear();
159 | if (typeof selected !== 'undefined') {
160 | this.selected = selected;
161 | }
162 | }
163 | Calendar.prototype = {
164 | drawCurrent: function() {
165 | CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback, this.selected);
166 | },
167 | drawDate: function(month, year, selected) {
168 | this.currentMonth = month;
169 | this.currentYear = year;
170 |
171 | if(selected) {
172 | this.selected = selected;
173 | }
174 |
175 | this.drawCurrent();
176 | },
177 | drawPreviousMonth: function() {
178 | if (this.currentMonth === 1) {
179 | this.currentMonth = 12;
180 | this.currentYear--;
181 | }
182 | else {
183 | this.currentMonth--;
184 | }
185 | this.drawCurrent();
186 | },
187 | drawNextMonth: function() {
188 | if (this.currentMonth === 12) {
189 | this.currentMonth = 1;
190 | this.currentYear++;
191 | }
192 | else {
193 | this.currentMonth++;
194 | }
195 | this.drawCurrent();
196 | },
197 | drawPreviousYear: function() {
198 | this.currentYear--;
199 | this.drawCurrent();
200 | },
201 | drawNextYear: function() {
202 | this.currentYear++;
203 | this.drawCurrent();
204 | }
205 | };
206 | window.Calendar = Calendar;
207 | window.CalendarNamespace = CalendarNamespace;
208 | })();
209 |
--------------------------------------------------------------------------------
/static/admin/js/core.js:
--------------------------------------------------------------------------------
1 | // Core javascript helper functions
2 |
3 | // basic browser identification & version
4 | var isOpera = (navigator.userAgent.indexOf("Opera") >= 0) && parseFloat(navigator.appVersion);
5 | var isIE = ((document.all) && (!isOpera)) && parseFloat(navigator.appVersion.split("MSIE ")[1].split(";")[0]);
6 |
7 | // Cross-browser event handlers.
8 | function addEvent(obj, evType, fn) {
9 | 'use strict';
10 | if (obj.addEventListener) {
11 | obj.addEventListener(evType, fn, false);
12 | return true;
13 | } else if (obj.attachEvent) {
14 | var r = obj.attachEvent("on" + evType, fn);
15 | return r;
16 | } else {
17 | return false;
18 | }
19 | }
20 |
21 | function removeEvent(obj, evType, fn) {
22 | 'use strict';
23 | if (obj.removeEventListener) {
24 | obj.removeEventListener(evType, fn, false);
25 | return true;
26 | } else if (obj.detachEvent) {
27 | obj.detachEvent("on" + evType, fn);
28 | return true;
29 | } else {
30 | return false;
31 | }
32 | }
33 |
34 | function cancelEventPropagation(e) {
35 | 'use strict';
36 | if (!e) {
37 | e = window.event;
38 | }
39 | e.cancelBubble = true;
40 | if (e.stopPropagation) {
41 | e.stopPropagation();
42 | }
43 | }
44 |
45 | // quickElement(tagType, parentReference [, textInChildNode, attribute, attributeValue ...]);
46 | function quickElement() {
47 | 'use strict';
48 | var obj = document.createElement(arguments[0]);
49 | if (arguments[2]) {
50 | var textNode = document.createTextNode(arguments[2]);
51 | obj.appendChild(textNode);
52 | }
53 | var len = arguments.length;
54 | for (var i = 3; i < len; i += 2) {
55 | obj.setAttribute(arguments[i], arguments[i + 1]);
56 | }
57 | arguments[1].appendChild(obj);
58 | return obj;
59 | }
60 |
61 | // "a" is reference to an object
62 | function removeChildren(a) {
63 | 'use strict';
64 | while (a.hasChildNodes()) {
65 | a.removeChild(a.lastChild);
66 | }
67 | }
68 |
69 | // ----------------------------------------------------------------------------
70 | // Find-position functions by PPK
71 | // See http://www.quirksmode.org/js/findpos.html
72 | // ----------------------------------------------------------------------------
73 | function findPosX(obj) {
74 | 'use strict';
75 | var curleft = 0;
76 | if (obj.offsetParent) {
77 | while (obj.offsetParent) {
78 | curleft += obj.offsetLeft - ((isOpera) ? 0 : obj.scrollLeft);
79 | obj = obj.offsetParent;
80 | }
81 | // IE offsetParent does not include the top-level
82 | if (isIE && obj.parentElement) {
83 | curleft += obj.offsetLeft - obj.scrollLeft;
84 | }
85 | } else if (obj.x) {
86 | curleft += obj.x;
87 | }
88 | return curleft;
89 | }
90 |
91 | function findPosY(obj) {
92 | 'use strict';
93 | var curtop = 0;
94 | if (obj.offsetParent) {
95 | while (obj.offsetParent) {
96 | curtop += obj.offsetTop - ((isOpera) ? 0 : obj.scrollTop);
97 | obj = obj.offsetParent;
98 | }
99 | // IE offsetParent does not include the top-level
100 | if (isIE && obj.parentElement) {
101 | curtop += obj.offsetTop - obj.scrollTop;
102 | }
103 | } else if (obj.y) {
104 | curtop += obj.y;
105 | }
106 | return curtop;
107 | }
108 |
109 | //-----------------------------------------------------------------------------
110 | // Date object extensions
111 | // ----------------------------------------------------------------------------
112 | (function() {
113 | 'use strict';
114 | Date.prototype.getTwelveHours = function() {
115 | var hours = this.getHours();
116 | if (hours === 0) {
117 | return 12;
118 | }
119 | else {
120 | return hours <= 12 ? hours : hours - 12;
121 | }
122 | };
123 |
124 | Date.prototype.getTwoDigitMonth = function() {
125 | return (this.getMonth() < 9) ? '0' + (this.getMonth() + 1) : (this.getMonth() + 1);
126 | };
127 |
128 | Date.prototype.getTwoDigitDate = function() {
129 | return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate();
130 | };
131 |
132 | Date.prototype.getTwoDigitTwelveHour = function() {
133 | return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours();
134 | };
135 |
136 | Date.prototype.getTwoDigitHour = function() {
137 | return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours();
138 | };
139 |
140 | Date.prototype.getTwoDigitMinute = function() {
141 | return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes();
142 | };
143 |
144 | Date.prototype.getTwoDigitSecond = function() {
145 | return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds();
146 | };
147 |
148 | Date.prototype.getHourMinute = function() {
149 | return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute();
150 | };
151 |
152 | Date.prototype.getHourMinuteSecond = function() {
153 | return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond();
154 | };
155 |
156 | Date.prototype.getFullMonthName = function() {
157 | return typeof window.CalendarNamespace === "undefined"
158 | ? this.getTwoDigitMonth()
159 | : window.CalendarNamespace.monthsOfYear[this.getMonth()];
160 | };
161 |
162 | Date.prototype.strftime = function(format) {
163 | var fields = {
164 | B: this.getFullMonthName(),
165 | c: this.toString(),
166 | d: this.getTwoDigitDate(),
167 | H: this.getTwoDigitHour(),
168 | I: this.getTwoDigitTwelveHour(),
169 | m: this.getTwoDigitMonth(),
170 | M: this.getTwoDigitMinute(),
171 | p: (this.getHours() >= 12) ? 'PM' : 'AM',
172 | S: this.getTwoDigitSecond(),
173 | w: '0' + this.getDay(),
174 | x: this.toLocaleDateString(),
175 | X: this.toLocaleTimeString(),
176 | y: ('' + this.getFullYear()).substr(2, 4),
177 | Y: '' + this.getFullYear(),
178 | '%': '%'
179 | };
180 | var result = '', i = 0;
181 | while (i < format.length) {
182 | if (format.charAt(i) === '%') {
183 | result = result + fields[format.charAt(i + 1)];
184 | ++i;
185 | }
186 | else {
187 | result = result + format.charAt(i);
188 | }
189 | ++i;
190 | }
191 | return result;
192 | };
193 |
194 | // ----------------------------------------------------------------------------
195 | // String object extensions
196 | // ----------------------------------------------------------------------------
197 | String.prototype.pad_left = function(pad_length, pad_string) {
198 | var new_string = this;
199 | for (var i = 0; new_string.length < pad_length; i++) {
200 | new_string = pad_string + new_string;
201 | }
202 | return new_string;
203 | };
204 |
205 | String.prototype.strptime = function(format) {
206 | var split_format = format.split(/[.\-/]/);
207 | var date = this.split(/[.\-/]/);
208 | var i = 0;
209 | var day, month, year;
210 | while (i < split_format.length) {
211 | switch (split_format[i]) {
212 | case "%d":
213 | day = date[i];
214 | break;
215 | case "%m":
216 | month = date[i] - 1;
217 | break;
218 | case "%Y":
219 | year = date[i];
220 | break;
221 | case "%y":
222 | year = date[i];
223 | break;
224 | }
225 | ++i;
226 | }
227 | // Create Date object from UTC since the parsed value is supposed to be
228 | // in UTC, not local time. Also, the calendar uses UTC functions for
229 | // date extraction.
230 | return new Date(Date.UTC(year, month, day));
231 | };
232 |
233 | })();
234 | // ----------------------------------------------------------------------------
235 | // Get the computed style for and element
236 | // ----------------------------------------------------------------------------
237 | function getStyle(oElm, strCssRule) {
238 | 'use strict';
239 | var strValue = "";
240 | if(document.defaultView && document.defaultView.getComputedStyle) {
241 | strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
242 | }
243 | else if(oElm.currentStyle) {
244 | strCssRule = strCssRule.replace(/\-(\w)/g, function(strMatch, p1) {
245 | return p1.toUpperCase();
246 | });
247 | strValue = oElm.currentStyle[strCssRule];
248 | }
249 | return strValue;
250 | }
251 |
--------------------------------------------------------------------------------
/static/admin/css/forms.css:
--------------------------------------------------------------------------------
1 | @import url('widgets.css');
2 |
3 | /* FORM ROWS */
4 |
5 | .form-row {
6 | overflow: hidden;
7 | padding: 10px;
8 | font-size: 13px;
9 | border-bottom: 1px solid #eee;
10 | }
11 |
12 | .form-row img, .form-row input {
13 | vertical-align: middle;
14 | }
15 |
16 | .form-row label input[type="checkbox"] {
17 | margin-top: 0;
18 | vertical-align: 0;
19 | }
20 |
21 | form .form-row p {
22 | padding-left: 0;
23 | }
24 |
25 | .hidden {
26 | display: none;
27 | }
28 |
29 | /* FORM LABELS */
30 |
31 | label {
32 | font-weight: normal;
33 | color: #666;
34 | font-size: 13px;
35 | }
36 |
37 | .required label, label.required {
38 | font-weight: bold;
39 | color: #333;
40 | }
41 |
42 | /* RADIO BUTTONS */
43 |
44 | form ul.radiolist li {
45 | list-style-type: none;
46 | }
47 |
48 | form ul.radiolist label {
49 | float: none;
50 | display: inline;
51 | }
52 |
53 | form ul.radiolist input[type="radio"] {
54 | margin: -2px 4px 0 0;
55 | padding: 0;
56 | }
57 |
58 | form ul.inline {
59 | margin-left: 0;
60 | padding: 0;
61 | }
62 |
63 | form ul.inline li {
64 | float: left;
65 | padding-right: 7px;
66 | }
67 |
68 | /* ALIGNED FIELDSETS */
69 |
70 | .aligned label {
71 | display: block;
72 | padding: 4px 10px 0 0;
73 | float: left;
74 | width: 160px;
75 | word-wrap: break-word;
76 | line-height: 1;
77 | }
78 |
79 | .aligned label:not(.vCheckboxLabel):after {
80 | content: '';
81 | display: inline-block;
82 | vertical-align: middle;
83 | height: 26px;
84 | }
85 |
86 | .aligned label + p, .aligned label + div.help, .aligned label + div.readonly {
87 | padding: 6px 0;
88 | margin-top: 0;
89 | margin-bottom: 0;
90 | margin-left: 170px;
91 | }
92 |
93 | .aligned ul label {
94 | display: inline;
95 | float: none;
96 | width: auto;
97 | }
98 |
99 | .aligned .form-row input {
100 | margin-bottom: 0;
101 | }
102 |
103 | .colMS .aligned .vLargeTextField, .colMS .aligned .vXMLLargeTextField {
104 | width: 350px;
105 | }
106 |
107 | form .aligned ul {
108 | margin-left: 160px;
109 | padding-left: 10px;
110 | }
111 |
112 | form .aligned ul.radiolist {
113 | display: inline-block;
114 | margin: 0;
115 | padding: 0;
116 | }
117 |
118 | form .aligned p.help,
119 | form .aligned div.help {
120 | clear: left;
121 | margin-top: 0;
122 | margin-left: 160px;
123 | padding-left: 10px;
124 | }
125 |
126 | form .aligned label + p.help,
127 | form .aligned label + div.help {
128 | margin-left: 0;
129 | padding-left: 0;
130 | }
131 |
132 | form .aligned p.help:last-child,
133 | form .aligned div.help:last-child {
134 | margin-bottom: 0;
135 | padding-bottom: 0;
136 | }
137 |
138 | form .aligned input + p.help,
139 | form .aligned textarea + p.help,
140 | form .aligned select + p.help,
141 | form .aligned input + div.help,
142 | form .aligned textarea + div.help,
143 | form .aligned select + div.help {
144 | margin-left: 160px;
145 | padding-left: 10px;
146 | }
147 |
148 | form .aligned ul li {
149 | list-style: none;
150 | }
151 |
152 | form .aligned table p {
153 | margin-left: 0;
154 | padding-left: 0;
155 | }
156 |
157 | .aligned .vCheckboxLabel {
158 | float: none;
159 | width: auto;
160 | display: inline-block;
161 | vertical-align: -3px;
162 | padding: 0 0 5px 5px;
163 | }
164 |
165 | .aligned .vCheckboxLabel + p.help,
166 | .aligned .vCheckboxLabel + div.help {
167 | margin-top: -4px;
168 | }
169 |
170 | .colM .aligned .vLargeTextField, .colM .aligned .vXMLLargeTextField {
171 | width: 610px;
172 | }
173 |
174 | .checkbox-row p.help,
175 | .checkbox-row div.help {
176 | margin-left: 0;
177 | padding-left: 0;
178 | }
179 |
180 | fieldset .field-box {
181 | float: left;
182 | margin-right: 20px;
183 | }
184 |
185 | /* WIDE FIELDSETS */
186 |
187 | .wide label {
188 | width: 200px;
189 | }
190 |
191 | form .wide p,
192 | form .wide input + p.help,
193 | form .wide input + div.help {
194 | margin-left: 200px;
195 | }
196 |
197 | form .wide p.help,
198 | form .wide div.help {
199 | padding-left: 38px;
200 | }
201 |
202 | form div.help ul {
203 | padding-left: 0;
204 | margin-left: 0;
205 | }
206 |
207 | .colM fieldset.wide .vLargeTextField, .colM fieldset.wide .vXMLLargeTextField {
208 | width: 450px;
209 | }
210 |
211 | /* COLLAPSED FIELDSETS */
212 |
213 | fieldset.collapsed * {
214 | display: none;
215 | }
216 |
217 | fieldset.collapsed h2, fieldset.collapsed {
218 | display: block;
219 | }
220 |
221 | fieldset.collapsed {
222 | border: 1px solid #eee;
223 | border-radius: 4px;
224 | overflow: hidden;
225 | }
226 |
227 | fieldset.collapsed h2 {
228 | background: #f8f8f8;
229 | color: #666;
230 | }
231 |
232 | fieldset .collapse-toggle {
233 | color: #fff;
234 | }
235 |
236 | fieldset.collapsed .collapse-toggle {
237 | background: transparent;
238 | display: inline;
239 | color: #447e9b;
240 | }
241 |
242 | /* MONOSPACE TEXTAREAS */
243 |
244 | fieldset.monospace textarea {
245 | font-family: "Bitstream Vera Sans Mono", Monaco, "Courier New", Courier, monospace;
246 | }
247 |
248 | /* SUBMIT ROW */
249 |
250 | .submit-row {
251 | padding: 12px 14px;
252 | margin: 0 0 20px;
253 | background: #f8f8f8;
254 | border: 1px solid #eee;
255 | border-radius: 4px;
256 | text-align: right;
257 | overflow: hidden;
258 | }
259 |
260 | body.popup .submit-row {
261 | overflow: auto;
262 | }
263 |
264 | .submit-row input {
265 | height: 35px;
266 | line-height: 15px;
267 | margin: 0 0 0 5px;
268 | }
269 |
270 | .submit-row input.default {
271 | margin: 0 0 0 8px;
272 | text-transform: uppercase;
273 | }
274 |
275 | .submit-row p {
276 | margin: 0.3em;
277 | }
278 |
279 | .submit-row p.deletelink-box {
280 | float: left;
281 | margin: 0;
282 | }
283 |
284 | .submit-row a.deletelink {
285 | display: block;
286 | background: #ba2121;
287 | border-radius: 4px;
288 | padding: 10px 15px;
289 | height: 15px;
290 | line-height: 15px;
291 | color: #fff;
292 | }
293 |
294 | .submit-row a.deletelink:focus,
295 | .submit-row a.deletelink:hover,
296 | .submit-row a.deletelink:active {
297 | background: #a41515;
298 | }
299 |
300 | /* CUSTOM FORM FIELDS */
301 |
302 | .vSelectMultipleField {
303 | vertical-align: top;
304 | }
305 |
306 | .vCheckboxField {
307 | border: none;
308 | }
309 |
310 | .vDateField, .vTimeField {
311 | margin-right: 2px;
312 | margin-bottom: 4px;
313 | }
314 |
315 | .vDateField {
316 | min-width: 6.85em;
317 | }
318 |
319 | .vTimeField {
320 | min-width: 4.7em;
321 | }
322 |
323 | .vURLField {
324 | width: 30em;
325 | }
326 |
327 | .vLargeTextField, .vXMLLargeTextField {
328 | width: 48em;
329 | }
330 |
331 | .flatpages-flatpage #id_content {
332 | height: 40.2em;
333 | }
334 |
335 | .module table .vPositiveSmallIntegerField {
336 | width: 2.2em;
337 | }
338 |
339 | .vTextField {
340 | width: 20em;
341 | }
342 |
343 | .vIntegerField {
344 | width: 5em;
345 | }
346 |
347 | .vBigIntegerField {
348 | width: 10em;
349 | }
350 |
351 | .vForeignKeyRawIdAdminField {
352 | width: 5em;
353 | }
354 |
355 | /* INLINES */
356 |
357 | .inline-group {
358 | padding: 0;
359 | margin: 0 0 30px;
360 | }
361 |
362 | .inline-group thead th {
363 | padding: 8px 10px;
364 | }
365 |
366 | .inline-group .aligned label {
367 | width: 160px;
368 | }
369 |
370 | .inline-related {
371 | position: relative;
372 | }
373 |
374 | .inline-related h3 {
375 | margin: 0;
376 | color: #666;
377 | padding: 5px;
378 | font-size: 13px;
379 | background: #f8f8f8;
380 | border-top: 1px solid #eee;
381 | border-bottom: 1px solid #eee;
382 | }
383 |
384 | .inline-related h3 span.delete {
385 | float: right;
386 | }
387 |
388 | .inline-related h3 span.delete label {
389 | margin-left: 2px;
390 | font-size: 11px;
391 | }
392 |
393 | .inline-related fieldset {
394 | margin: 0;
395 | background: #fff;
396 | border: none;
397 | width: 100%;
398 | }
399 |
400 | .inline-related fieldset.module h3 {
401 | margin: 0;
402 | padding: 2px 5px 3px 5px;
403 | font-size: 11px;
404 | text-align: left;
405 | font-weight: bold;
406 | background: #bcd;
407 | color: #fff;
408 | }
409 |
410 | .inline-group .tabular fieldset.module {
411 | border: none;
412 | }
413 |
414 | .inline-related.tabular fieldset.module table {
415 | width: 100%;
416 | }
417 |
418 | .last-related fieldset {
419 | border: none;
420 | }
421 |
422 | .inline-group .tabular tr.has_original td {
423 | padding-top: 2em;
424 | }
425 |
426 | .inline-group .tabular tr td.original {
427 | padding: 2px 0 0 0;
428 | width: 0;
429 | _position: relative;
430 | }
431 |
432 | .inline-group .tabular th.original {
433 | width: 0px;
434 | padding: 0;
435 | }
436 |
437 | .inline-group .tabular td.original p {
438 | position: absolute;
439 | left: 0;
440 | height: 1.1em;
441 | padding: 2px 9px;
442 | overflow: hidden;
443 | font-size: 9px;
444 | font-weight: bold;
445 | color: #666;
446 | _width: 700px;
447 | }
448 |
449 | .inline-group ul.tools {
450 | padding: 0;
451 | margin: 0;
452 | list-style: none;
453 | }
454 |
455 | .inline-group ul.tools li {
456 | display: inline;
457 | padding: 0 5px;
458 | }
459 |
460 | .inline-group div.add-row,
461 | .inline-group .tabular tr.add-row td {
462 | color: #666;
463 | background: #f8f8f8;
464 | padding: 8px 10px;
465 | border-bottom: 1px solid #eee;
466 | }
467 |
468 | .inline-group .tabular tr.add-row td {
469 | padding: 8px 10px;
470 | border-bottom: 1px solid #eee;
471 | }
472 |
473 | .inline-group ul.tools a.add,
474 | .inline-group div.add-row a,
475 | .inline-group .tabular tr.add-row td a {
476 | background: url(../img/icon-addlink.svg) 0 1px no-repeat;
477 | padding-left: 16px;
478 | font-size: 12px;
479 | }
480 |
481 | .empty-form {
482 | display: none;
483 | }
484 |
485 | /* RELATED FIELD ADD ONE / LOOKUP */
486 |
487 | .add-another, .related-lookup {
488 | margin-left: 5px;
489 | display: inline-block;
490 | vertical-align: middle;
491 | background-repeat: no-repeat;
492 | background-size: 14px;
493 | }
494 |
495 | .add-another {
496 | width: 16px;
497 | height: 16px;
498 | background-image: url(../img/icon-addlink.svg);
499 | }
500 |
501 | .related-lookup {
502 | width: 16px;
503 | height: 16px;
504 | background-image: url(../img/search.svg);
505 | }
506 |
507 | form .related-widget-wrapper ul {
508 | display: inline-block;
509 | margin-left: 0;
510 | padding-left: 0;
511 | }
512 |
513 | .clearable-file-input input {
514 | margin-top: 0;
515 | }
516 |
--------------------------------------------------------------------------------
/static/admin/fonts/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/static/admin/css/widgets.css:
--------------------------------------------------------------------------------
1 | /* SELECTOR (FILTER INTERFACE) */
2 |
3 | .selector {
4 | width: 800px;
5 | float: left;
6 | }
7 |
8 | .selector select {
9 | width: 380px;
10 | height: 17.2em;
11 | }
12 |
13 | .selector-available, .selector-chosen {
14 | float: left;
15 | width: 380px;
16 | text-align: center;
17 | margin-bottom: 5px;
18 | }
19 |
20 | .selector-chosen select {
21 | border-top: none;
22 | }
23 |
24 | .selector-available h2, .selector-chosen h2 {
25 | border: 1px solid #ccc;
26 | border-radius: 4px 4px 0 0;
27 | }
28 |
29 | .selector-chosen h2 {
30 | background: #79aec8;
31 | color: #fff;
32 | }
33 |
34 | .selector .selector-available h2 {
35 | background: #f8f8f8;
36 | color: #666;
37 | }
38 |
39 | .selector .selector-filter {
40 | background: white;
41 | border: 1px solid #ccc;
42 | border-width: 0 1px;
43 | padding: 8px;
44 | color: #999;
45 | font-size: 10px;
46 | margin: 0;
47 | text-align: left;
48 | }
49 |
50 | .selector .selector-filter label,
51 | .inline-group .aligned .selector .selector-filter label {
52 | float: left;
53 | margin: 7px 0 0;
54 | width: 18px;
55 | height: 18px;
56 | padding: 0;
57 | overflow: hidden;
58 | line-height: 1;
59 | }
60 |
61 | .selector .selector-available input {
62 | width: 320px;
63 | margin-left: 8px;
64 | }
65 |
66 | .selector ul.selector-chooser {
67 | float: left;
68 | width: 22px;
69 | background-color: #eee;
70 | border-radius: 10px;
71 | margin: 10em 5px 0 5px;
72 | padding: 0;
73 | }
74 |
75 | .selector-chooser li {
76 | margin: 0;
77 | padding: 3px;
78 | list-style-type: none;
79 | }
80 |
81 | .selector select {
82 | padding: 0 10px;
83 | margin: 0 0 10px;
84 | border-radius: 0 0 4px 4px;
85 | }
86 |
87 | .selector-add, .selector-remove {
88 | width: 16px;
89 | height: 16px;
90 | display: block;
91 | text-indent: -3000px;
92 | overflow: hidden;
93 | cursor: default;
94 | opacity: 0.3;
95 | }
96 |
97 | .active.selector-add, .active.selector-remove {
98 | opacity: 1;
99 | }
100 |
101 | .active.selector-add:hover, .active.selector-remove:hover {
102 | cursor: pointer;
103 | }
104 |
105 | .selector-add {
106 | background: url(../img/selector-icons.svg) 0 -96px no-repeat;
107 | }
108 |
109 | .active.selector-add:focus, .active.selector-add:hover {
110 | background-position: 0 -112px;
111 | }
112 |
113 | .selector-remove {
114 | background: url(../img/selector-icons.svg) 0 -64px no-repeat;
115 | }
116 |
117 | .active.selector-remove:focus, .active.selector-remove:hover {
118 | background-position: 0 -80px;
119 | }
120 |
121 | a.selector-chooseall, a.selector-clearall {
122 | display: inline-block;
123 | height: 16px;
124 | text-align: left;
125 | margin: 1px auto 3px;
126 | overflow: hidden;
127 | font-weight: bold;
128 | line-height: 16px;
129 | color: #666;
130 | text-decoration: none;
131 | opacity: 0.3;
132 | }
133 |
134 | a.active.selector-chooseall:focus, a.active.selector-clearall:focus,
135 | a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
136 | color: #447e9b;
137 | }
138 |
139 | a.active.selector-chooseall, a.active.selector-clearall {
140 | opacity: 1;
141 | }
142 |
143 | a.active.selector-chooseall:hover, a.active.selector-clearall:hover {
144 | cursor: pointer;
145 | }
146 |
147 | a.selector-chooseall {
148 | padding: 0 18px 0 0;
149 | background: url(../img/selector-icons.svg) right -160px no-repeat;
150 | cursor: default;
151 | }
152 |
153 | a.active.selector-chooseall:focus, a.active.selector-chooseall:hover {
154 | background-position: 100% -176px;
155 | }
156 |
157 | a.selector-clearall {
158 | padding: 0 0 0 18px;
159 | background: url(../img/selector-icons.svg) 0 -128px no-repeat;
160 | cursor: default;
161 | }
162 |
163 | a.active.selector-clearall:focus, a.active.selector-clearall:hover {
164 | background-position: 0 -144px;
165 | }
166 |
167 | /* STACKED SELECTORS */
168 |
169 | .stacked {
170 | float: left;
171 | width: 490px;
172 | }
173 |
174 | .stacked select {
175 | width: 480px;
176 | height: 10.1em;
177 | }
178 |
179 | .stacked .selector-available, .stacked .selector-chosen {
180 | width: 480px;
181 | }
182 |
183 | .stacked .selector-available {
184 | margin-bottom: 0;
185 | }
186 |
187 | .stacked .selector-available input {
188 | width: 422px;
189 | }
190 |
191 | .stacked ul.selector-chooser {
192 | height: 22px;
193 | width: 50px;
194 | margin: 0 0 10px 40%;
195 | background-color: #eee;
196 | border-radius: 10px;
197 | }
198 |
199 | .stacked .selector-chooser li {
200 | float: left;
201 | padding: 3px 3px 3px 5px;
202 | }
203 |
204 | .stacked .selector-chooseall, .stacked .selector-clearall {
205 | display: none;
206 | }
207 |
208 | .stacked .selector-add {
209 | background: url(../img/selector-icons.svg) 0 -32px no-repeat;
210 | cursor: default;
211 | }
212 |
213 | .stacked .active.selector-add {
214 | background-position: 0 -48px;
215 | cursor: pointer;
216 | }
217 |
218 | .stacked .selector-remove {
219 | background: url(../img/selector-icons.svg) 0 0 no-repeat;
220 | cursor: default;
221 | }
222 |
223 | .stacked .active.selector-remove {
224 | background-position: 0 -16px;
225 | cursor: pointer;
226 | }
227 |
228 | .selector .help-icon {
229 | background: url(../img/icon-unknown.svg) 0 0 no-repeat;
230 | display: inline-block;
231 | vertical-align: middle;
232 | margin: -2px 0 0 2px;
233 | width: 13px;
234 | height: 13px;
235 | }
236 |
237 | .selector .selector-chosen .help-icon {
238 | background: url(../img/icon-unknown-alt.svg) 0 0 no-repeat;
239 | }
240 |
241 | .selector .search-label-icon {
242 | background: url(../img/search.svg) 0 0 no-repeat;
243 | display: inline-block;
244 | height: 18px;
245 | width: 18px;
246 | }
247 |
248 | /* DATE AND TIME */
249 |
250 | p.datetime {
251 | line-height: 20px;
252 | margin: 0;
253 | padding: 0;
254 | color: #666;
255 | font-weight: bold;
256 | }
257 |
258 | .datetime span {
259 | white-space: nowrap;
260 | font-weight: normal;
261 | font-size: 11px;
262 | color: #ccc;
263 | }
264 |
265 | .datetime input, .form-row .datetime input.vDateField, .form-row .datetime input.vTimeField {
266 | min-width: 0;
267 | margin-left: 5px;
268 | margin-bottom: 4px;
269 | }
270 |
271 | table p.datetime {
272 | font-size: 11px;
273 | margin-left: 0;
274 | padding-left: 0;
275 | }
276 |
277 | .datetimeshortcuts .clock-icon, .datetimeshortcuts .date-icon {
278 | position: relative;
279 | display: inline-block;
280 | vertical-align: middle;
281 | height: 16px;
282 | width: 16px;
283 | overflow: hidden;
284 | }
285 |
286 | .datetimeshortcuts .clock-icon {
287 | background: url(../img/icon-clock.svg) 0 0 no-repeat;
288 | }
289 |
290 | .datetimeshortcuts a:focus .clock-icon,
291 | .datetimeshortcuts a:hover .clock-icon {
292 | background-position: 0 -16px;
293 | }
294 |
295 | .datetimeshortcuts .date-icon {
296 | background: url(../img/icon-calendar.svg) 0 0 no-repeat;
297 | top: -1px;
298 | }
299 |
300 | .datetimeshortcuts a:focus .date-icon,
301 | .datetimeshortcuts a:hover .date-icon {
302 | background-position: 0 -16px;
303 | }
304 |
305 | .timezonewarning {
306 | font-size: 11px;
307 | color: #999;
308 | }
309 |
310 | /* URL */
311 |
312 | p.url {
313 | line-height: 20px;
314 | margin: 0;
315 | padding: 0;
316 | color: #666;
317 | font-size: 11px;
318 | font-weight: bold;
319 | }
320 |
321 | .url a {
322 | font-weight: normal;
323 | }
324 |
325 | /* FILE UPLOADS */
326 |
327 | p.file-upload {
328 | line-height: 20px;
329 | margin: 0;
330 | padding: 0;
331 | color: #666;
332 | font-size: 11px;
333 | font-weight: bold;
334 | }
335 |
336 | .aligned p.file-upload {
337 | margin-left: 170px;
338 | }
339 |
340 | .file-upload a {
341 | font-weight: normal;
342 | }
343 |
344 | .file-upload .deletelink {
345 | margin-left: 5px;
346 | }
347 |
348 | span.clearable-file-input label {
349 | color: #333;
350 | font-size: 11px;
351 | display: inline;
352 | float: none;
353 | }
354 |
355 | /* CALENDARS & CLOCKS */
356 |
357 | .calendarbox, .clockbox {
358 | margin: 5px auto;
359 | font-size: 12px;
360 | width: 19em;
361 | text-align: center;
362 | background: white;
363 | border: 1px solid #ddd;
364 | border-radius: 4px;
365 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
366 | overflow: hidden;
367 | position: relative;
368 | }
369 |
370 | .clockbox {
371 | width: auto;
372 | }
373 |
374 | .calendar {
375 | margin: 0;
376 | padding: 0;
377 | }
378 |
379 | .calendar table {
380 | margin: 0;
381 | padding: 0;
382 | border-collapse: collapse;
383 | background: white;
384 | width: 100%;
385 | }
386 |
387 | .calendar caption, .calendarbox h2 {
388 | margin: 0;
389 | text-align: center;
390 | border-top: none;
391 | background: #f5dd5d;
392 | font-weight: 700;
393 | font-size: 12px;
394 | color: #333;
395 | }
396 |
397 | .calendar th {
398 | padding: 8px 5px;
399 | background: #f8f8f8;
400 | border-bottom: 1px solid #ddd;
401 | font-weight: 400;
402 | font-size: 12px;
403 | text-align: center;
404 | color: #666;
405 | }
406 |
407 | .calendar td {
408 | font-weight: 400;
409 | font-size: 12px;
410 | text-align: center;
411 | padding: 0;
412 | border-top: 1px solid #eee;
413 | border-bottom: none;
414 | }
415 |
416 | .calendar td.selected a {
417 | background: #79aec8;
418 | color: #fff;
419 | }
420 |
421 | .calendar td.nonday {
422 | background: #f8f8f8;
423 | }
424 |
425 | .calendar td.today a {
426 | font-weight: 700;
427 | }
428 |
429 | .calendar td a, .timelist a {
430 | display: block;
431 | font-weight: 400;
432 | padding: 6px;
433 | text-decoration: none;
434 | color: #444;
435 | }
436 |
437 | .calendar td a:focus, .timelist a:focus,
438 | .calendar td a:hover, .timelist a:hover {
439 | background: #79aec8;
440 | color: white;
441 | }
442 |
443 | .calendar td a:active, .timelist a:active {
444 | background: #417690;
445 | color: white;
446 | }
447 |
448 | .calendarnav {
449 | font-size: 10px;
450 | text-align: center;
451 | color: #ccc;
452 | margin: 0;
453 | padding: 1px 3px;
454 | }
455 |
456 | .calendarnav a:link, #calendarnav a:visited,
457 | #calendarnav a:focus, #calendarnav a:hover {
458 | color: #999;
459 | }
460 |
461 | .calendar-shortcuts {
462 | background: white;
463 | font-size: 11px;
464 | line-height: 11px;
465 | border-top: 1px solid #eee;
466 | padding: 8px 0;
467 | color: #ccc;
468 | }
469 |
470 | .calendarbox .calendarnav-previous, .calendarbox .calendarnav-next {
471 | display: block;
472 | position: absolute;
473 | top: 8px;
474 | width: 15px;
475 | height: 15px;
476 | text-indent: -9999px;
477 | padding: 0;
478 | }
479 |
480 | .calendarnav-previous {
481 | left: 10px;
482 | background: url(../img/calendar-icons.svg) 0 0 no-repeat;
483 | }
484 |
485 | .calendarbox .calendarnav-previous:focus,
486 | .calendarbox .calendarnav-previous:hover {
487 | background-position: 0 -15px;
488 | }
489 |
490 | .calendarnav-next {
491 | right: 10px;
492 | background: url(../img/calendar-icons.svg) 0 -30px no-repeat;
493 | }
494 |
495 | .calendarbox .calendarnav-next:focus,
496 | .calendarbox .calendarnav-next:hover {
497 | background-position: 0 -45px;
498 | }
499 |
500 | .calendar-cancel {
501 | margin: 0;
502 | padding: 4px 0;
503 | font-size: 12px;
504 | background: #eee;
505 | border-top: 1px solid #ddd;
506 | color: #333;
507 | }
508 |
509 | .calendar-cancel:focus, .calendar-cancel:hover {
510 | background: #ddd;
511 | }
512 |
513 | .calendar-cancel a {
514 | color: black;
515 | display: block;
516 | }
517 |
518 | ul.timelist, .timelist li {
519 | list-style-type: none;
520 | margin: 0;
521 | padding: 0;
522 | }
523 |
524 | .timelist a {
525 | padding: 2px;
526 | }
527 |
528 | /* EDIT INLINE */
529 |
530 | .inline-deletelink {
531 | float: right;
532 | text-indent: -9999px;
533 | background: url(../img/inline-delete.svg) 0 0 no-repeat;
534 | width: 16px;
535 | height: 16px;
536 | border: 0px none;
537 | }
538 |
539 | .inline-deletelink:focus, .inline-deletelink:hover {
540 | cursor: pointer;
541 | }
542 |
543 | /* RELATED WIDGET WRAPPER */
544 | .related-widget-wrapper {
545 | float: left; /* display properly in form rows with multiple fields */
546 | overflow: hidden; /* clear floated contents */
547 | }
548 |
549 | .related-widget-wrapper-link {
550 | opacity: 0.3;
551 | }
552 |
553 | .related-widget-wrapper-link:link {
554 | opacity: .8;
555 | }
556 |
557 | .related-widget-wrapper-link:link:focus,
558 | .related-widget-wrapper-link:link:hover {
559 | opacity: 1;
560 | }
561 |
562 | select + .related-widget-wrapper-link,
563 | .related-widget-wrapper-link + .related-widget-wrapper-link {
564 | margin-left: 7px;
565 | }
566 |
--------------------------------------------------------------------------------
/static/admin/js/SelectFilter2.js:
--------------------------------------------------------------------------------
1 | /*global SelectBox, addEvent, gettext, interpolate, quickElement, SelectFilter*/
2 | /*
3 | SelectFilter2 - Turns a multiple-select box into a filter interface.
4 |
5 | Requires jQuery, core.js, and SelectBox.js.
6 | */
7 | (function($) {
8 | 'use strict';
9 | function findForm(node) {
10 | // returns the node of the form containing the given node
11 | if (node.tagName.toLowerCase() !== 'form') {
12 | return findForm(node.parentNode);
13 | }
14 | return node;
15 | }
16 |
17 | window.SelectFilter = {
18 | init: function(field_id, field_name, is_stacked) {
19 | if (field_id.match(/__prefix__/)) {
20 | // Don't initialize on empty forms.
21 | return;
22 | }
23 | var from_box = document.getElementById(field_id);
24 | from_box.id += '_from'; // change its ID
25 | from_box.className = 'filtered';
26 |
27 | var ps = from_box.parentNode.getElementsByTagName('p');
28 | for (var i = 0; i < ps.length; i++) {
29 | if (ps[i].className.indexOf("info") !== -1) {
30 | // Remove , because it just gets in the way.
31 | from_box.parentNode.removeChild(ps[i]);
32 | } else if (ps[i].className.indexOf("help") !== -1) {
33 | // Move help text up to the top so it isn't below the select
34 | // boxes or wrapped off on the side to the right of the add
35 | // button:
36 | from_box.parentNode.insertBefore(ps[i], from_box.parentNode.firstChild);
37 | }
38 | }
39 |
40 | // or
41 | var selector_div = quickElement('div', from_box.parentNode);
42 | selector_div.className = is_stacked ? 'selector stacked' : 'selector';
43 |
44 | //
45 | var selector_available = quickElement('div', selector_div);
46 | selector_available.className = 'selector-available';
47 | var title_available = quickElement('h2', selector_available, interpolate(gettext('Available %s') + ' ', [field_name]));
48 | quickElement(
49 | 'span', title_available, '',
50 | 'class', 'help help-tooltip help-icon',
51 | 'title', interpolate(
52 | gettext(
53 | 'This is the list of available %s. You may choose some by ' +
54 | 'selecting them in the box below and then clicking the ' +
55 | '"Choose" arrow between the two boxes.'
56 | ),
57 | [field_name]
58 | )
59 | );
60 |
61 | var filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter');
62 | filter_p.className = 'selector-filter';
63 |
64 | var search_filter_label = quickElement('label', filter_p, '', 'for', field_id + '_input');
65 |
66 | quickElement(
67 | 'span', search_filter_label, '',
68 | 'class', 'help-tooltip search-label-icon',
69 | 'title', interpolate(gettext("Type into this box to filter down the list of available %s."), [field_name])
70 | );
71 |
72 | filter_p.appendChild(document.createTextNode(' '));
73 |
74 | var filter_input = quickElement('input', filter_p, '', 'type', 'text', 'placeholder', gettext("Filter"));
75 | filter_input.id = field_id + '_input';
76 |
77 | selector_available.appendChild(from_box);
78 | var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', '#', 'id', field_id + '_add_all_link');
79 | choose_all.className = 'selector-chooseall';
80 |
81 | //
82 | var selector_chooser = quickElement('ul', selector_div);
83 | selector_chooser.className = 'selector-chooser';
84 | var add_link = quickElement('a', quickElement('li', selector_chooser), gettext('Choose'), 'title', gettext('Choose'), 'href', '#', 'id', field_id + '_add_link');
85 | add_link.className = 'selector-add';
86 | var remove_link = quickElement('a', quickElement('li', selector_chooser), gettext('Remove'), 'title', gettext('Remove'), 'href', '#', 'id', field_id + '_remove_link');
87 | remove_link.className = 'selector-remove';
88 |
89 | //
90 | var selector_chosen = quickElement('div', selector_div);
91 | selector_chosen.className = 'selector-chosen';
92 | var title_chosen = quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s') + ' ', [field_name]));
93 | quickElement(
94 | 'span', title_chosen, '',
95 | 'class', 'help help-tooltip help-icon',
96 | 'title', interpolate(
97 | gettext(
98 | 'This is the list of chosen %s. You may remove some by ' +
99 | 'selecting them in the box below and then clicking the ' +
100 | '"Remove" arrow between the two boxes.'
101 | ),
102 | [field_name]
103 | )
104 | );
105 |
106 | var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
107 | to_box.className = 'filtered';
108 | var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', '#', 'id', field_id + '_remove_all_link');
109 | clear_all.className = 'selector-clearall';
110 |
111 | from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
112 |
113 | // Set up the JavaScript event handlers for the select box filter interface
114 | var move_selection = function(e, elem, move_func, from, to) {
115 | if (elem.className.indexOf('active') !== -1) {
116 | move_func(from, to);
117 | SelectFilter.refresh_icons(field_id);
118 | }
119 | e.preventDefault();
120 | };
121 | addEvent(choose_all, 'click', function(e) { move_selection(e, this, SelectBox.move_all, field_id + '_from', field_id + '_to'); });
122 | addEvent(add_link, 'click', function(e) { move_selection(e, this, SelectBox.move, field_id + '_from', field_id + '_to'); });
123 | addEvent(remove_link, 'click', function(e) { move_selection(e, this, SelectBox.move, field_id + '_to', field_id + '_from'); });
124 | addEvent(clear_all, 'click', function(e) { move_selection(e, this, SelectBox.move_all, field_id + '_to', field_id + '_from'); });
125 | addEvent(filter_input, 'keypress', function(e) { SelectFilter.filter_key_press(e, field_id); });
126 | addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
127 | addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
128 | addEvent(selector_div, 'change', function(e) {
129 | if (e.target.tagName === 'SELECT') {
130 | SelectFilter.refresh_icons(field_id);
131 | }
132 | });
133 | addEvent(selector_div, 'dblclick', function(e) {
134 | if (e.target.tagName === 'OPTION') {
135 | if (e.target.closest('select').id === field_id + '_to') {
136 | SelectBox.move(field_id + '_to', field_id + '_from');
137 | } else {
138 | SelectBox.move(field_id + '_from', field_id + '_to');
139 | }
140 | SelectFilter.refresh_icons(field_id);
141 | }
142 | });
143 | addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
144 | SelectBox.init(field_id + '_from');
145 | SelectBox.init(field_id + '_to');
146 | // Move selected from_box options to to_box
147 | SelectBox.move(field_id + '_from', field_id + '_to');
148 |
149 | if (!is_stacked) {
150 | // In horizontal mode, give the same height to the two boxes.
151 | var j_from_box = $(from_box);
152 | var j_to_box = $(to_box);
153 | var resize_filters = function() { j_to_box.height($(filter_p).outerHeight() + j_from_box.outerHeight()); };
154 | if (j_from_box.outerHeight() > 0) {
155 | resize_filters(); // This fieldset is already open. Resize now.
156 | } else {
157 | // This fieldset is probably collapsed. Wait for its 'show' event.
158 | j_to_box.closest('fieldset').one('show.fieldset', resize_filters);
159 | }
160 | }
161 |
162 | // Initial icon refresh
163 | SelectFilter.refresh_icons(field_id);
164 | },
165 | any_selected: function(field) {
166 | var any_selected = false;
167 | try {
168 | // Temporarily add the required attribute and check validity.
169 | // This is much faster in WebKit browsers than the fallback.
170 | field.attr('required', 'required');
171 | any_selected = field.is(':valid');
172 | field.removeAttr('required');
173 | } catch (e) {
174 | // Browsers that don't support :valid (IE < 10)
175 | any_selected = field.find('option:selected').length > 0;
176 | }
177 | return any_selected;
178 | },
179 | refresh_icons: function(field_id) {
180 | var from = $('#' + field_id + '_from');
181 | var to = $('#' + field_id + '_to');
182 | // Active if at least one item is selected
183 | $('#' + field_id + '_add_link').toggleClass('active', SelectFilter.any_selected(from));
184 | $('#' + field_id + '_remove_link').toggleClass('active', SelectFilter.any_selected(to));
185 | // Active if the corresponding box isn't empty
186 | $('#' + field_id + '_add_all_link').toggleClass('active', from.find('option').length > 0);
187 | $('#' + field_id + '_remove_all_link').toggleClass('active', to.find('option').length > 0);
188 | },
189 | filter_key_press: function(event, field_id) {
190 | var from = document.getElementById(field_id + '_from');
191 | // don't submit form if user pressed Enter
192 | if ((event.which && event.which === 13) || (event.keyCode && event.keyCode === 13)) {
193 | from.selectedIndex = 0;
194 | SelectBox.move(field_id + '_from', field_id + '_to');
195 | from.selectedIndex = 0;
196 | event.preventDefault();
197 | return false;
198 | }
199 | },
200 | filter_key_up: function(event, field_id) {
201 | var from = document.getElementById(field_id + '_from');
202 | var temp = from.selectedIndex;
203 | SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
204 | from.selectedIndex = temp;
205 | return true;
206 | },
207 | filter_key_down: function(event, field_id) {
208 | var from = document.getElementById(field_id + '_from');
209 | // right arrow -- move across
210 | if ((event.which && event.which === 39) || (event.keyCode && event.keyCode === 39)) {
211 | var old_index = from.selectedIndex;
212 | SelectBox.move(field_id + '_from', field_id + '_to');
213 | from.selectedIndex = (old_index === from.length) ? from.length - 1 : old_index;
214 | return false;
215 | }
216 | // down arrow -- wrap around
217 | if ((event.which && event.which === 40) || (event.keyCode && event.keyCode === 40)) {
218 | from.selectedIndex = (from.length === from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
219 | }
220 | // up arrow -- wrap around
221 | if ((event.which && event.which === 38) || (event.keyCode && event.keyCode === 38)) {
222 | from.selectedIndex = (from.selectedIndex === 0) ? from.length - 1 : from.selectedIndex - 1;
223 | }
224 | return true;
225 | }
226 | };
227 |
228 | addEvent(window, 'load', function(e) {
229 | $('select.selectfilter, select.selectfilterstacked').each(function() {
230 | var $el = $(this),
231 | data = $el.data();
232 | SelectFilter.init($el.attr('id'), data.fieldName, parseInt(data.isStacked, 10));
233 | });
234 | });
235 |
236 | })(django.jQuery);
237 |
--------------------------------------------------------------------------------
/static/admin/js/inlines.js:
--------------------------------------------------------------------------------
1 | /*global DateTimeShortcuts, SelectFilter*/
2 | /**
3 | * Django admin inlines
4 | *
5 | * Based on jQuery Formset 1.1
6 | * @author Stanislaus Madueke (stan DOT madueke AT gmail DOT com)
7 | * @requires jQuery 1.2.6 or later
8 | *
9 | * Copyright (c) 2009, Stanislaus Madueke
10 | * All rights reserved.
11 | *
12 | * Spiced up with Code from Zain Memon's GSoC project 2009
13 | * and modified for Django by Jannis Leidel, Travis Swicegood and Julien Phalip.
14 | *
15 | * Licensed under the New BSD License
16 | * See: http://www.opensource.org/licenses/bsd-license.php
17 | */
18 | (function($) {
19 | 'use strict';
20 | $.fn.formset = function(opts) {
21 | var options = $.extend({}, $.fn.formset.defaults, opts);
22 | var $this = $(this);
23 | var $parent = $this.parent();
24 | var updateElementIndex = function(el, prefix, ndx) {
25 | var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
26 | var replacement = prefix + "-" + ndx;
27 | if ($(el).prop("for")) {
28 | $(el).prop("for", $(el).prop("for").replace(id_regex, replacement));
29 | }
30 | if (el.id) {
31 | el.id = el.id.replace(id_regex, replacement);
32 | }
33 | if (el.name) {
34 | el.name = el.name.replace(id_regex, replacement);
35 | }
36 | };
37 | var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").prop("autocomplete", "off");
38 | var nextIndex = parseInt(totalForms.val(), 10);
39 | var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").prop("autocomplete", "off");
40 | // only show the add button if we are allowed to add more items,
41 | // note that max_num = None translates to a blank string.
42 | var showAddButton = maxForms.val() === '' || (maxForms.val() - totalForms.val()) > 0;
43 | $this.each(function(i) {
44 | $(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
45 | });
46 | if ($this.length && showAddButton) {
47 | var addButton = options.addButton;
48 | if (addButton === null) {
49 | if ($this.prop("tagName") === "TR") {
50 | // If forms are laid out as table rows, insert the
51 | // "add" button in a new table row:
52 | var numCols = this.eq(-1).children().length;
53 | $parent.append(' | ' + options.addText + " | ");
54 | addButton = $parent.find("tr:last a");
55 | } else {
56 | // Otherwise, insert it immediately after the last form:
57 | $this.filter(":last").after(' ");
58 | addButton = $this.filter(":last").next().find("a");
59 | }
60 | }
61 | addButton.click(function(e) {
62 | e.preventDefault();
63 | var template = $("#" + options.prefix + "-empty");
64 | var row = template.clone(true);
65 | row.removeClass(options.emptyCssClass)
66 | .addClass(options.formCssClass)
67 | .attr("id", options.prefix + "-" + nextIndex);
68 | if (row.is("tr")) {
69 | // If the forms are laid out in table rows, insert
70 | // the remove button into the last table cell:
71 | row.children(":last").append(' ");
72 | } else if (row.is("ul") || row.is("ol")) {
73 | // If they're laid out as an ordered/unordered list,
74 | // insert an - after the last list item:
75 | row.append('
- ' + options.deleteText + "
");
76 | } else {
77 | // Otherwise, just insert the remove button as the
78 | // last child element of the form's container:
79 | row.children(":first").append(' ' + options.deleteText + "");
80 | }
81 | row.find("*").each(function() {
82 | updateElementIndex(this, options.prefix, totalForms.val());
83 | });
84 | // Insert the new form when it has been fully edited
85 | row.insertBefore($(template));
86 | // Update number of total forms
87 | $(totalForms).val(parseInt(totalForms.val(), 10) + 1);
88 | nextIndex += 1;
89 | // Hide add button in case we've hit the max, except we want to add infinitely
90 | if ((maxForms.val() !== '') && (maxForms.val() - totalForms.val()) <= 0) {
91 | addButton.parent().hide();
92 | }
93 | // The delete button of each row triggers a bunch of other things
94 | row.find("a." + options.deleteCssClass).click(function(e1) {
95 | e1.preventDefault();
96 | // Remove the parent form containing this button:
97 | row.remove();
98 | nextIndex -= 1;
99 | // If a post-delete callback was provided, call it with the deleted form:
100 | if (options.removed) {
101 | options.removed(row);
102 | }
103 | $(document).trigger('formset:removed', [row, options.prefix]);
104 | // Update the TOTAL_FORMS form count.
105 | var forms = $("." + options.formCssClass);
106 | $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
107 | // Show add button again once we drop below max
108 | if ((maxForms.val() === '') || (maxForms.val() - forms.length) > 0) {
109 | addButton.parent().show();
110 | }
111 | // Also, update names and ids for all remaining form controls
112 | // so they remain in sequence:
113 | var i, formCount;
114 | var updateElementCallback = function() {
115 | updateElementIndex(this, options.prefix, i);
116 | };
117 | for (i = 0, formCount = forms.length; i < formCount; i++) {
118 | updateElementIndex($(forms).get(i), options.prefix, i);
119 | $(forms.get(i)).find("*").each(updateElementCallback);
120 | }
121 | });
122 | // If a post-add callback was supplied, call it with the added form:
123 | if (options.added) {
124 | options.added(row);
125 | }
126 | $(document).trigger('formset:added', [row, options.prefix]);
127 | });
128 | }
129 | return this;
130 | };
131 |
132 | /* Setup plugin defaults */
133 | $.fn.formset.defaults = {
134 | prefix: "form", // The form prefix for your django formset
135 | addText: "add another", // Text for the add link
136 | deleteText: "remove", // Text for the delete link
137 | addCssClass: "add-row", // CSS class applied to the add link
138 | deleteCssClass: "delete-row", // CSS class applied to the delete link
139 | emptyCssClass: "empty-row", // CSS class applied to the empty row
140 | formCssClass: "dynamic-form", // CSS class applied to each form in a formset
141 | added: null, // Function called each time a new form is added
142 | removed: null, // Function called each time a form is deleted
143 | addButton: null // Existing add button to use
144 | };
145 |
146 |
147 | // Tabular inlines ---------------------------------------------------------
148 | $.fn.tabularFormset = function(options) {
149 | var $rows = $(this);
150 | var alternatingRows = function(row) {
151 | $($rows.selector).not(".add-row").removeClass("row1 row2")
152 | .filter(":even").addClass("row1").end()
153 | .filter(":odd").addClass("row2");
154 | };
155 |
156 | var reinitDateTimeShortCuts = function() {
157 | // Reinitialize the calendar and clock widgets by force
158 | if (typeof DateTimeShortcuts !== "undefined") {
159 | $(".datetimeshortcuts").remove();
160 | DateTimeShortcuts.init();
161 | }
162 | };
163 |
164 | var updateSelectFilter = function() {
165 | // If any SelectFilter widgets are a part of the new form,
166 | // instantiate a new SelectFilter instance for it.
167 | if (typeof SelectFilter !== 'undefined') {
168 | $('.selectfilter').each(function(index, value) {
169 | var namearr = value.name.split('-');
170 | SelectFilter.init(value.id, namearr[namearr.length - 1], false);
171 | });
172 | $('.selectfilterstacked').each(function(index, value) {
173 | var namearr = value.name.split('-');
174 | SelectFilter.init(value.id, namearr[namearr.length - 1], true);
175 | });
176 | }
177 | };
178 |
179 | var initPrepopulatedFields = function(row) {
180 | row.find('.prepopulated_field').each(function() {
181 | var field = $(this),
182 | input = field.find('input, select, textarea'),
183 | dependency_list = input.data('dependency_list') || [],
184 | dependencies = [];
185 | $.each(dependency_list, function(i, field_name) {
186 | dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id'));
187 | });
188 | if (dependencies.length) {
189 | input.prepopulate(dependencies, input.attr('maxlength'));
190 | }
191 | });
192 | };
193 |
194 | $rows.formset({
195 | prefix: options.prefix,
196 | addText: options.addText,
197 | formCssClass: "dynamic-" + options.prefix,
198 | deleteCssClass: "inline-deletelink",
199 | deleteText: options.deleteText,
200 | emptyCssClass: "empty-form",
201 | removed: alternatingRows,
202 | added: function(row) {
203 | initPrepopulatedFields(row);
204 | reinitDateTimeShortCuts();
205 | updateSelectFilter();
206 | alternatingRows(row);
207 | },
208 | addButton: options.addButton
209 | });
210 |
211 | return $rows;
212 | };
213 |
214 | // Stacked inlines ---------------------------------------------------------
215 | $.fn.stackedFormset = function(options) {
216 | var $rows = $(this);
217 | var updateInlineLabel = function(row) {
218 | $($rows.selector).find(".inline_label").each(function(i) {
219 | var count = i + 1;
220 | $(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
221 | });
222 | };
223 |
224 | var reinitDateTimeShortCuts = function() {
225 | // Reinitialize the calendar and clock widgets by force, yuck.
226 | if (typeof DateTimeShortcuts !== "undefined") {
227 | $(".datetimeshortcuts").remove();
228 | DateTimeShortcuts.init();
229 | }
230 | };
231 |
232 | var updateSelectFilter = function() {
233 | // If any SelectFilter widgets were added, instantiate a new instance.
234 | if (typeof SelectFilter !== "undefined") {
235 | $(".selectfilter").each(function(index, value) {
236 | var namearr = value.name.split('-');
237 | SelectFilter.init(value.id, namearr[namearr.length - 1], false);
238 | });
239 | $(".selectfilterstacked").each(function(index, value) {
240 | var namearr = value.name.split('-');
241 | SelectFilter.init(value.id, namearr[namearr.length - 1], true);
242 | });
243 | }
244 | };
245 |
246 | var initPrepopulatedFields = function(row) {
247 | row.find('.prepopulated_field').each(function() {
248 | var field = $(this),
249 | input = field.find('input, select, textarea'),
250 | dependency_list = input.data('dependency_list') || [],
251 | dependencies = [];
252 | $.each(dependency_list, function(i, field_name) {
253 | dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id'));
254 | });
255 | if (dependencies.length) {
256 | input.prepopulate(dependencies, input.attr('maxlength'));
257 | }
258 | });
259 | };
260 |
261 | $rows.formset({
262 | prefix: options.prefix,
263 | addText: options.addText,
264 | formCssClass: "dynamic-" + options.prefix,
265 | deleteCssClass: "inline-deletelink",
266 | deleteText: options.deleteText,
267 | emptyCssClass: "empty-form",
268 | removed: updateInlineLabel,
269 | added: function(row) {
270 | initPrepopulatedFields(row);
271 | reinitDateTimeShortCuts();
272 | updateSelectFilter();
273 | updateInlineLabel(row);
274 | },
275 | addButton: options.addButton
276 | });
277 |
278 | return $rows;
279 | };
280 |
281 | $(document).ready(function() {
282 | $(".js-inline-admin-formset").each(function() {
283 | var data = $(this).data(),
284 | inlineOptions = data.inlineFormset;
285 | switch(data.inlineType) {
286 | case "stacked":
287 | $(inlineOptions.name + "-group .inline-related").stackedFormset(inlineOptions.options);
288 | break;
289 | case "tabular":
290 | $(inlineOptions.name + "-group .tabular.inline-related tbody tr").tabularFormset(inlineOptions.options);
291 | break;
292 | }
293 | });
294 | });
295 | })(django.jQuery);
296 |
--------------------------------------------------------------------------------
|