Browse Source

Fixed #22463 -- Added code style guide and JavaScript linting (EditorConfig and ESLint)

Trey Hunner 10 years ago
parent
commit
ec4f219ecb

+ 37 - 0
.editorconfig

@@ -0,0 +1,37 @@
+# http://editorconfig.org
+
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
+end_of_line = lf
+charset = utf-8
+
+# Use 2 spaces for the HTML files
+[*.html]
+indent_size = 2
+
+# The JSON files contain newlines inconsistently
+[*.json]
+indent_size = 2
+insert_final_newline = ignore
+
+[**/admin/js/vendor/**]
+indent_style = ignore
+indent_size = ignore
+
+# Minified JavaScript files shouldn't be changed
+[**.min.js]
+indent_style = ignore
+insert_final_newline = ignore
+
+# Makefiles always use tabs for indentation
+[Makefile]
+indent_style = tab
+
+# Batch files use tabs for indentation
+[*.bat]
+indent_style = tab

+ 3 - 0
.eslintignore

@@ -0,0 +1,3 @@
+**/{*.min,jquery}.js
+django/contrib/gis/templates/**/*.js
+node_modules/**.js

+ 51 - 0
.eslintrc

@@ -0,0 +1,51 @@
+{
+    "rules": {
+        "camelcase": [1, {"properties": "always"}],
+        "comma-spacing": [1, {"before": false, "after": true}],
+        "dot-notation": [1, {"allowKeywords": true}],
+        "curly": [1, "all"],
+        "indent": [
+            2,
+            4
+        ],
+        "key-spacing": [1, {
+            "beforeColon": false,
+            "afterColon": true
+        }],
+        "new-cap": [1, {"newIsCap": true, "capIsNew": true}],
+        "no-alert": [0],
+        "no-eval": [1],
+        "no-extend-native": [2, {"exceptions": ["Date", "String"]}],
+        "no-multi-spaces": [1],
+        "no-octal-escape": [1],
+        "no-underscore-dangle": [1],
+        "no-unused-vars": [2, {"vars": "local", "args": "none"}],
+        "no-script-url": [1],
+        "no-shadow": [1, {"hoist": "functions"}],
+        "quotes": [
+            1,
+            "single"
+        ],
+        "linebreak-style": [
+            2,
+            "unix"
+        ],
+        "semi": [
+            2,
+            "always"
+        ],
+        "space-before-blocks": [2, "always"],
+        "space-before-function-paren": [1, {"anonymous": "always", "named": "never"}],
+        "space-infix-ops": [
+            1,
+            {"int32Hint": false}
+        ],
+        "strict": [1, "function"]
+    },
+    "env": {
+        "browser": true
+    },
+    "globals": {
+        "django": false
+    }
+}

+ 1 - 0
.gitignore

@@ -6,6 +6,7 @@ MANIFEST
 dist/
 docs/_build/
 docs/locale/
+node_modules/
 tests/coverage_html/
 tests/.coverage
 build/

+ 8 - 9
django/contrib/admin/static/admin/js/SelectBox.js

@@ -1,9 +1,10 @@
+/*eslint no-cond-assign:1*/
 var SelectBox = {
-    cache: new Object(),
+    cache: {},
     init: function(id) {
         var box = document.getElementById(id);
         var node;
-        SelectBox.cache[id] = new Array();
+        SelectBox.cache[id] = [];
         var cache = SelectBox.cache[id];
         for (var i = 0; (node = box.options[i]); i++) {
             cache.push({value: node.value, text: node.text, displayed: 1});
@@ -31,7 +32,7 @@ var SelectBox = {
         for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
             node.displayed = 1;
             for (var j = 0; (token = tokens[j]); j++) {
-                if (node.text.toLowerCase().indexOf(token) == -1) {
+                if (node.text.toLowerCase().indexOf(token) === -1) {
                     node.displayed = 0;
                 }
             }
@@ -41,13 +42,13 @@ var SelectBox = {
     delete_from_cache: function(id, value) {
         var node, delete_index = null;
         for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
-            if (node.value == value) {
+            if (node.value === value) {
                 delete_index = i;
                 break;
             }
         }
         var j = SelectBox.cache[id].length - 1;
-        for (var i = delete_index; i < j; i++) {
+        for (i = delete_index; i < j; i++) {
             SelectBox.cache[id][i] = SelectBox.cache[id][i+1];
         }
         SelectBox.cache[id].length--;
@@ -59,7 +60,7 @@ var SelectBox = {
         // Check if an item is contained in the cache
         var node;
         for (var i = 0; (node = SelectBox.cache[id][i]); i++) {
-            if (node.value == value) {
+            if (node.value === value) {
                 return true;
             }
         }
@@ -67,7 +68,6 @@ var SelectBox = {
     },
     move: function(from, to) {
         var from_box = document.getElementById(from);
-        var to_box = document.getElementById(to);
         var option;
         for (var i = 0; (option = from_box.options[i]); i++) {
             if (option.selected && SelectBox.cache_contains(from, option.value)) {
@@ -80,7 +80,6 @@ var SelectBox = {
     },
     move_all: function(from, to) {
         var from_box = document.getElementById(from);
-        var to_box = document.getElementById(to);
         var option;
         for (var i = 0; (option = from_box.options[i]); i++) {
             if (SelectBox.cache_contains(from, option.value)) {
@@ -111,4 +110,4 @@ var SelectBox = {
             box.options[i].selected = 'selected';
         }
     }
-}
+};

+ 183 - 182
django/contrib/admin/static/admin/js/SelectFilter2.js

@@ -1,196 +1,197 @@
+/*global SelectBox, addEvent, gettext, interpolate, quickElement, SelectFilter*/
 /*
 SelectFilter2 - Turns a multiple-select box into a filter interface.
 
 Requires core.js, SelectBox.js and addevent.js.
 */
 (function($) {
-function findForm(node) {
-    // returns the node of the form containing the given node
-    if (node.tagName.toLowerCase() != 'form') {
-        return findForm(node.parentNode);
-    }
-    return node;
-}
-
-window.SelectFilter = {
-    init: function(field_id, field_name, is_stacked) {
-        if (field_id.match(/__prefix__/)){
-            // Don't initialize on empty forms.
-            return;
-        }
-        var from_box = document.getElementById(field_id);
-        from_box.id += '_from'; // change its ID
-        from_box.className = 'filtered';
-
-        var ps = from_box.parentNode.getElementsByTagName('p');
-        for (var i=0; i<ps.length; i++) {
-            if (ps[i].className.indexOf("info") != -1) {
-                // Remove <p class="info">, because it just gets in the way.
-                from_box.parentNode.removeChild(ps[i]);
-            } else if (ps[i].className.indexOf("help") != -1) {
-                // Move help text up to the top so it isn't below the select
-                // boxes or wrapped off on the side to the right of the add
-                // button:
-                from_box.parentNode.insertBefore(ps[i], from_box.parentNode.firstChild);
-            }
+    function findForm(node) {
+        // returns the node of the form containing the given node
+        if (node.tagName.toLowerCase() !== 'form') {
+            return findForm(node.parentNode);
         }
+        return node;
+    }
 
-        // <div class="selector"> or <div class="selector stacked">
-        var selector_div = quickElement('div', from_box.parentNode);
-        selector_div.className = is_stacked ? 'selector stacked' : 'selector';
-
-        // <div class="selector-available">
-        var selector_available = quickElement('div', selector_div);
-        selector_available.className = 'selector-available';
-        var title_available = quickElement('h2', selector_available, interpolate(gettext('Available %s') + ' ', [field_name]));
-        quickElement(
-            'span', title_available, '',
-            'class', 'help help-tooltip help-icon',
-            'title', interpolate(
-                gettext(
-                    'This is the list of available %s. You may choose some by ' +
-                    'selecting them in the box below and then clicking the ' +
-                    '"Choose" arrow between the two boxes.'
-                ),
-                [field_name]
-            )
-        );
-
-        var filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter');
-        filter_p.className = 'selector-filter';
-
-        var search_filter_label = quickElement('label', filter_p, '', 'for', field_id + "_input");
-
-        var search_selector_img = quickElement(
-            'span', search_filter_label, '',
-            'class', 'help-tooltip search-label-icon',
-            'title', interpolate(gettext("Type into this box to filter down the list of available %s."), [field_name])
-        );
-
-        filter_p.appendChild(document.createTextNode(' '));
-
-        var filter_input = quickElement('input', filter_p, '', 'type', 'text', 'placeholder', gettext("Filter"));
-        filter_input.id = field_id + '_input';
-
-        selector_available.appendChild(from_box);
-        var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', 'javascript:void(0);', 'id', field_id + '_add_all_link');
-        choose_all.className = 'selector-chooseall';
-
-        // <ul class="selector-chooser">
-        var selector_chooser = quickElement('ul', selector_div);
-        selector_chooser.className = 'selector-chooser';
-        var add_link = quickElement('a', quickElement('li', selector_chooser), gettext('Choose'), 'title', gettext('Choose'), 'href', 'javascript:void(0);', 'id', field_id + '_add_link');
-        add_link.className = 'selector-add';
-        var remove_link = quickElement('a', quickElement('li', selector_chooser), gettext('Remove'), 'title', gettext('Remove'), 'href', 'javascript:void(0);', 'id', field_id + '_remove_link');
-        remove_link.className = 'selector-remove';
-
-        // <div class="selector-chosen">
-        var selector_chosen = quickElement('div', selector_div);
-        selector_chosen.className = 'selector-chosen';
-        var title_chosen = quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s') + ' ', [field_name]));
-        quickElement(
-            'span', title_chosen, '',
-            'class', 'help help-tooltip help-icon',
-            'title', interpolate(
-                gettext(
-                    'This is the list of chosen %s. You may remove some by ' +
-                    'selecting them in the box below and then clicking the ' +
-                    '"Remove" arrow between the two boxes.'
-                ),
-                [field_name]
-            )
-        );
-
-        var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
-        to_box.className = 'filtered';
-        var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', 'javascript:void(0);', 'id', field_id + '_remove_all_link');
-        clear_all.className = 'selector-clearall';
-
-        from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
-
-        // Set up the JavaScript event handlers for the select box filter interface
-        addEvent(choose_all, 'click', function() { SelectBox.move_all(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
-        addEvent(add_link, 'click', function() { SelectBox.move(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
-        addEvent(remove_link, 'click', function() { SelectBox.move(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
-        addEvent(clear_all, 'click', function() { SelectBox.move_all(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
-        addEvent(filter_input, 'keypress', function(e) { SelectFilter.filter_key_press(e, field_id); });
-        addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
-        addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
-        addEvent(from_box, 'change', function(e) { SelectFilter.refresh_icons(field_id) });
-        addEvent(to_box, 'change', function(e) { SelectFilter.refresh_icons(field_id) });
-        addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
-        addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
-        addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
-        SelectBox.init(field_id + '_from');
-        SelectBox.init(field_id + '_to');
-        // Move selected from_box options to to_box
-        SelectBox.move(field_id + '_from', field_id + '_to');
-
-        if (!is_stacked) {
-            // In horizontal mode, give the same height to the two boxes.
-            var j_from_box = $(from_box);
-            var j_to_box = $(to_box);
-            var resize_filters = function() { j_to_box.height($(filter_p).outerHeight() + j_from_box.outerHeight()); }
-            if (j_from_box.outerHeight() > 0) {
-                resize_filters(); // This fieldset is already open. Resize now.
-            } else {
-                // This fieldset is probably collapsed. Wait for its 'show' event.
-                j_to_box.closest('fieldset').one('show.fieldset', resize_filters);
+    window.SelectFilter = {
+        init: function(field_id, field_name, is_stacked) {
+            if (field_id.match(/__prefix__/)) {
+                // Don't initialize on empty forms.
+                return;
+            }
+            var from_box = document.getElementById(field_id);
+            from_box.id += '_from'; // change its ID
+            from_box.className = 'filtered';
+
+            var ps = from_box.parentNode.getElementsByTagName('p');
+            for (var i=0; i<ps.length; i++) {
+                if (ps[i].className.indexOf("info") !== -1) {
+                    // Remove <p class="info">, because it just gets in the way.
+                    from_box.parentNode.removeChild(ps[i]);
+                } else if (ps[i].className.indexOf("help") !== -1) {
+                    // Move help text up to the top so it isn't below the select
+                    // boxes or wrapped off on the side to the right of the add
+                    // button:
+                    from_box.parentNode.insertBefore(ps[i], from_box.parentNode.firstChild);
+                }
             }
-        }
 
-        // Initial icon refresh
-        SelectFilter.refresh_icons(field_id);
-    },
-    refresh_icons: function(field_id) {
-        var from = $('#' + field_id + '_from');
-        var to = $('#' + field_id + '_to');
-        var is_from_selected = from.find('option:selected').length > 0;
-        var is_to_selected = to.find('option:selected').length > 0;
-        // Active if at least one item is selected
-        $('#' + field_id + '_add_link').toggleClass('active', is_from_selected);
-        $('#' + field_id + '_remove_link').toggleClass('active', is_to_selected);
-        // Active if the corresponding box isn't empty
-        $('#' + field_id + '_add_all_link').toggleClass('active', from.find('option').length > 0);
-        $('#' + field_id + '_remove_all_link').toggleClass('active', to.find('option').length > 0);
-    },
-    filter_key_press: function(event, field_id) {
-        var from = document.getElementById(field_id + '_from');
-        // don't submit form if user pressed Enter
-        if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
-            from.selectedIndex = 0;
-            SelectBox.move(field_id + '_from', field_id + '_to');
-            from.selectedIndex = 0;
-            event.preventDefault()
-            return false;
-        }
-    },
-    filter_key_up: function(event, field_id) {
-        var from = document.getElementById(field_id + '_from');
-        var temp = from.selectedIndex;
-        SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
-        from.selectedIndex = temp;
-        return true;
-    },
-    filter_key_down: function(event, field_id) {
-        var from = document.getElementById(field_id + '_from');
-        // right arrow -- move across
-        if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) {
-            var old_index = from.selectedIndex;
+            // <div class="selector"> or <div class="selector stacked">
+            var selector_div = quickElement('div', from_box.parentNode);
+            selector_div.className = is_stacked ? 'selector stacked' : 'selector';
+
+            // <div class="selector-available">
+            var selector_available = quickElement('div', selector_div);
+            selector_available.className = 'selector-available';
+            var title_available = quickElement('h2', selector_available, interpolate(gettext('Available %s') + ' ', [field_name]));
+            quickElement(
+                'span', title_available, '',
+                'class', 'help help-tooltip help-icon',
+                'title', interpolate(
+                    gettext(
+                        'This is the list of available %s. You may choose some by ' +
+                        'selecting them in the box below and then clicking the ' +
+                        '"Choose" arrow between the two boxes.'
+                    ),
+                    [field_name]
+                )
+            );
+
+            var filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter');
+            filter_p.className = 'selector-filter';
+
+            var search_filter_label = quickElement('label', filter_p, '', 'for', field_id + '_input');
+
+            quickElement(
+                'span', search_filter_label, '',
+                'class', 'help-tooltip search-label-icon',
+                'title', interpolate(gettext("Type into this box to filter down the list of available %s."), [field_name])
+            );
+
+            filter_p.appendChild(document.createTextNode(' '));
+
+            var filter_input = quickElement('input', filter_p, '', 'type', 'text', 'placeholder', gettext("Filter"));
+            filter_input.id = field_id + '_input';
+
+            selector_available.appendChild(from_box);
+            var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', 'javascript:void(0);', 'id', field_id + '_add_all_link');
+            choose_all.className = 'selector-chooseall';
+
+            // <ul class="selector-chooser">
+            var selector_chooser = quickElement('ul', selector_div);
+            selector_chooser.className = 'selector-chooser';
+            var add_link = quickElement('a', quickElement('li', selector_chooser), gettext('Choose'), 'title', gettext('Choose'), 'href', 'javascript:void(0);', 'id', field_id + '_add_link');
+            add_link.className = 'selector-add';
+            var remove_link = quickElement('a', quickElement('li', selector_chooser), gettext('Remove'), 'title', gettext('Remove'), 'href', 'javascript:void(0);', 'id', field_id + '_remove_link');
+            remove_link.className = 'selector-remove';
+
+            // <div class="selector-chosen">
+            var selector_chosen = quickElement('div', selector_div);
+            selector_chosen.className = 'selector-chosen';
+            var title_chosen = quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s') + ' ', [field_name]));
+            quickElement(
+                'span', title_chosen, '',
+                'class', 'help help-tooltip help-icon',
+                'title', interpolate(
+                    gettext(
+                        'This is the list of chosen %s. You may remove some by ' +
+                        'selecting them in the box below and then clicking the ' +
+                        '"Remove" arrow between the two boxes.'
+                    ),
+                    [field_name]
+                )
+            );
+
+            var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
+            to_box.className = 'filtered';
+            var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', 'javascript:void(0);', 'id', field_id + '_remove_all_link');
+            clear_all.className = 'selector-clearall';
+
+            from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
+
+            // Set up the JavaScript event handlers for the select box filter interface
+            addEvent(choose_all, 'click', function() { SelectBox.move_all(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
+            addEvent(add_link, 'click', function() { SelectBox.move(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
+            addEvent(remove_link, 'click', function() { SelectBox.move(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
+            addEvent(clear_all, 'click', function() { SelectBox.move_all(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
+            addEvent(filter_input, 'keypress', function(e) { SelectFilter.filter_key_press(e, field_id); });
+            addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
+            addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
+            addEvent(from_box, 'change', function(e) { SelectFilter.refresh_icons(field_id); });
+            addEvent(to_box, 'change', function(e) { SelectFilter.refresh_icons(field_id); });
+            addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
+            addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
+            addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
+            SelectBox.init(field_id + '_from');
+            SelectBox.init(field_id + '_to');
+            // Move selected from_box options to to_box
             SelectBox.move(field_id + '_from', field_id + '_to');
-            from.selectedIndex = (old_index == from.length) ? from.length - 1 : old_index;
-            return false;
-        }
-        // down arrow -- wrap around
-        if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) {
-            from.selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
-        }
-        // up arrow -- wrap around
-        if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) {
-            from.selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1;
+
+            if (!is_stacked) {
+                // In horizontal mode, give the same height to the two boxes.
+                var j_from_box = $(from_box);
+                var j_to_box = $(to_box);
+                var resize_filters = function() { j_to_box.height($(filter_p).outerHeight() + j_from_box.outerHeight()); };
+                if (j_from_box.outerHeight() > 0) {
+                    resize_filters(); // This fieldset is already open. Resize now.
+                } else {
+                    // This fieldset is probably collapsed. Wait for its 'show' event.
+                    j_to_box.closest('fieldset').one('show.fieldset', resize_filters);
+                }
+            }
+
+            // Initial icon refresh
+            SelectFilter.refresh_icons(field_id);
+        },
+        refresh_icons: function(field_id) {
+            var from = $('#' + field_id + '_from');
+            var to = $('#' + field_id + '_to');
+            var is_from_selected = from.find('option:selected').length > 0;
+            var is_to_selected = to.find('option:selected').length > 0;
+            // Active if at least one item is selected
+            $('#' + field_id + '_add_link').toggleClass('active', is_from_selected);
+            $('#' + field_id + '_remove_link').toggleClass('active', is_to_selected);
+            // Active if the corresponding box isn't empty
+            $('#' + field_id + '_add_all_link').toggleClass('active', from.find('option').length > 0);
+            $('#' + field_id + '_remove_all_link').toggleClass('active', to.find('option').length > 0);
+        },
+        filter_key_press: function(event, field_id) {
+            var from = document.getElementById(field_id + '_from');
+            // don't submit form if user pressed Enter
+            if ((event.which && event.which === 13) || (event.keyCode && event.keyCode === 13)) {
+                from.selectedIndex = 0;
+                SelectBox.move(field_id + '_from', field_id + '_to');
+                from.selectedIndex = 0;
+                event.preventDefault();
+                return false;
+            }
+        },
+        filter_key_up: function(event, field_id) {
+            var from = document.getElementById(field_id + '_from');
+            var temp = from.selectedIndex;
+            SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
+            from.selectedIndex = temp;
+            return true;
+        },
+        filter_key_down: function(event, field_id) {
+            var from = document.getElementById(field_id + '_from');
+            // right arrow -- move across
+            if ((event.which && event.which === 39) || (event.keyCode && event.keyCode === 39)) {
+                var old_index = from.selectedIndex;
+                SelectBox.move(field_id + '_from', field_id + '_to');
+                from.selectedIndex = (old_index === from.length) ? from.length - 1 : old_index;
+                return false;
+            }
+            // down arrow -- wrap around
+            if ((event.which && event.which === 40) || (event.keyCode && event.keyCode === 40)) {
+                from.selectedIndex = (from.length === from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
+            }
+            // up arrow -- wrap around
+            if ((event.which && event.which === 38) || (event.keyCode && event.keyCode === 38)) {
+                from.selectedIndex = (from.selectedIndex === 0) ? from.length - 1 : from.selectedIndex - 1;
+            }
+            return true;
         }
-        return true;
-    }
-}
+    };
 
 })(django.jQuery);

+ 142 - 141
django/contrib/admin/static/admin/js/actions.js

@@ -1,144 +1,145 @@
+/*global _actions_icnt, gettext, interpolate, ngettext*/
 (function($) {
-	var lastChecked;
+    var lastChecked;
 
-	$.fn.actions = function(opts) {
-		var options = $.extend({}, $.fn.actions.defaults, opts);
-		var actionCheckboxes = $(this);
-		var list_editable_changed = false;
-		var checker = function(checked) {
-			if (checked) {
-				showQuestion();
-			} else {
-				reset();
-			}
-			$(actionCheckboxes).prop("checked", checked)
-				.parent().parent().toggleClass(options.selectedClass, checked);
-		},
-		updateCounter = function() {
-			var sel = $(actionCheckboxes).filter(":checked").length;
-			// _actions_icnt is defined in the generated HTML
-			// and contains the total amount of objects in the queryset
-			$(options.counterContainer).html(interpolate(
-			ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
-				sel: sel,
-				cnt: _actions_icnt
-			}, true));
-			$(options.allToggle).prop("checked", function() {
-				var value;
-				if (sel == actionCheckboxes.length) {
-					value = true;
-					showQuestion();
-				} else {
-					value = false;
-					clearAcross();
-				}
-				return value;
-			});
-		},
-		showQuestion = function() {
-			$(options.acrossClears).hide();
-			$(options.acrossQuestions).show();
-			$(options.allContainer).hide();
-		},
-		showClear = function() {
-			$(options.acrossClears).show();
-			$(options.acrossQuestions).hide();
-			$(options.actionContainer).toggleClass(options.selectedClass);
-			$(options.allContainer).show();
-			$(options.counterContainer).hide();
-		},
-		reset = function() {
-			$(options.acrossClears).hide();
-			$(options.acrossQuestions).hide();
-			$(options.allContainer).hide();
-			$(options.counterContainer).show();
-		},
-		clearAcross = function() {
-			reset();
-			$(options.acrossInput).val(0);
-			$(options.actionContainer).removeClass(options.selectedClass);
-		};
-		// Show counter by default
-		$(options.counterContainer).show();
-		// Check state of checkboxes and reinit state if needed
-		$(this).filter(":checked").each(function(i) {
-			$(this).parent().parent().toggleClass(options.selectedClass);
-			updateCounter();
-			if ($(options.acrossInput).val() == 1) {
-				showClear();
-			}
-		});
-		$(options.allToggle).show().click(function() {
-			checker($(this).prop("checked"));
-			updateCounter();
-		});
-		$("a", options.acrossQuestions).click(function(event) {
-			event.preventDefault();
-			$(options.acrossInput).val(1);
-			showClear();
-		});
-		$("a", options.acrossClears).click(function(event) {
-			event.preventDefault();
-			$(options.allToggle).prop("checked", false);
-			clearAcross();
-			checker(0);
-			updateCounter();
-		});
-		lastChecked = null;
-		$(actionCheckboxes).click(function(event) {
-			if (!event) { event = window.event; }
-			var target = event.target ? event.target : event.srcElement;
-			if (lastChecked && $.data(lastChecked) != $.data(target) && event.shiftKey === true) {
-				var inrange = false;
-				$(lastChecked).prop("checked", target.checked)
-					.parent().parent().toggleClass(options.selectedClass, target.checked);
-				$(actionCheckboxes).each(function() {
-					if ($.data(this) == $.data(lastChecked) || $.data(this) == $.data(target)) {
-						inrange = (inrange) ? false : true;
-					}
-					if (inrange) {
-						$(this).prop("checked", target.checked)
-							.parent().parent().toggleClass(options.selectedClass, target.checked);
-					}
-				});
-			}
-			$(target).parent().parent().toggleClass(options.selectedClass, target.checked);
-			lastChecked = target;
-			updateCounter();
-		});
-		$('form#changelist-form table#result_list tr').find('td:gt(0) :input').change(function() {
-			list_editable_changed = true;
-		});
-		$('form#changelist-form button[name="index"]').click(function(event) {
-			if (list_editable_changed) {
-				return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
-			}
-		});
-		$('form#changelist-form input[name="_save"]').click(function(event) {
-			var action_changed = false;
-			$('select option:selected', options.actionContainer).each(function() {
-				if ($(this).val()) {
-					action_changed = true;
-				}
-			});
-			if (action_changed) {
-				if (list_editable_changed) {
-					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."));
-				} else {
-					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."));
-				}
-			}
-		});
-	};
-	/* Setup plugin defaults */
-	$.fn.actions.defaults = {
-		actionContainer: "div.actions",
-		counterContainer: "span.action-counter",
-		allContainer: "div.actions span.all",
-		acrossInput: "div.actions input.select-across",
-		acrossQuestions: "div.actions span.question",
-		acrossClears: "div.actions span.clear",
-		allToggle: "#action-toggle",
-		selectedClass: "selected"
-	};
+    $.fn.actions = function(opts) {
+        var options = $.extend({}, $.fn.actions.defaults, opts);
+        var actionCheckboxes = $(this);
+        var list_editable_changed = false;
+        var showQuestion = function() {
+            $(options.acrossClears).hide();
+            $(options.acrossQuestions).show();
+            $(options.allContainer).hide();
+        },
+        showClear = function() {
+            $(options.acrossClears).show();
+            $(options.acrossQuestions).hide();
+            $(options.actionContainer).toggleClass(options.selectedClass);
+            $(options.allContainer).show();
+            $(options.counterContainer).hide();
+        },
+        reset = function() {
+            $(options.acrossClears).hide();
+            $(options.acrossQuestions).hide();
+            $(options.allContainer).hide();
+            $(options.counterContainer).show();
+        },
+        clearAcross = function() {
+            reset();
+            $(options.acrossInput).val(0);
+            $(options.actionContainer).removeClass(options.selectedClass);
+        },
+        checker = function(checked) {
+            if (checked) {
+                showQuestion();
+            } else {
+                reset();
+            }
+            $(actionCheckboxes).prop("checked", checked)
+                .parent().parent().toggleClass(options.selectedClass, checked);
+        },
+        updateCounter = function() {
+            var sel = $(actionCheckboxes).filter(":checked").length;
+            // _actions_icnt is defined in the generated HTML
+            // and contains the total amount of objects in the queryset
+            $(options.counterContainer).html(interpolate(
+            ngettext('%(sel)s of %(cnt)s selected', '%(sel)s of %(cnt)s selected', sel), {
+                sel: sel,
+                cnt: _actions_icnt
+            }, true));
+            $(options.allToggle).prop("checked", function() {
+                var value;
+                if (sel === actionCheckboxes.length) {
+                    value = true;
+                    showQuestion();
+                } else {
+                    value = false;
+                    clearAcross();
+                }
+                return value;
+            });
+        };
+        // Show counter by default
+        $(options.counterContainer).show();
+        // Check state of checkboxes and reinit state if needed
+        $(this).filter(":checked").each(function(i) {
+            $(this).parent().parent().toggleClass(options.selectedClass);
+            updateCounter();
+            if ($(options.acrossInput).val() === 1) {
+                showClear();
+            }
+        });
+        $(options.allToggle).show().click(function() {
+            checker($(this).prop("checked"));
+            updateCounter();
+        });
+        $("a", options.acrossQuestions).click(function(event) {
+            event.preventDefault();
+            $(options.acrossInput).val(1);
+            showClear();
+        });
+        $("a", options.acrossClears).click(function(event) {
+            event.preventDefault();
+            $(options.allToggle).prop("checked", false);
+            clearAcross();
+            checker(0);
+            updateCounter();
+        });
+        lastChecked = null;
+        $(actionCheckboxes).click(function(event) {
+            if (!event) { event = window.event; }
+            var target = event.target ? event.target : event.srcElement;
+            if (lastChecked && $.data(lastChecked) !== $.data(target) && event.shiftKey === true) {
+                var inrange = false;
+                $(lastChecked).prop("checked", target.checked)
+                    .parent().parent().toggleClass(options.selectedClass, target.checked);
+                $(actionCheckboxes).each(function() {
+                    if ($.data(this) === $.data(lastChecked) || $.data(this) === $.data(target)) {
+                        inrange = (inrange) ? false : true;
+                    }
+                    if (inrange) {
+                        $(this).prop("checked", target.checked)
+                            .parent().parent().toggleClass(options.selectedClass, target.checked);
+                    }
+                });
+            }
+            $(target).parent().parent().toggleClass(options.selectedClass, target.checked);
+            lastChecked = target;
+            updateCounter();
+        });
+        $('form#changelist-form table#result_list tr').find('td:gt(0) :input').change(function() {
+            list_editable_changed = true;
+        });
+        $('form#changelist-form button[name="index"]').click(function(event) {
+            if (list_editable_changed) {
+                return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
+            }
+        });
+        $('form#changelist-form input[name="_save"]').click(function(event) {
+            var action_changed = false;
+            $('select option:selected', options.actionContainer).each(function() {
+                if ($(this).val()) {
+                    action_changed = true;
+                }
+            });
+            if (action_changed) {
+                if (list_editable_changed) {
+                    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."));
+                } else {
+                    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."));
+                }
+            }
+        });
+    };
+    /* Setup plugin defaults */
+    $.fn.actions.defaults = {
+        actionContainer: "div.actions",
+        counterContainer: "span.action-counter",
+        allContainer: "div.actions span.all",
+        acrossInput: "div.actions input.select-across",
+        acrossQuestions: "div.actions span.question",
+        acrossClears: "div.actions span.clear",
+        allToggle: "#action-toggle",
+        selectedClass: "selected"
+    };
 })(django.jQuery);

+ 35 - 34
django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js

@@ -1,3 +1,4 @@
+/*global addEvent, Calendar, cancelEventPropagation, findPosX, findPosY, getStyle, get_format, gettext, interpolate, ngettext, quickElement, removeEvent*/
 // Inserts shortcut buttons after all of the following:
 //     <input type="text" class="vDateField">
 //     <input type="text" class="vTimeField">
@@ -17,20 +18,20 @@ var DateTimeShortcuts = {
     timezoneWarningClass: 'timezonewarning', // class of the warning for timezone mismatch
     timezoneOffset: 0,
     init: function() {
-        if (window.__admin_utc_offset__ != undefined) {
+        if (window.__admin_utc_offset__ !== undefined) {
             var serverOffset = window.__admin_utc_offset__;
             var localOffset = new Date().getTimezoneOffset() * -60;
             DateTimeShortcuts.timezoneOffset = localOffset - serverOffset;
         }
 
         var inputs = document.getElementsByTagName('input');
-        for (i=0; i<inputs.length; i++) {
+        for (var i=0; i<inputs.length; i++) {
             var inp = inputs[i];
-            if (inp.getAttribute('type') == 'text' && inp.className.match(/vTimeField/)) {
+            if (inp.getAttribute('type') === 'text' && inp.className.match(/vTimeField/)) {
                 DateTimeShortcuts.addClock(inp);
                 DateTimeShortcuts.addTimezoneWarning(inp);
             }
-            else if (inp.getAttribute('type') == 'text' && inp.className.match(/vDateField/)) {
+            else if (inp.getAttribute('type') === 'text' && inp.className.match(/vDateField/)) {
                 DateTimeShortcuts.addCalendar(inp);
                 DateTimeShortcuts.addTimezoneWarning(inp);
             }
@@ -38,7 +39,7 @@ var DateTimeShortcuts = {
     },
     // Return the current time while accounting for the server timezone.
     now: function() {
-        if (window.__admin_utc_offset__ != undefined) {
+        if (window.__admin_utc_offset__ !== undefined) {
             var serverOffset = window.__admin_utc_offset__;
             var localNow = new Date();
             var localOffset = localNow.getTimezoneOffset() * -60;
@@ -71,7 +72,7 @@ var DateTimeShortcuts = {
             );
         }
         else {
-            timezoneOffset *= -1
+            timezoneOffset *= -1;
             message = ngettext(
                 'Note: You are %s hour behind server time.',
                 'Note: You are %s hours behind server time.',
@@ -86,7 +87,7 @@ var DateTimeShortcuts = {
 
         $(inp).parent()
             .append($('<br>'))
-            .append($warning)
+            .append($warning);
     },
     // Add clock widget to a given field
     addClock: function(inp) {
@@ -150,7 +151,7 @@ var DateTimeShortcuts = {
         cancel_p.className = 'calendar-cancel';
         quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');');
         django.jQuery(document).bind('keyup', function(event) {
-            if (event.which == 27) {
+            if (event.which === 27) {
                 // ESC key closes popup
                 DateTimeShortcuts.dismissClock(num);
                 event.preventDefault();
@@ -158,12 +159,12 @@ var DateTimeShortcuts = {
         });
     },
     openClock: function(num) {
-        var clock_box = document.getElementById(DateTimeShortcuts.clockDivName+num)
-        var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName+num)
+        var clock_box = document.getElementById(DateTimeShortcuts.clockDivName+num);
+        var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName+num);
 
         // Recalculate the clockbox position
         // is it left-to-right or right-to-left layout ?
-        if (getStyle(document.body,'direction')!='rtl') {
+        if (getStyle(document.body,'direction')!=='rtl') {
             clock_box.style.left = findPosX(clock_link) + 17 + 'px';
         }
         else {
@@ -180,20 +181,20 @@ var DateTimeShortcuts = {
         addEvent(document, 'click', DateTimeShortcuts.dismissClockFunc[num]);
     },
     dismissClock: function(num) {
-       document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none';
-       removeEvent(document, 'click', DateTimeShortcuts.dismissClockFunc[num]);
+        document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'none';
+        removeEvent(document, 'click', DateTimeShortcuts.dismissClockFunc[num]);
     },
     handleClockQuicklink: function(num, val) {
-       var d;
-       if (val == -1) {
-           d = DateTimeShortcuts.now();
-       }
-       else {
-           d = new Date(1970, 1, 1, val, 0, 0, 0)
-       }
-       DateTimeShortcuts.clockInputs[num].value = d.strftime(get_format('TIME_INPUT_FORMATS')[0]);
-       DateTimeShortcuts.clockInputs[num].focus();
-       DateTimeShortcuts.dismissClock(num);
+        var d;
+        if (val === -1) {
+            d = DateTimeShortcuts.now();
+        }
+        else {
+            d = new Date(1970, 1, 1, val, 0, 0, 0);
+        }
+        DateTimeShortcuts.clockInputs[num].value = d.strftime(get_format('TIME_INPUT_FORMATS')[0]);
+        DateTimeShortcuts.clockInputs[num].focus();
+        DateTimeShortcuts.dismissClock(num);
     },
     // Add calendar widget to a given field.
     addCalendar: function(inp) {
@@ -274,7 +275,7 @@ var DateTimeShortcuts = {
         cancel_p.className = 'calendar-cancel';
         quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');');
         django.jQuery(document).bind('keyup', function(event) {
-            if (event.which == 27) {
+            if (event.which === 27) {
                 // ESC key closes popup
                 DateTimeShortcuts.dismissCalendar(num);
                 event.preventDefault();
@@ -282,8 +283,8 @@ var DateTimeShortcuts = {
         });
     },
     openCalendar: function(num) {
-        var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)
-        var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num)
+        var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num);
+        var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num);
         var inp = DateTimeShortcuts.calendarInputs[num];
 
         // Determine if the current value in the input has a valid date.
@@ -293,7 +294,7 @@ var DateTimeShortcuts = {
             var selected = inp.value.strptime(format);
             var year = selected.getFullYear();
             var month = selected.getMonth() + 1;
-            var re = /\d{4}/
+            var re = /\d{4}/;
             if (re.test(year.toString()) && month >= 1 && month <= 12) {
                 DateTimeShortcuts.calendars[num].drawDate(month, year, selected);
             }
@@ -301,7 +302,7 @@ var DateTimeShortcuts = {
 
         // Recalculate the clockbox position
         // is it left-to-right or right-to-left layout ?
-        if (getStyle(document.body,'direction')!='rtl') {
+        if (getStyle(document.body,'direction')!=='rtl') {
             cal_box.style.left = findPosX(cal_link) + 17 + 'px';
         }
         else {
@@ -345,12 +346,12 @@ var DateTimeShortcuts = {
                ").style.display='none';}"].join('');
     },
     handleCalendarQuickLink: function(num, offset) {
-       var d = DateTimeShortcuts.now();
-       d.setDate(d.getDate() + offset)
-       DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]);
-       DateTimeShortcuts.calendarInputs[num].focus();
-       DateTimeShortcuts.dismissCalendar(num);
+        var d = DateTimeShortcuts.now();
+        d.setDate(d.getDate() + offset);
+        DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]);
+        DateTimeShortcuts.calendarInputs[num].focus();
+        DateTimeShortcuts.dismissCalendar(num);
     }
-}
+};
 
 addEvent(window, 'load', DateTimeShortcuts.init);

+ 12 - 11
django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js

@@ -1,3 +1,4 @@
+/*global SelectBox, interpolate*/
 // Handles related-objects functionality: lookup link for raw_id_fields
 // and Add Another links.
 
@@ -32,7 +33,7 @@ function showAdminPopup(triggeringLink, name_regexp, add_popup) {
     name = id_to_windowname(name);
     var href = triggeringLink.href;
     if (add_popup) {
-        if (href.indexOf('?') == -1) {
+        if (href.indexOf('?') === -1) {
             href += '?_popup=1';
         } else {
             href += '&_popup=1';
@@ -50,7 +51,7 @@ function showRelatedObjectLookupPopup(triggeringLink) {
 function dismissRelatedLookupPopup(win, chosenId) {
     var name = windowname_to_id(win.name);
     var elem = document.getElementById(name);
-    if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
+    if (elem.className.indexOf('vManyToManyRawIdAdminField') !== -1 && elem.value) {
         elem.value += ',' + chosenId;
     } else {
         document.getElementById(name).value = chosenId;
@@ -86,10 +87,10 @@ function dismissAddRelatedObjectPopup(win, newId, newRepr) {
     var elem = document.getElementById(name);
     if (elem) {
         var elemName = elem.nodeName.toUpperCase();
-        if (elemName == 'SELECT') {
+        if (elemName === 'SELECT') {
             elem.options[elem.options.length] = new Option(newRepr, newId, true, true);
-        } else if (elemName == 'INPUT') {
-            if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
+        } else if (elemName === 'INPUT') {
+            if (elem.className.indexOf('vManyToManyRawIdAdminField') !== -1 && elem.value) {
                 elem.value += ',' + newId;
             } else {
                 elem.value = newId;
@@ -113,13 +114,13 @@ function dismissChangeRelatedObjectPopup(win, objId, newRepr, newId) {
     var selectsSelector = interpolate('#%s, #%s_from, #%s_to', [id, id, id]);
     var selects = django.jQuery(selectsSelector);
     selects.find('option').each(function() {
-        if (this.value == objId) {
+        if (this.value === objId) {
             this.innerHTML = newRepr;
             this.value = newId;
         }
     });
     win.close();
-};
+}
 
 function dismissDeleteRelatedObjectPopup(win, objId) {
     objId = html_unescape(objId);
@@ -127,13 +128,13 @@ function dismissDeleteRelatedObjectPopup(win, objId) {
     var selectsSelector = interpolate('#%s, #%s_from, #%s_to', [id, id, id]);
     var selects = django.jQuery(selectsSelector);
     selects.find('option').each(function() {
-        if (this.value == objId) {
+        if (this.value === objId) {
             django.jQuery(this).remove();
         }
     }).trigger('change');
     win.close();
-};
+}
 
 // Kept for backward compatibility
-showAddAnotherPopup = showRelatedObjectPopup;
-dismissAddAnotherPopup = dismissAddRelatedObjectPopup;
+var showAddAnotherPopup = showRelatedObjectPopup;
+var dismissAddAnotherPopup = dismissAddRelatedObjectPopup;

+ 22 - 19
django/contrib/admin/static/admin/js/calendar.js

@@ -1,3 +1,4 @@
+/*global gettext, get_format, quickElement, removeChildren*/
 /*
 calendar.js - Calendar functions by Adrian Holovaty
 depends on core.js for utility functions like removeChildren or quickElement
@@ -9,17 +10,17 @@ var CalendarNamespace = {
     daysOfWeek: gettext('S M T W T F S').split(' '),
     firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')),
     isLeapYear: function(year) {
-        return (((year % 4)==0) && ((year % 100)!=0) || ((year % 400)==0));
+        return (((year % 4)===0) && ((year % 100)!==0) || ((year % 400)===0));
     },
     getDaysInMonth: function(month,year) {
         var days;
-        if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) {
+        if (month===1 || month===3 || month===5 || month===7 || month===8 || month===10 || month===12) {
             days = 31;
         }
-        else if (month==4 || month==6 || month==9 || month==11) {
+        else if (month===4 || month===6 || month===9 || month===11) {
             days = 30;
         }
-        else if (month==2 && CalendarNamespace.isLeapYear(year)) {
+        else if (month===2 && CalendarNamespace.isLeapYear(year)) {
             days = 29;
         }
         else {
@@ -46,8 +47,8 @@ var CalendarNamespace = {
         // The day variable above will be 1 instead of 2 in, say, US Pacific time
         // zone.
         var isSelectedMonth = false;
-        if (typeof selected != 'undefined') {
-            isSelectedMonth = (selected.getUTCFullYear() == year && (selected.getUTCMonth()+1) == month);
+        if (typeof selected !== 'undefined') {
+            isSelectedMonth = (selected.getUTCFullYear() === year && (selected.getUTCMonth()+1) === month);
         }
 
         month = parseInt(month);
@@ -67,28 +68,30 @@ var CalendarNamespace = {
         var startingPos = new Date(year, month-1, 1 - CalendarNamespace.firstDayOfWeek).getDay();
         var days = CalendarNamespace.getDaysInMonth(month, year);
 
+        var _cell;
+
         // Draw blanks before first of month
         tableRow = quickElement('tr', tableBody);
-        for (var i = 0; i < startingPos; i++) {
-            var _cell = quickElement('td', tableRow, ' ');
+        for (i = 0; i < startingPos; i++) {
+            _cell = quickElement('td', tableRow, ' ');
             _cell.className = "nonday";
         }
 
         // Draw days of month
         var currentDay = 1;
-        for (var i = startingPos; currentDay <= days; i++) {
-            if (i%7 == 0 && currentDay != 1) {
+        for (i = startingPos; currentDay <= days; i++) {
+            if (i%7 === 0 && currentDay !== 1) {
                 tableRow = quickElement('tr', tableBody);
             }
-            if ((currentDay==todayDay) && (month==todayMonth) && (year==todayYear)) {
+            if ((currentDay===todayDay) && (month===todayMonth) && (year===todayYear)) {
                 todayClass='today';
             } else {
                 todayClass='';
             }
 
             // use UTC function; see above for explanation.
-            if (isSelectedMonth && currentDay == selected.getUTCDate()) {
-                if (todayClass != '') todayClass += " ";
+            if (isSelectedMonth && currentDay === selected.getUTCDate()) {
+                if (todayClass !== '') todayClass += " ";
                 todayClass += "selected";
             }
 
@@ -100,13 +103,13 @@ var CalendarNamespace = {
 
         // Draw blanks after end of month (optional, but makes for valid code)
         while (tableRow.childNodes.length < 7) {
-            var _cell = quickElement('td', tableRow, ' ');
+            _cell = quickElement('td', tableRow, ' ');
             _cell.className = "nonday";
         }
 
         calDiv.appendChild(calTable);
     }
-}
+};
 
 // Calendar -- A calendar instance
 function Calendar(div_id, callback, selected) {
@@ -120,7 +123,7 @@ function Calendar(div_id, callback, selected) {
     this.today = new Date();
     this.currentMonth = this.today.getMonth() + 1;
     this.currentYear = this.today.getFullYear();
-    if (typeof selected != 'undefined') {
+    if (typeof selected !== 'undefined') {
         this.selected = selected;
     }
 }
@@ -139,7 +142,7 @@ Calendar.prototype = {
         this.drawCurrent();
     },
     drawPreviousMonth: function() {
-        if (this.currentMonth == 1) {
+        if (this.currentMonth === 1) {
             this.currentMonth = 12;
             this.currentYear--;
         }
@@ -149,7 +152,7 @@ Calendar.prototype = {
         this.drawCurrent();
     },
     drawNextMonth: function() {
-        if (this.currentMonth == 12) {
+        if (this.currentMonth === 12) {
             this.currentMonth = 1;
             this.currentYear++;
         }
@@ -166,4 +169,4 @@ Calendar.prototype = {
         this.currentYear++;
         this.drawCurrent();
     }
-}
+};

+ 23 - 22
django/contrib/admin/static/admin/js/collapse.js

@@ -1,24 +1,25 @@
+/*global gettext*/
 (function($) {
-	$(document).ready(function() {
-		// Add anchor tag for Show/Hide link
-		$("fieldset.collapse").each(function(i, elem) {
-			// Don't hide if fields in this fieldset have errors
-			if ($(elem).find("div.errors").length == 0) {
-				$(elem).addClass("collapsed").find("h2").first().append(' (<a id="fieldsetcollapser' +
-					i +'" class="collapse-toggle" href="#">' + gettext("Show") +
-					'</a>)');
-			}
-		});
-		// Add toggle to anchor tag
-		$("fieldset.collapse a.collapse-toggle").click(function(ev) {
-			if ($(this).closest("fieldset").hasClass("collapsed")) {
-				// Show
-				$(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset", [$(this).attr("id")]);
-			} else {
-				// Hide
-				$(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset", [$(this).attr("id")]);
-			}
-			return false;
-		});
-	});
+    $(document).ready(function() {
+        // Add anchor tag for Show/Hide link
+        $("fieldset.collapse").each(function(i, elem) {
+            // Don't hide if fields in this fieldset have errors
+            if ($(elem).find("div.errors").length === 0) {
+                $(elem).addClass("collapsed").find("h2").first().append(' (<a id="fieldsetcollapser' +
+                    i +'" class="collapse-toggle" href="#">' + gettext("Show") +
+                    '</a>)');
+            }
+        });
+        // Add toggle to anchor tag
+        $("fieldset.collapse a.collapse-toggle").click(function(ev) {
+            if ($(this).closest("fieldset").hasClass("collapsed")) {
+                // Show
+                $(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset", [$(this).attr("id")]);
+            } else {
+                // Hide
+                $(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset", [$(this).attr("id")]);
+            }
+            return false;
+        });
+    });
 })(django.jQuery);

+ 2 - 2
django/contrib/admin/static/admin/js/collapse.min.js

@@ -1,2 +1,2 @@
-(function(a){a(document).ready(function(){a("fieldset.collapse").each(function(c,b){a(b).find("div.errors").length==0&&a(b).addClass("collapsed").find("h2").first().append(' (<a id="fieldsetcollapser'+c+'" class="collapse-toggle" href="#">'+gettext("Show")+"</a>)")});a("fieldset.collapse a.collapse-toggle").click(function(){a(this).closest("fieldset").hasClass("collapsed")?a(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset",[a(this).attr("id")]):a(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset",
-[a(this).attr("id")]);return false})})})(django.jQuery);
+(function(a){a(document).ready(function(){a("fieldset.collapse").each(function(b,c){0===a(c).find("div.errors").length&&a(c).addClass("collapsed").find("h2").first().append(' (<a id="fieldsetcollapser'+b+'" class="collapse-toggle" href="#">'+gettext("Show")+"</a>)")});a("fieldset.collapse a.collapse-toggle").click(function(b){a(this).closest("fieldset").hasClass("collapsed")?a(this).text(gettext("Hide")).closest("fieldset").removeClass("collapsed").trigger("show.fieldset",[a(this).attr("id")]):a(this).text(gettext("Show")).closest("fieldset").addClass("collapsed").trigger("hide.fieldset",
+[a(this).attr("id")]);return!1})})})(django.jQuery);

+ 29 - 28
django/contrib/admin/static/admin/js/core.js

@@ -74,8 +74,8 @@ var xmlhttp;
 @else
     xmlhttp = false;
 @end @*/
-if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
-  xmlhttp = new XMLHttpRequest();
+if (!xmlhttp && typeof XMLHttpRequest !== 'undefined') {
+    xmlhttp = new XMLHttpRequest();
 }
 
 // ----------------------------------------------------------------------------
@@ -90,7 +90,7 @@ function findPosX(obj) {
             obj = obj.offsetParent;
         }
         // IE offsetParent does not include the top-level
-        if (isIE && obj.parentElement){
+        if (isIE && obj.parentElement) {
             curleft += obj.offsetLeft - obj.scrollLeft;
         }
     } else if (obj.x) {
@@ -107,7 +107,7 @@ function findPosY(obj) {
             obj = obj.offsetParent;
         }
         // IE offsetParent does not include the top-level
-        if (isIE && obj.parentElement){
+        if (isIE && obj.parentElement) {
             curtop += obj.offsetTop - obj.scrollTop;
         }
     } else if (obj.y) {
@@ -121,46 +121,46 @@ function findPosY(obj) {
 // ----------------------------------------------------------------------------
 
 Date.prototype.getTwelveHours = function() {
-    hours = this.getHours();
-    if (hours == 0) {
+    var hours = this.getHours();
+    if (hours === 0) {
         return 12;
     }
     else {
-        return hours <= 12 ? hours : hours-12
+        return hours <= 12 ? hours : hours-12;
     }
-}
+};
 
 Date.prototype.getTwoDigitMonth = function() {
     return (this.getMonth() < 9) ? '0' + (this.getMonth()+1) : (this.getMonth()+1);
-}
+};
 
 Date.prototype.getTwoDigitDate = function() {
     return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate();
-}
+};
 
 Date.prototype.getTwoDigitTwelveHour = function() {
     return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours();
-}
+};
 
 Date.prototype.getTwoDigitHour = function() {
     return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours();
-}
+};
 
 Date.prototype.getTwoDigitMinute = function() {
     return (this.getMinutes() < 10) ? '0' + this.getMinutes() : this.getMinutes();
-}
+};
 
 Date.prototype.getTwoDigitSecond = function() {
     return (this.getSeconds() < 10) ? '0' + this.getSeconds() : this.getSeconds();
-}
+};
 
 Date.prototype.getHourMinute = function() {
     return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute();
-}
+};
 
 Date.prototype.getHourMinuteSecond = function() {
     return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond();
-}
+};
 
 Date.prototype.strftime = function(format) {
     var fields = {
@@ -191,7 +191,7 @@ Date.prototype.strftime = function(format) {
         ++i;
     }
     return result;
-}
+};
 
 // ----------------------------------------------------------------------------
 // String object extensions
@@ -202,42 +202,43 @@ String.prototype.pad_left = function(pad_length, pad_string) {
         new_string = pad_string + new_string;
     }
     return new_string;
-}
+};
 
 String.prototype.strptime = function(format) {
     var split_format = format.split(/[.\-/]/);
     var date = this.split(/[.\-/]/);
     var i = 0;
+    var day, month, year;
     while (i < split_format.length) {
         switch (split_format[i]) {
             case "%d":
-                var day = date[i];
+                day = date[i];
                 break;
             case "%m":
-                var month = date[i] - 1;
+                month = date[i] - 1;
                 break;
             case "%Y":
-                var year = date[i];
+                year = date[i];
                 break;
             case "%y":
-                var year = date[i];
+                year = date[i];
                 break;
         }
         ++i;
-    };
+    }
     return new Date(year, month, day);
-}
+};
 
 // ----------------------------------------------------------------------------
 // Get the computed style for and element
 // ----------------------------------------------------------------------------
-function getStyle(oElm, strCssRule){
+function getStyle(oElm, strCssRule) {
     var strValue = "";
-    if(document.defaultView && document.defaultView.getComputedStyle){
+    if(document.defaultView && document.defaultView.getComputedStyle) {
         strValue = document.defaultView.getComputedStyle(oElm, "").getPropertyValue(strCssRule);
     }
-    else if(oElm.currentStyle){
-        strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1){
+    else if(oElm.currentStyle) {
+        strCssRule = strCssRule.replace(/\-(\w)/g, function (strMatch, p1) {
             return p1.toUpperCase();
         });
         strValue = oElm.currentStyle[strCssRule];

+ 237 - 234
django/contrib/admin/static/admin/js/inlines.js

@@ -1,3 +1,4 @@
+/*global DateTimeShortcuts, SelectFilter*/
 /**
  * Django admin inlines
  *
@@ -15,258 +16,260 @@
  * See: http://www.opensource.org/licenses/bsd-license.php
  */
 (function($) {
-  $.fn.formset = function(opts) {
-    var options = $.extend({}, $.fn.formset.defaults, opts);
-    var $this = $(this);
-    var $parent = $this.parent();
-    var updateElementIndex = function(el, prefix, ndx) {
-      var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
-      var replacement = prefix + "-" + ndx;
-      if ($(el).prop("for")) {
-        $(el).prop("for", $(el).prop("for").replace(id_regex, replacement));
-      }
-      if (el.id) {
-        el.id = el.id.replace(id_regex, replacement);
-      }
-      if (el.name) {
-        el.name = el.name.replace(id_regex, replacement);
-      }
-    };
-    var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").prop("autocomplete", "off");
-    var nextIndex = parseInt(totalForms.val(), 10);
-    var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").prop("autocomplete", "off");
-    // only show the add button if we are allowed to add more items,
+    $.fn.formset = function(opts) {
+        var options = $.extend({}, $.fn.formset.defaults, opts);
+        var $this = $(this);
+        var $parent = $this.parent();
+        var updateElementIndex = function(el, prefix, ndx) {
+            var id_regex = new RegExp("(" + prefix + "-(\\d+|__prefix__))");
+            var replacement = prefix + "-" + ndx;
+            if ($(el).prop("for")) {
+                $(el).prop("for", $(el).prop("for").replace(id_regex, replacement));
+            }
+            if (el.id) {
+                el.id = el.id.replace(id_regex, replacement);
+            }
+            if (el.name) {
+                el.name = el.name.replace(id_regex, replacement);
+            }
+        };
+        var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS").prop("autocomplete", "off");
+        var nextIndex = parseInt(totalForms.val(), 10);
+        var maxForms = $("#id_" + options.prefix + "-MAX_NUM_FORMS").prop("autocomplete", "off");
+        // only show the add button if we are allowed to add more items,
         // note that max_num = None translates to a blank string.
-    var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0;
-    $this.each(function(i) {
-      $(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
-    });
-    if ($this.length && showAddButton) {
-      var addButton;
-      if ($this.prop("tagName") == "TR") {
-        // If forms are laid out as table rows, insert the
-        // "add" button in a new table row:
-        var numCols = this.eq(-1).children().length;
-        $parent.append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
-        addButton = $parent.find("tr:last a");
-      } else {
-        // Otherwise, insert it immediately after the last form:
-        $this.filter(":last").after('<div class="' + options.addCssClass + '"><a href="javascript:void(0)">' + options.addText + "</a></div>");
-        addButton = $this.filter(":last").next().find("a");
-      }
-      addButton.click(function(e) {
-        e.preventDefault();
-        var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
-        var template = $("#" + options.prefix + "-empty");
-        var row = template.clone(true);
-        row.removeClass(options.emptyCssClass)
-          .addClass(options.formCssClass)
-          .attr("id", options.prefix + "-" + nextIndex);
-        if (row.is("tr")) {
-          // If the forms are laid out in table rows, insert
-          // the remove button into the last table cell:
-          row.children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
-        } else if (row.is("ul") || row.is("ol")) {
-          // If they're laid out as an ordered/unordered list,
-          // insert an <li> after the last list item:
-          row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
-        } else {
-          // Otherwise, just insert the remove button as the
-          // last child element of the form's container:
-          row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
-        }
-        row.find("*").each(function() {
-          updateElementIndex(this, options.prefix, totalForms.val());
+        var showAddButton = maxForms.val() === '' || (maxForms.val()-totalForms.val()) > 0;
+        $this.each(function(i) {
+            $(this).not("." + options.emptyCssClass).addClass(options.formCssClass);
         });
-        // Insert the new form when it has been fully edited
-        row.insertBefore($(template));
-        // Update number of total forms
-        $(totalForms).val(parseInt(totalForms.val(), 10) + 1);
-        nextIndex += 1;
-        // Hide add button in case we've hit the max, except we want to add infinitely
-        if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
-          addButton.parent().hide();
-        }
-        // The delete button of each row triggers a bunch of other things
-        row.find("a." + options.deleteCssClass).click(function(e) {
-          e.preventDefault();
-          // Remove the parent form containing this button:
-          var row = $(this).parents("." + options.formCssClass);
-          row.remove();
-          nextIndex -= 1;
-          // If a post-delete callback was provided, call it with the deleted form:
-          if (options.removed) {
-            options.removed(row);
-          }
-          // Update the TOTAL_FORMS form count.
-          var forms = $("." + options.formCssClass);
-          $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
-          // Show add button again once we drop below max
-          if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
-            addButton.parent().show();
-          }
-          // Also, update names and ids for all remaining form controls
-          // so they remain in sequence:
-          for (var i=0, formCount=forms.length; i<formCount; i++)
-          {
-            updateElementIndex($(forms).get(i), options.prefix, i);
-            $(forms.get(i)).find("*").each(function() {
-              updateElementIndex(this, options.prefix, i);
+        if ($this.length && showAddButton) {
+            var addButton;
+            if ($this.prop("tagName") === "TR") {
+                // If forms are laid out as table rows, insert the
+                // "add" button in a new table row:
+                var numCols = this.eq(-1).children().length;
+                $parent.append('<tr class="' + options.addCssClass + '"><td colspan="' + numCols + '"><a href="javascript:void(0)">' + options.addText + "</a></tr>");
+                addButton = $parent.find("tr:last a");
+            } else {
+                // Otherwise, insert it immediately after the last form:
+                $this.filter(":last").after('<div class="' + options.addCssClass + '"><a href="javascript:void(0)">' + options.addText + "</a></div>");
+                addButton = $this.filter(":last").next().find("a");
+            }
+            addButton.click(function(e) {
+                e.preventDefault();
+                var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
+                var template = $("#" + options.prefix + "-empty");
+                var row = template.clone(true);
+                row.removeClass(options.emptyCssClass)
+                .addClass(options.formCssClass)
+                .attr("id", options.prefix + "-" + nextIndex);
+                if (row.is("tr")) {
+                    // If the forms are laid out in table rows, insert
+                    // the remove button into the last table cell:
+                    row.children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
+                } else if (row.is("ul") || row.is("ol")) {
+                    // If they're laid out as an ordered/unordered list,
+                    // insert an <li> after the last list item:
+                    row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
+                } else {
+                    // Otherwise, just insert the remove button as the
+                    // last child element of the form's container:
+                    row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
+                }
+                row.find("*").each(function() {
+                    updateElementIndex(this, options.prefix, totalForms.val());
+                });
+                // Insert the new form when it has been fully edited
+                row.insertBefore($(template));
+                // Update number of total forms
+                $(totalForms).val(parseInt(totalForms.val(), 10) + 1);
+                nextIndex += 1;
+                // Hide add button in case we've hit the max, except we want to add infinitely
+                if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
+                    addButton.parent().hide();
+                }
+                // The delete button of each row triggers a bunch of other things
+                row.find("a." + options.deleteCssClass).click(function(e) {
+                    e.preventDefault();
+                    // Remove the parent form containing this button:
+                    var row = $(this).parents("." + options.formCssClass);
+                    row.remove();
+                    nextIndex -= 1;
+                    // If a post-delete callback was provided, call it with the deleted form:
+                    if (options.removed) {
+                        options.removed(row);
+                    }
+                    // Update the TOTAL_FORMS form count.
+                    var forms = $("." + options.formCssClass);
+                    $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);
+                    // Show add button again once we drop below max
+                    if ((maxForms.val() === '') || (maxForms.val()-forms.length) > 0) {
+                        addButton.parent().show();
+                    }
+                    // Also, update names and ids for all remaining form controls
+                    // so they remain in sequence:
+                    var i, formCount;
+                    var updateElementCallback = function() {
+                        updateElementIndex(this, options.prefix, i);
+                    };
+                    for (i=0, formCount=forms.length; i<formCount; i++)
+                    {
+                        updateElementIndex($(forms).get(i), options.prefix, i);
+                        $(forms.get(i)).find("*").each(updateElementCallback);
+                    }
+                });
+                // If a post-add callback was supplied, call it with the added form:
+                if (options.added) {
+                    options.added(row);
+                }
             });
-          }
-        });
-        // If a post-add callback was supplied, call it with the added form:
-        if (options.added) {
-          options.added(row);
         }
-      });
-    }
-    return this;
-  };
+        return this;
+    };
 
-  /* Setup plugin defaults */
-  $.fn.formset.defaults = {
-    prefix: "form",          // The form prefix for your django formset
-    addText: "add another",      // Text for the add link
-    deleteText: "remove",      // Text for the delete link
-    addCssClass: "add-row",      // CSS class applied to the add link
-    deleteCssClass: "delete-row",  // CSS class applied to the delete link
-    emptyCssClass: "empty-row",    // CSS class applied to the empty row
-    formCssClass: "dynamic-form",  // CSS class applied to each form in a formset
-    added: null,          // Function called each time a new form is added
-    removed: null          // Function called each time a form is deleted
-  };
+    /* Setup plugin defaults */
+    $.fn.formset.defaults = {
+        prefix: "form",          // The form prefix for your django formset
+        addText: "add another",      // Text for the add link
+        deleteText: "remove",      // Text for the delete link
+        addCssClass: "add-row",      // CSS class applied to the add link
+        deleteCssClass: "delete-row",  // CSS class applied to the delete link
+        emptyCssClass: "empty-row",    // CSS class applied to the empty row
+        formCssClass: "dynamic-form",  // CSS class applied to each form in a formset
+        added: null,          // Function called each time a new form is added
+        removed: null          // Function called each time a form is deleted
+    };
 
 
-  // Tabular inlines ---------------------------------------------------------
-  $.fn.tabularFormset = function(options) {
-    var $rows = $(this);
-    var alternatingRows = function(row) {
-      $($rows.selector).not(".add-row").removeClass("row1 row2")
-        .filter(":even").addClass("row1").end()
-        .filter(":odd").addClass("row2");
-    };
+    // Tabular inlines ---------------------------------------------------------
+    $.fn.tabularFormset = function(options) {
+        var $rows = $(this);
+        var alternatingRows = function(row) {
+            $($rows.selector).not(".add-row").removeClass("row1 row2")
+            .filter(":even").addClass("row1").end()
+            .filter(":odd").addClass("row2");
+        };
 
-    var reinitDateTimeShortCuts = function() {
-      // Reinitialize the calendar and clock widgets by force
-      if (typeof DateTimeShortcuts != "undefined") {
-        $(".datetimeshortcuts").remove();
-        DateTimeShortcuts.init();
-      }
-    };
+        var reinitDateTimeShortCuts = function() {
+            // Reinitialize the calendar and clock widgets by force
+            if (typeof DateTimeShortcuts !== "undefined") {
+                $(".datetimeshortcuts").remove();
+                DateTimeShortcuts.init();
+            }
+        };
 
-    var updateSelectFilter = function() {
-      // If any SelectFilter widgets are a part of the new form,
-      // instantiate a new SelectFilter instance for it.
-      if (typeof SelectFilter != 'undefined'){
-        $('.selectfilter').each(function(index, value){
-          var namearr = value.name.split('-');
-          SelectFilter.init(value.id, namearr[namearr.length-1], false);
-        });
-        $('.selectfilterstacked').each(function(index, value){
-          var namearr = value.name.split('-');
-          SelectFilter.init(value.id, namearr[namearr.length-1], true);
-        });
-      }
-    };
+        var updateSelectFilter = function() {
+            // If any SelectFilter widgets are a part of the new form,
+            // instantiate a new SelectFilter instance for it.
+            if (typeof SelectFilter !== 'undefined') {
+                $('.selectfilter').each(function(index, value) {
+                    var namearr = value.name.split('-');
+                    SelectFilter.init(value.id, namearr[namearr.length-1], false);
+                });
+                $('.selectfilterstacked').each(function(index, value) {
+                    var namearr = value.name.split('-');
+                    SelectFilter.init(value.id, namearr[namearr.length-1], true);
+                });
+            }
+        };
+
+        var initPrepopulatedFields = function(row) {
+            row.find('.prepopulated_field').each(function() {
+                var field = $(this),
+                    input = field.find('input, select, textarea'),
+                    dependency_list = input.data('dependency_list') || [],
+                    dependencies = [];
+                $.each(dependency_list, function(i, field_name) {
+                    dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id'));
+                });
+                if (dependencies.length) {
+                    input.prepopulate(dependencies, input.attr('maxlength'));
+                }
+            });
+        };
 
-    var initPrepopulatedFields = function(row) {
-      row.find('.prepopulated_field').each(function() {
-        var field = $(this),
-            input = field.find('input, select, textarea'),
-            dependency_list = input.data('dependency_list') || [],
-            dependencies = [];
-        $.each(dependency_list, function(i, field_name) {
-          dependencies.push('#' + row.find('.field-' + field_name).find('input, select, textarea').attr('id'));
+        $rows.formset({
+            prefix: options.prefix,
+            addText: options.addText,
+            formCssClass: "dynamic-" + options.prefix,
+            deleteCssClass: "inline-deletelink",
+            deleteText: options.deleteText,
+            emptyCssClass: "empty-form",
+            removed: alternatingRows,
+            added: function(row) {
+                initPrepopulatedFields(row);
+                reinitDateTimeShortCuts();
+                updateSelectFilter();
+                alternatingRows(row);
+            }
         });
-        if (dependencies.length) {
-          input.prepopulate(dependencies, input.attr('maxlength'));
-        }
-      });
+
+        return $rows;
     };
 
-    $rows.formset({
-      prefix: options.prefix,
-      addText: options.addText,
-      formCssClass: "dynamic-" + options.prefix,
-      deleteCssClass: "inline-deletelink",
-      deleteText: options.deleteText,
-      emptyCssClass: "empty-form",
-      removed: alternatingRows,
-      added: function(row) {
-        initPrepopulatedFields(row);
-        reinitDateTimeShortCuts();
-        updateSelectFilter();
-        alternatingRows(row);
-      }
-    });
+    // Stacked inlines ---------------------------------------------------------
+    $.fn.stackedFormset = function(options) {
+        var $rows = $(this);
+        var updateInlineLabel = function(row) {
+            $($rows.selector).find(".inline_label").each(function(i) {
+                var count = i + 1;
+                $(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
+            });
+        };
 
-    return $rows;
-  };
+        var reinitDateTimeShortCuts = function() {
+            // Reinitialize the calendar and clock widgets by force, yuck.
+            if (typeof DateTimeShortcuts !== "undefined") {
+                $(".datetimeshortcuts").remove();
+                DateTimeShortcuts.init();
+            }
+        };
 
-  // Stacked inlines ---------------------------------------------------------
-  $.fn.stackedFormset = function(options) {
-    var $rows = $(this);
-    var updateInlineLabel = function(row) {
-      $($rows.selector).find(".inline_label").each(function(i) {
-        var count = i + 1;
-        $(this).html($(this).html().replace(/(#\d+)/g, "#" + count));
-      });
-    };
+        var updateSelectFilter = function() {
+            // If any SelectFilter widgets were added, instantiate a new instance.
+            if (typeof SelectFilter !== "undefined") {
+                $(".selectfilter").each(function(index, value) {
+                    var namearr = value.name.split('-');
+                    SelectFilter.init(value.id, namearr[namearr.length-1], false);
+                });
+                $(".selectfilterstacked").each(function(index, value) {
+                    var namearr = value.name.split('-');
+                    SelectFilter.init(value.id, namearr[namearr.length-1], true);
+                });
+            }
+        };
 
-    var reinitDateTimeShortCuts = function() {
-      // Reinitialize the calendar and clock widgets by force, yuck.
-      if (typeof DateTimeShortcuts != "undefined") {
-        $(".datetimeshortcuts").remove();
-        DateTimeShortcuts.init();
-      }
-    };
+        var initPrepopulatedFields = function(row) {
+            row.find('.prepopulated_field').each(function() {
+                var field = $(this),
+                    input = field.find('input, select, textarea'),
+                    dependency_list = input.data('dependency_list') || [],
+                    dependencies = [];
+                $.each(dependency_list, function(i, field_name) {
+                    dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id'));
+                });
+                if (dependencies.length) {
+                    input.prepopulate(dependencies, input.attr('maxlength'));
+                }
+            });
+        };
 
-    var updateSelectFilter = function() {
-      // If any SelectFilter widgets were added, instantiate a new instance.
-      if (typeof SelectFilter != "undefined"){
-        $(".selectfilter").each(function(index, value){
-          var namearr = value.name.split('-');
-          SelectFilter.init(value.id, namearr[namearr.length-1], false);
+        $rows.formset({
+            prefix: options.prefix,
+            addText: options.addText,
+            formCssClass: "dynamic-" + options.prefix,
+            deleteCssClass: "inline-deletelink",
+            deleteText: options.deleteText,
+            emptyCssClass: "empty-form",
+            removed: updateInlineLabel,
+            added: function(row) {
+                initPrepopulatedFields(row);
+                reinitDateTimeShortCuts();
+                updateSelectFilter();
+                updateInlineLabel(row);
+            }
         });
-        $(".selectfilterstacked").each(function(index, value){
-          var namearr = value.name.split('-');
-          SelectFilter.init(value.id, namearr[namearr.length-1], true);
-        });
-      }
-    };
 
-    var initPrepopulatedFields = function(row) {
-      row.find('.prepopulated_field').each(function() {
-        var field = $(this),
-            input = field.find('input, select, textarea'),
-            dependency_list = input.data('dependency_list') || [],
-            dependencies = [];
-        $.each(dependency_list, function(i, field_name) {
-          dependencies.push('#' + row.find('.form-row .field-' + field_name).find('input, select, textarea').attr('id'));
-        });
-        if (dependencies.length) {
-          input.prepopulate(dependencies, input.attr('maxlength'));
-        }
-      });
+        return $rows;
     };
-
-    $rows.formset({
-      prefix: options.prefix,
-      addText: options.addText,
-      formCssClass: "dynamic-" + options.prefix,
-      deleteCssClass: "inline-deletelink",
-      deleteText: options.deleteText,
-      emptyCssClass: "empty-form",
-      removed: updateInlineLabel,
-      added: (function(row) {
-        initPrepopulatedFields(row);
-        reinitDateTimeShortCuts();
-        updateSelectFilter();
-        updateInlineLabel(row);
-      })
-    });
-
-    return $rows;
-  };
 })(django.jQuery);

+ 9 - 9
django/contrib/admin/static/admin/js/inlines.min.js

@@ -1,9 +1,9 @@
-(function(c){c.fn.formset=function(b){var a=c.extend({},c.fn.formset.defaults,b),d=c(this);b=d.parent();var k=function(a,f,l){var h=new RegExp("("+f+"-(\\d+|__prefix__))");f=f+"-"+l;c(a).prop("for")&&c(a).prop("for",c(a).prop("for").replace(h,f));a.id&&(a.id=a.id.replace(h,f));a.name&&(a.name=a.name.replace(h,f))},e=c("#id_"+a.prefix+"-TOTAL_FORMS").prop("autocomplete","off"),l=parseInt(e.val(),10),f=c("#id_"+a.prefix+"-MAX_NUM_FORMS").prop("autocomplete","off"),e=""===f.val()||0<f.val()-e.val();
-d.each(function(f){c(this).not("."+a.emptyCssClass).addClass(a.formCssClass)});if(d.length&&e){var h;"TR"==d.prop("tagName")?(d=this.eq(-1).children().length,b.append('<tr class="'+a.addCssClass+'"><td colspan="'+d+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>"),h=b.find("tr:last a")):(d.filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>"),h=d.filter(":last").next().find("a"));h.click(function(b){b.preventDefault();var d=c("#id_"+a.prefix+
-"-TOTAL_FORMS");b=c("#"+a.prefix+"-empty");var g=b.clone(!0);g.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+l);g.is("tr")?g.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>"):g.is("ul")||g.is("ol")?g.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):g.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+
-"</a></span>");g.find("*").each(function(){k(this,a.prefix,d.val())});g.insertBefore(c(b));c(d).val(parseInt(d.val(),10)+1);l+=1;""!==f.val()&&0>=f.val()-d.val()&&h.parent().hide();g.find("a."+a.deleteCssClass).click(function(b){b.preventDefault();b=c(this).parents("."+a.formCssClass);b.remove();--l;a.removed&&a.removed(b);b=c("."+a.formCssClass);c("#id_"+a.prefix+"-TOTAL_FORMS").val(b.length);(""===f.val()||0<f.val()-b.length)&&h.parent().show();for(var d=0,g=b.length;d<g;d++)k(c(b).get(d),a.prefix,
-d),c(b.get(d)).find("*").each(function(){k(this,a.prefix,d)})});a.added&&a.added(g)})}return this};c.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null};c.fn.tabularFormset=function(b){var a=c(this),d=function(b){c(a.selector).not(".add-row").removeClass("row1 row2").filter(":even").addClass("row1").end().filter(":odd").addClass("row2")},k=function(){"undefined"!=
-typeof SelectFilter&&(c(".selectfilter").each(function(a,c){var b=c.name.split("-");SelectFilter.init(c.id,b[b.length-1],!1)}),c(".selectfilterstacked").each(function(a,c){var b=c.name.split("-");SelectFilter.init(c.id,b[b.length-1],!0)}))},e=function(a){a.find(".prepopulated_field").each(function(){var b=c(this).find("input, select, textarea"),d=b.data("dependency_list")||[],e=[];c.each(d,function(c,b){e.push("#"+a.find(".field-"+b).find("input, select, textarea").attr("id"))});e.length&&b.prepopulate(e,
-b.attr("maxlength"))})};a.formset({prefix:b.prefix,addText:b.addText,formCssClass:"dynamic-"+b.prefix,deleteCssClass:"inline-deletelink",deleteText:b.deleteText,emptyCssClass:"empty-form",removed:d,added:function(a){e(a);"undefined"!=typeof DateTimeShortcuts&&(c(".datetimeshortcuts").remove(),DateTimeShortcuts.init());k();d(a)}});return a};c.fn.stackedFormset=function(b){var a=c(this),d=function(b){c(a.selector).find(".inline_label").each(function(a){a+=1;c(this).html(c(this).html().replace(/(#\d+)/g,
-"#"+a))})},k=function(){"undefined"!=typeof SelectFilter&&(c(".selectfilter").each(function(a,c){var b=c.name.split("-");SelectFilter.init(c.id,b[b.length-1],!1)}),c(".selectfilterstacked").each(function(a,c){var b=c.name.split("-");SelectFilter.init(c.id,b[b.length-1],!0)}))},e=function(a){a.find(".prepopulated_field").each(function(){var b=c(this).find("input, select, textarea"),d=b.data("dependency_list")||[],e=[];c.each(d,function(b,c){e.push("#"+a.find(".form-row .field-"+c).find("input, select, textarea").attr("id"))});
-e.length&&b.prepopulate(e,b.attr("maxlength"))})};a.formset({prefix:b.prefix,addText:b.addText,formCssClass:"dynamic-"+b.prefix,deleteCssClass:"inline-deletelink",deleteText:b.deleteText,emptyCssClass:"empty-form",removed:d,added:function(a){e(a);"undefined"!=typeof DateTimeShortcuts&&(c(".datetimeshortcuts").remove(),DateTimeShortcuts.init());k();d(a)}});return a}})(django.jQuery);
+(function(c){c.fn.formset=function(b){var a=c.extend({},c.fn.formset.defaults,b),d=c(this);b=d.parent();var k=function(a,g,l){var h=new RegExp("("+g+"-(\\d+|__prefix__))");g=g+"-"+l;c(a).prop("for")&&c(a).prop("for",c(a).prop("for").replace(h,g));a.id&&(a.id=a.id.replace(h,g));a.name&&(a.name=a.name.replace(h,g))},f=c("#id_"+a.prefix+"-TOTAL_FORMS").prop("autocomplete","off"),l=parseInt(f.val(),10),g=c("#id_"+a.prefix+"-MAX_NUM_FORMS").prop("autocomplete","off"),f=""===g.val()||0<g.val()-f.val();
+d.each(function(g){c(this).not("."+a.emptyCssClass).addClass(a.formCssClass)});if(d.length&&f){var h;"TR"==d.prop("tagName")?(d=this.eq(-1).children().length,b.append('<tr class="'+a.addCssClass+'"><td colspan="'+d+'"><a href="javascript:void(0)">'+a.addText+"</a></tr>"),h=b.find("tr:last a")):(d.filter(":last").after('<div class="'+a.addCssClass+'"><a href="javascript:void(0)">'+a.addText+"</a></div>"),h=d.filter(":last").next().find("a"));h.click(function(b){b.preventDefault();var d=c("#id_"+a.prefix+
+"-TOTAL_FORMS");b=c("#"+a.prefix+"-empty");var e=b.clone(!0);e.removeClass(a.emptyCssClass).addClass(a.formCssClass).attr("id",a.prefix+"-"+l);e.is("tr")?e.children(":last").append('<div><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></div>"):e.is("ul")||e.is("ol")?e.append('<li><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+"</a></li>"):e.children(":first").append('<span><a class="'+a.deleteCssClass+'" href="javascript:void(0)">'+a.deleteText+
+"</a></span>");e.find("*").each(function(){k(this,a.prefix,d.val())});e.insertBefore(c(b));c(d).val(parseInt(d.val(),10)+1);l+=1;""!==g.val()&&0>=g.val()-d.val()&&h.parent().hide();e.find("a."+a.deleteCssClass).click(function(b){b.preventDefault();b=c(this).parents("."+a.formCssClass);b.remove();--l;a.removed&&a.removed(b);b=c("."+a.formCssClass);c("#id_"+a.prefix+"-TOTAL_FORMS").val(b.length);(""===g.val()||0<g.val()-b.length)&&h.parent().show();for(var d=function(){k(this,a.prefix,e)},e=0,f=b.length;e<
+f;e++)k(c(b).get(e),a.prefix,e),c(b.get(e)).find("*").each(d)});a.added&&a.added(e)})}return this};c.fn.formset.defaults={prefix:"form",addText:"add another",deleteText:"remove",addCssClass:"add-row",deleteCssClass:"delete-row",emptyCssClass:"empty-row",formCssClass:"dynamic-form",added:null,removed:null};c.fn.tabularFormset=function(b){var a=c(this),d=function(b){c(a.selector).not(".add-row").removeClass("row1 row2").filter(":even").addClass("row1").end().filter(":odd").addClass("row2")},k=function(){"undefined"!=
+typeof SelectFilter&&(c(".selectfilter").each(function(a,c){var b=c.name.split("-");SelectFilter.init(c.id,b[b.length-1],!1)}),c(".selectfilterstacked").each(function(a,c){var b=c.name.split("-");SelectFilter.init(c.id,b[b.length-1],!0)}))},f=function(a){a.find(".prepopulated_field").each(function(){var b=c(this).find("input, select, textarea"),d=b.data("dependency_list")||[],f=[];c.each(d,function(c,b){f.push("#"+a.find(".field-"+b).find("input, select, textarea").attr("id"))});f.length&&b.prepopulate(f,
+b.attr("maxlength"))})};a.formset({prefix:b.prefix,addText:b.addText,formCssClass:"dynamic-"+b.prefix,deleteCssClass:"inline-deletelink",deleteText:b.deleteText,emptyCssClass:"empty-form",removed:d,added:function(a){f(a);"undefined"!=typeof DateTimeShortcuts&&(c(".datetimeshortcuts").remove(),DateTimeShortcuts.init());k();d(a)}});return a};c.fn.stackedFormset=function(b){var a=c(this),d=function(b){c(a.selector).find(".inline_label").each(function(a){a+=1;c(this).html(c(this).html().replace(/(#\d+)/g,
+"#"+a))})},k=function(){"undefined"!=typeof SelectFilter&&(c(".selectfilter").each(function(a,c){var b=c.name.split("-");SelectFilter.init(c.id,b[b.length-1],!1)}),c(".selectfilterstacked").each(function(a,c){var b=c.name.split("-");SelectFilter.init(c.id,b[b.length-1],!0)}))},f=function(a){a.find(".prepopulated_field").each(function(){var b=c(this).find("input, select, textarea"),d=b.data("dependency_list")||[],f=[];c.each(d,function(b,c){f.push("#"+a.find(".form-row .field-"+c).find("input, select, textarea").attr("id"))});
+f.length&&b.prepopulate(f,b.attr("maxlength"))})};a.formset({prefix:b.prefix,addText:b.addText,formCssClass:"dynamic-"+b.prefix,deleteCssClass:"inline-deletelink",deleteText:b.deleteText,emptyCssClass:"empty-form",removed:d,added:function(a){f(a);"undefined"!=typeof DateTimeShortcuts&&(c(".datetimeshortcuts").remove(),DateTimeShortcuts.init());k();d(a)}});return a}})(django.jQuery);

+ 1 - 0
django/contrib/admin/static/admin/js/jquery.init.js

@@ -1,3 +1,4 @@
+/*global django:true, jQuery:false*/
 /* Puts the included jQuery into our own namespace using noConflict and passing
  * it 'true'. This ensures that the included jQuery doesn't pollute the global
  * namespace (i.e. this preserves pre-existing values for both window.$ and

+ 1 - 0
django/contrib/admin/static/admin/js/prepopulate.js

@@ -1,3 +1,4 @@
+/*global URLify*/
 (function($) {
     $.fn.prepopulate = function(dependencies, maxLength) {
         /*

+ 1 - 1
django/contrib/admin/static/admin/js/prepopulate.min.js

@@ -1 +1 @@
-(function(b){b.fn.prepopulate=function(e,g){return this.each(function(){var a=b(this),d=function(){if(!a.data("_changed")){var f=[];b.each(e,function(h,c){c=b(c);c.val().length>0&&f.push(c.val())});a.val(URLify(f.join(" "),g))}};a.data("_changed",false);a.change(function(){a.data("_changed",true)});a.val()||b(e.join(",")).keyup(d).change(d).focus(d)})}})(django.jQuery);
+(function(c){c.fn.prepopulate=function(e,f){return this.each(function(){var a=c(this),b=function(){if(!a.data("_changed")){var b=[];c.each(e,function(a,d){d=c(d);0<d.val().length&&b.push(d.val())});a.val(URLify(b.join(" "),f))}};a.data("_changed",!1);a.change(function(){a.data("_changed",!0)});a.val()||c(e.join(",")).keyup(b).change(b).focus(b)})}})(django.jQuery);

+ 7 - 7
django/contrib/admin/static/admin/js/timeparse.js

@@ -2,7 +2,7 @@ var timeParsePatterns = [
     // 9
     {   re: /^\d{1,2}$/i,
         handler: function(bits) {
-            if (bits[0].length == 1) {
+            if (bits[0].length === 1) {
                 return '0' + bits[0] + ':00';
             } else {
                 return bits[0] + ':00';
@@ -25,11 +25,11 @@ var timeParsePatterns = [
     {   re: /^(\d+)\s*([ap])(?:.?m.?)?$/i,
         handler: function(bits) {
             var hour = parseInt(bits[1]);
-            if (hour == 12) {
+            if (hour === 12) {
                 hour = 0;
             }
-            if (bits[2].toLowerCase() == 'p') {
-                if (hour == 12) {
+            if (bits[2].toLowerCase() === 'p') {
+                if (hour === 12) {
                     hour = 0;
                 }
                 return (hour + 12) + ':00';
@@ -50,11 +50,11 @@ var timeParsePatterns = [
             if (mins < 10) {
                 mins = '0' + mins;
             }
-            if (hour == 12) {
+            if (hour === 12) {
                 hour = 0;
             }
-            if (bits[3].toLowerCase() == 'p') {
-                if (hour == 12) {
+            if (bits[3].toLowerCase() === 'p') {
+                if (hour === 12) {
                     hour = 0;
                 }
                 return (hour + 12) + ':' + mins;

+ 22 - 20
django/contrib/gis/static/gis/js/OLMapWidget.js

@@ -1,10 +1,12 @@
+/*global OpenLayers*/
+/*eslint indent:1*/
 (function() {
-/**
- * Transforms an array of features to a single feature with the merged
- * geometry of geom_type
- */
+ /**
+  * Transforms an array of features to a single feature with the merged
+  * geometry of geom_type
+  */
 OpenLayers.Util.properFeatures = function(features, geom_type) {
-    if (features.constructor == Array) {
+        if (features.constructor === Array) {
         var geoms = [];
         for (var i=0; i<features.length; i++) {
             geoms.push(features[i].geometry);
@@ -13,7 +15,7 @@ OpenLayers.Util.properFeatures = function(features, geom_type) {
         features = new OpenLayers.Feature.Vector(geom);
     }
     return features;
-}
+};
 
 /**
  * @requires OpenLayers/Format/WKT.js
@@ -128,7 +130,7 @@ OpenLayers.Format.DjangoWKT = OpenLayers.Class(OpenLayers.Format.WKT, {
             geometry = geometry.clone();
             geometry.transform(this.internalProjection, this.externalProjection);
         }
-        var wktType = type == 'collection' ? 'GEOMETRYCOLLECTION' : type.toUpperCase();
+        var wktType = type === 'collection' ? 'GEOMETRYCOLLECTION' : type.toUpperCase();
         var data = wktType + '(' + this.extract[type].apply(this, [geometry]) + ')';
         return data;
     },
@@ -138,8 +140,8 @@ OpenLayers.Format.DjangoWKT = OpenLayers.Class(OpenLayers.Format.WKT, {
      * geometrycollections.
      */
     write: function(features) {
-        var collection, geometry, type, data, isCollection;
-        isCollection = features.geometry.CLASS_NAME == "OpenLayers.Geometry.Collection";
+        var collection, isCollection;
+        isCollection = features.geometry.CLASS_NAME === "OpenLayers.Geometry.Collection";
         var pieces = [];
         if (isCollection) {
             collection = features.geometry.components;
@@ -168,8 +170,8 @@ function MapWidget(options) {
     this.wkt_f = new OpenLayers.Format.DjangoWKT();
 
     // Mapping from OGRGeomType name to OpenLayers.Geometry name
-    if (options['geom_name'] == 'Unknown') options['geom_type'] = OpenLayers.Geometry;
-    else if (options['geom_name'] == 'GeometryCollection') options['geom_type'] = OpenLayers.Geometry.Collection;
+    if (options['geom_name'] === 'Unknown') options['geom_type'] = OpenLayers.Geometry;
+    else if (options['geom_name'] === 'GeometryCollection') options['geom_type'] = OpenLayers.Geometry.Collection;
     else options['geom_type'] = eval('OpenLayers.Geometry.' + options['geom_name']);
 
     // Default options
@@ -204,7 +206,7 @@ function MapWidget(options) {
         'fillOpacity': this.options.opacity,
         'strokeColor': '#' + this.options.color
     };
-    if (this.options.geom_name == 'LineString') {
+    if (this.options.geom_name === 'LineString') {
         defaults_style['strokeWidth'] = 3;
     }
     var styleMap = new OpenLayers.StyleMap({'default': OpenLayers.Util.applyDefaults(defaults_style, OpenLayers.Feature.Vector.style['default'])});
@@ -222,7 +224,7 @@ function MapWidget(options) {
             this.layers.vector.addFeatures([feat]);
         }
         this.map.zoomToExtent(feat.geometry.getBounds());
-        if (this.options.geom_name == 'Point') {
+        if (this.options.geom_name === 'Point') {
             this.map.zoomTo(this.options.point_zoom);
         }
     } else {
@@ -262,7 +264,7 @@ MapWidget.prototype.create_map = function() {
     if (this.options.base_layer) this.layers.base = this.options.base_layer;
     else this.layers.base = new OpenLayers.Layer.WMS('OpenLayers WMS', 'http://vmap0.tiles.osgeo.org/wms/vmap0', {layers: 'basic'});
     map.addLayer(this.layers.base);
-    return map
+    return map;
 };
 
 MapWidget.prototype.get_ewkt = function(feat) {
@@ -270,7 +272,7 @@ MapWidget.prototype.get_ewkt = function(feat) {
 };
 
 MapWidget.prototype.read_wkt = function(wkt) {
-    var prefix = 'SRID=' + this.options.map_srid + ';'
+    var prefix = 'SRID=' + this.options.map_srid + ';';
     if (wkt.indexOf(prefix) === 0) {
         wkt = wkt.slice(prefix.length);
     }
@@ -296,7 +298,7 @@ MapWidget.prototype.add_wkt = function(event) {
         this.write_wkt(feat);
     } else {
         if (this.layers.vector.features.length > 1) {
-            old_feats = [this.layers.vector.features[0]];
+            var old_feats = [this.layers.vector.features[0]];
             this.layers.vector.removeFeatures(old_feats);
             this.layers.vector.destroyFeatures(old_feats);
         }
@@ -306,7 +308,7 @@ MapWidget.prototype.add_wkt = function(event) {
 
 MapWidget.prototype.modify_wkt = function(event) {
     if (this.options.is_collection) {
-        if (this.options.geom_name == 'MultiPoint') {
+        if (this.options.geom_name === 'MultiPoint') {
             this.add_wkt(event);
             return;
         } else {
@@ -359,13 +361,13 @@ MapWidget.prototype.getControls = function(layer) {
     this.controls = [new OpenLayers.Control.Navigation()];
     if (!this.options.modifiable && layer.features.length)
         return;
-    if (this.options.geom_name.indexOf('LineString') >= 0 || this.options.geom_name == 'GeometryCollection' || this.options.geom_name == 'Unknown') {
+    if (this.options.geom_name.indexOf('LineString') >= 0 || this.options.geom_name === 'GeometryCollection' || this.options.geom_name === 'Unknown') {
         this.controls.push(new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {'displayClass': 'olControlDrawFeaturePath'}));
     }
-    if (this.options.geom_name.indexOf('Polygon') >= 0 || this.options.geom_name == 'GeometryCollection' || this.options.geom_name == 'Unknown') {
+    if (this.options.geom_name.indexOf('Polygon') >= 0 || this.options.geom_name === 'GeometryCollection' || this.options.geom_name === 'Unknown') {
         this.controls.push(new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, {'displayClass': 'olControlDrawFeaturePolygon'}));
     }
-    if (this.options.geom_name.indexOf('Point') >= 0 || this.options.geom_name == 'GeometryCollection' || this.options.geom_name == 'Unknown') {
+    if (this.options.geom_name.indexOf('Point') >= 0 || this.options.geom_name === 'GeometryCollection' || this.options.geom_name === 'Unknown') {
         this.controls.push(new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {'displayClass': 'olControlDrawFeaturePoint'}));
     }
     if (this.options.modifiable) {

+ 90 - 90
django/contrib/gis/templates/gis/admin/openlayers.js

@@ -12,98 +12,98 @@ OpenLayers.Projection.addTransform("EPSG:4326", "EPSG:3857", OpenLayers.Layer.Sp
 {{ module }}.is_point = {{ is_point|yesno:"true,false" }};
 {% endblock %}
 {{ module }}.get_ewkt = function(feat){
-  return 'SRID={{ srid|unlocalize }};' + {{ module }}.wkt_f.write(feat);
+    return 'SRID={{ srid|unlocalize }};' + {{ module }}.wkt_f.write(feat);
 };
 {{ module }}.read_wkt = function(wkt){
-  // OpenLayers cannot handle EWKT -- we make sure to strip it out.
-  // EWKT is only exposed to OL if there's a validation error in the admin.
-  var match = {{ module }}.re.exec(wkt);
-  if (match){wkt = match[1];}
-  return {{ module }}.wkt_f.read(wkt);
+    // OpenLayers cannot handle EWKT -- we make sure to strip it out.
+    // EWKT is only exposed to OL if there's a validation error in the admin.
+    var match = {{ module }}.re.exec(wkt);
+    if (match){wkt = match[1];}
+    return {{ module }}.wkt_f.read(wkt);
 };
 {{ module }}.write_wkt = function(feat){
-  if ({{ module }}.is_collection){ {{ module }}.num_geom = feat.geometry.components.length;}
-  else { {{ module }}.num_geom = 1;}
-  document.getElementById('{{ id }}').value = {{ module }}.get_ewkt(feat);
+    if ({{ module }}.is_collection){ {{ module }}.num_geom = feat.geometry.components.length;}
+    else { {{ module }}.num_geom = 1;}
+    document.getElementById('{{ id }}').value = {{ module }}.get_ewkt(feat);
 };
 {{ module }}.add_wkt = function(event){
-  // This function will sync the contents of the `vector` layer with the
-  // WKT in the text field.
-  if ({{ module }}.is_collection){
-    var feat = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.{{ geom_type }}());
-    for (var i = 0; i < {{ module }}.layers.vector.features.length; i++){
-      feat.geometry.addComponents([{{ module }}.layers.vector.features[i].geometry]);
-    }
-    {{ module }}.write_wkt(feat);
-  } else {
-    // Make sure to remove any previously added features.
-    if ({{ module }}.layers.vector.features.length > 1){
-      old_feats = [{{ module }}.layers.vector.features[0]];
-      {{ module }}.layers.vector.removeFeatures(old_feats);
-      {{ module }}.layers.vector.destroyFeatures(old_feats);
+    // This function will sync the contents of the `vector` layer with the
+    // WKT in the text field.
+    if ({{ module }}.is_collection){
+        var feat = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.{{ geom_type }}());
+        for (var i = 0; i < {{ module }}.layers.vector.features.length; i++){
+            feat.geometry.addComponents([{{ module }}.layers.vector.features[i].geometry]);
+        }
+        {{ module }}.write_wkt(feat);
+    } else {
+        // Make sure to remove any previously added features.
+        if ({{ module }}.layers.vector.features.length > 1){
+            old_feats = [{{ module }}.layers.vector.features[0]];
+            {{ module }}.layers.vector.removeFeatures(old_feats);
+            {{ module }}.layers.vector.destroyFeatures(old_feats);
+        }
+        {{ module }}.write_wkt(event.feature);
     }
-    {{ module }}.write_wkt(event.feature);
-  }
 };
 {{ module }}.modify_wkt = function(event){
-  if ({{ module }}.is_collection){
-    if ({{ module }}.is_point){
-      {{ module }}.add_wkt(event);
-      return;
+    if ({{ module }}.is_collection){
+        if ({{ module }}.is_point){
+            {{ module }}.add_wkt(event);
+            return;
+        } else {
+            // When modifying the selected components are added to the
+            // vector layer so we only increment to the `num_geom` value.
+            var feat = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.{{ geom_type }}());
+            for (var i = 0; i < {{ module }}.num_geom; i++){
+                feat.geometry.addComponents([{{ module }}.layers.vector.features[i].geometry]);
+            }
+            {{ module }}.write_wkt(feat);
+        }
     } else {
-      // When modifying the selected components are added to the
-      // vector layer so we only increment to the `num_geom` value.
-      var feat = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.{{ geom_type }}());
-      for (var i = 0; i < {{ module }}.num_geom; i++){
-        feat.geometry.addComponents([{{ module }}.layers.vector.features[i].geometry]);
-      }
-      {{ module }}.write_wkt(feat);
+        {{ module }}.write_wkt(event.feature);
     }
-  } else {
-    {{ module }}.write_wkt(event.feature);
-  }
 };
 // Function to clear vector features and purge wkt from div
 {{ module }}.deleteFeatures = function(){
-  {{ module }}.layers.vector.removeFeatures({{ module }}.layers.vector.features);
-  {{ module }}.layers.vector.destroyFeatures();
+    {{ module }}.layers.vector.removeFeatures({{ module }}.layers.vector.features);
+    {{ module }}.layers.vector.destroyFeatures();
 };
 {{ module }}.clearFeatures = function (){
-  {{ module }}.deleteFeatures();
-  document.getElementById('{{ id }}').value = '';
-  {% localize off %}
-  {{ module }}.map.setCenter(new OpenLayers.LonLat({{ default_lon }}, {{ default_lat }}), {{ default_zoom }});
-  {% endlocalize %}
+    {{ module }}.deleteFeatures();
+    document.getElementById('{{ id }}').value = '';
+    {% localize off %}
+    {{ module }}.map.setCenter(new OpenLayers.LonLat({{ default_lon }}, {{ default_lat }}), {{ default_zoom }});
+    {% endlocalize %}
 };
 // Add Select control
 {{ module }}.addSelectControl = function(){
-  var select = new OpenLayers.Control.SelectFeature({{ module }}.layers.vector, {'toggle' : true, 'clickout' : true});
-  {{ module }}.map.addControl(select);
-  select.activate();
+    var select = new OpenLayers.Control.SelectFeature({{ module }}.layers.vector, {'toggle' : true, 'clickout' : true});
+    {{ module }}.map.addControl(select);
+    select.activate();
 };
 {{ module }}.enableDrawing = function(){
-  {{ module }}.map.getControlsByClass('OpenLayers.Control.DrawFeature')[0].activate();
+    {{ module }}.map.getControlsByClass('OpenLayers.Control.DrawFeature')[0].activate();
 };
 {{ module }}.enableEditing = function(){
-  {{ module }}.map.getControlsByClass('OpenLayers.Control.ModifyFeature')[0].activate();
+    {{ module }}.map.getControlsByClass('OpenLayers.Control.ModifyFeature')[0].activate();
 };
 // Create an array of controls based on geometry type
 {{ module }}.getControls = function(lyr){
-  {{ module }}.panel = new OpenLayers.Control.Panel({'displayClass': 'olControlEditingToolbar'});
-  {{ module }}.controls = [new OpenLayers.Control.Navigation()];
-  if (!{{ module }}.modifiable && lyr.features.length) return;
-  if ({{ module }}.is_linestring || {{ module }}.is_generic){
-    {{ module }}.controls.push(new OpenLayers.Control.DrawFeature(lyr, OpenLayers.Handler.Path, {'displayClass': 'olControlDrawFeaturePath'}));
-  }
-  if ({{ module }}.is_polygon || {{ module }}.is_generic){
-    {{ module }}.controls.push(new OpenLayers.Control.DrawFeature(lyr, OpenLayers.Handler.Polygon, {'displayClass': 'olControlDrawFeaturePolygon'}));
-  }
-  if ({{ module }}.is_point || {{ module }}.is_generic){
-    {{ module }}.controls.push(new OpenLayers.Control.DrawFeature(lyr, OpenLayers.Handler.Point, {'displayClass': 'olControlDrawFeaturePoint'}));
-  }
-  if ({{ module }}.modifiable){
-    {{ module }}.controls.push(new OpenLayers.Control.ModifyFeature(lyr, {'displayClass': 'olControlModifyFeature'}));
-  }
+    {{ module }}.panel = new OpenLayers.Control.Panel({'displayClass': 'olControlEditingToolbar'});
+    {{ module }}.controls = [new OpenLayers.Control.Navigation()];
+    if (!{{ module }}.modifiable && lyr.features.length) return;
+    if ({{ module }}.is_linestring || {{ module }}.is_generic){
+        {{ module }}.controls.push(new OpenLayers.Control.DrawFeature(lyr, OpenLayers.Handler.Path, {'displayClass': 'olControlDrawFeaturePath'}));
+    }
+    if ({{ module }}.is_polygon || {{ module }}.is_generic){
+        {{ module }}.controls.push(new OpenLayers.Control.DrawFeature(lyr, OpenLayers.Handler.Polygon, {'displayClass': 'olControlDrawFeaturePolygon'}));
+    }
+    if ({{ module }}.is_point || {{ module }}.is_generic){
+        {{ module }}.controls.push(new OpenLayers.Control.DrawFeature(lyr, OpenLayers.Handler.Point, {'displayClass': 'olControlDrawFeaturePoint'}));
+    }
+    if ({{ module }}.modifiable){
+        {{ module }}.controls.push(new OpenLayers.Control.ModifyFeature(lyr, {'displayClass': 'olControlModifyFeature'}));
+    }
 };
 {{ module }}.init = function(){
     {% block map_options %}// The options hash, w/ zoom, resolution, and projection settings.
@@ -124,28 +124,28 @@ OpenLayers.Projection.addTransform("EPSG:4326", "EPSG:3857", OpenLayers.Layer.Sp
     // Read WKT from the text field.
     var wkt = document.getElementById('{{ id }}').value;
     if (wkt){
-      // After reading into geometry, immediately write back to
-      // WKT <textarea> as EWKT (so that SRID is included).
-      var admin_geom = {{ module }}.read_wkt(wkt);
-      {{ module }}.write_wkt(admin_geom);
-      if ({{ module }}.is_collection){
-        // If geometry collection, add each component individually so they may be
-        // edited individually.
-        for (var i = 0; i < {{ module }}.num_geom; i++){
-          {{ module }}.layers.vector.addFeatures([new OpenLayers.Feature.Vector(admin_geom.geometry.components[i].clone())]);
+        // After reading into geometry, immediately write back to
+        // WKT <textarea> as EWKT (so that SRID is included).
+        var admin_geom = {{ module }}.read_wkt(wkt);
+        {{ module }}.write_wkt(admin_geom);
+        if ({{ module }}.is_collection){
+            // If geometry collection, add each component individually so they may be
+            // edited individually.
+            for (var i = 0; i < {{ module }}.num_geom; i++){
+                {{ module }}.layers.vector.addFeatures([new OpenLayers.Feature.Vector(admin_geom.geometry.components[i].clone())]);
+            }
+        } else {
+            {{ module }}.layers.vector.addFeatures([admin_geom]);
+        }
+        // Zooming to the bounds.
+        {{ module }}.map.zoomToExtent(admin_geom.geometry.getBounds());
+        if ({{ module }}.is_point){
+            {{ module }}.map.zoomTo({{ point_zoom }});
         }
-      } else {
-        {{ module }}.layers.vector.addFeatures([admin_geom]);
-      }
-      // Zooming to the bounds.
-      {{ module }}.map.zoomToExtent(admin_geom.geometry.getBounds());
-      if ({{ module }}.is_point){
-          {{ module }}.map.zoomTo({{ point_zoom }});
-      }
     } else {
-      {% localize off %}
-      {{ module }}.map.setCenter(new OpenLayers.LonLat({{ default_lon }}, {{ default_lat }}), {{ default_zoom }});
-      {% endlocalize %}
+        {% localize off %}
+        {{ module }}.map.setCenter(new OpenLayers.LonLat({{ default_lon }}, {{ default_lat }}), {{ default_zoom }});
+        {% endlocalize %}
     }
     // This allows editing of the geographic fields -- the modified WKT is
     // written back to the content field (as EWKT, so that the ORM will know
@@ -167,10 +167,10 @@ OpenLayers.Projection.addTransform("EPSG:4326", "EPSG:3857", OpenLayers.Layer.Sp
     {% if not scrollable %}{{ module }}.map.getControlsByClass('OpenLayers.Control.Navigation')[0].disableZoomWheel();{% endif %}
     {% endblock %}
     if (wkt){
-      if ({{ module }}.modifiable){
-        {{ module }}.enableEditing();
-      }
+        if ({{ module }}.modifiable){
+            {{ module }}.enableEditing();
+        }
     } else {
-      {{ module }}.enableDrawing();
+        {{ module }}.enableDrawing();
     }
 };

+ 12 - 0
docs/internals/contributing/writing-code/coding-style.txt

@@ -7,6 +7,11 @@ Please follow these coding standards when writing code for inclusion in Django.
 Python style
 ------------
 
+* Please conform to the indentation style dictated in the ``.editorconfig``
+  file. We recommend using a text editor with `EditorConfig`_ support to avoid
+  indentation and whitespace issues. The Python files use 4 spaces for
+  indentation and the HTML files use 2 spaces.
+
 * Unless otherwise specified, follow :pep:`8`.
 
   Use `flake8`_ to check for problems in this area. Note that our ``setup.cfg``
@@ -286,4 +291,11 @@ Miscellaneous
   change to the ``AUTHORS`` file in your patch if you make more than a
   single trivial change.
 
+JavaScript style
+----------------
+
+For details about the JavaScript code style used by Django, see
+:doc:`javascript`.
+
+.. _editorconfig: http://editorconfig.org/
 .. _flake8: https://pypi.python.org/pypi/flake8

+ 1 - 0
docs/internals/contributing/writing-code/index.txt

@@ -13,3 +13,4 @@ chances to be included in Django core:
    unit-tests
    submitting-patches
    working-with-git
+   javascript

+ 64 - 0
docs/internals/contributing/writing-code/javascript.txt

@@ -0,0 +1,64 @@
+==========
+JavaScript
+==========
+
+While most of Django core is Python, the ``admin`` and ``gis`` contrib apps
+contain JavaScript code.
+
+Please follow these coding standards when writing JavaScript code for inclusion
+in Django.
+
+Code style
+----------
+
+* Please conform to the indentation style dictated in the ``.editorconfig``
+  file. We recommend using a text editor with `EditorConfig`_ support to avoid
+  indentation and whitespace issues. Most of the JavaScript files use 4 spaces
+  for indentation, but there are some exceptions.
+
+* When naming variables, use ``camelCase`` instead of ``underscore_case``.
+  Different JavaScript files sometimes use a different code style. Please try to
+  conform to the code style of each file.
+
+* Use the `JSHint`_ code linter to check your code for bugs and style errors.
+  JSHint will be run when you run the JavaScript tests. We also recommended
+  installing a JSHint plugin in your text editor.
+
+.. _javascript-patches:
+
+JavaScript patches
+------------------
+
+Django's admin system leverages the jQuery framework to increase the
+capabilities of the admin interface. In conjunction, there is an emphasis on
+admin JavaScript performance and minimizing overall admin media file size.
+Serving compressed or "minified" versions of JavaScript files is considered
+best practice in this regard.
+
+To that end, patches for JavaScript files should include both the original
+code for future development (e.g. ``foo.js``), and a compressed version for
+production use (e.g. ``foo.min.js``). Any links to the file in the codebase
+should point to the compressed version.
+
+Compressing JavaScript
+~~~~~~~~~~~~~~~~~~~~~~
+
+To simplify the process of providing optimized JavaScript code, Django
+includes a handy Python script which should be used to create a "minified"
+version. To run it::
+
+    python django/contrib/admin/bin/compress.py
+
+Behind the scenes, ``compress.py`` is a front-end for Google's
+`Closure Compiler`_ which is written in Java. However, the Closure Compiler
+library is not bundled with Django directly, so those wishing to contribute
+complete JavaScript patches will need to download and install the library
+independently. The Closure Compiler library requires `Java`_ 7 or higher.
+
+Please don't forget to run ``compress.py`` and include the ``diff`` of the
+minified scripts when submitting patches for Django's JavaScript.
+
+.. _Closure Compiler: https://developers.google.com/closure/compiler/
+.. _EditorConfig: http://editorconfig.org/
+.. _Java: https://www.java.com
+.. _jshint: http://jshint.com/

+ 4 - 33
docs/internals/contributing/writing-code/submitting-patches.txt

@@ -145,6 +145,8 @@ Regardless of the way you submit your work, follow these steps.
   obvious that the ticket includes a patch, and it will add the ticket to
   the `list of tickets with patches`_.
 
+.. _list of tickets with patches: https://code.djangoproject.com/query?status=new&status=assigned&status=reopened&has_patch=1&order=priority
+.. _ticket tracker: https://code.djangoproject.com/newticket
 
 Non-trivial patches
 -------------------
@@ -245,39 +247,8 @@ the new version are removed.
 JavaScript patches
 ------------------
 
-Django's admin system leverages the jQuery framework to increase the
-capabilities of the admin interface. In conjunction, there is an emphasis on
-admin JavaScript performance and minimizing overall admin media file size.
-Serving compressed or "minified" versions of JavaScript files is considered
-best practice in this regard.
-
-To that end, patches for JavaScript files should include both the original
-code for future development (e.g. ``foo.js``), and a compressed version for
-production use (e.g. ``foo.min.js``). Any links to the file in the codebase
-should point to the compressed version.
-
-Compressing JavaScript
-~~~~~~~~~~~~~~~~~~~~~~
-
-To simplify the process of providing optimized JavaScript code, Django
-includes a handy Python script which should be used to create a "minified"
-version. To run it::
-
-    python django/contrib/admin/bin/compress.py
-
-Behind the scenes, ``compress.py`` is a front-end for Google's
-`Closure Compiler`_ which is written in Java. However, the Closure Compiler
-library is not bundled with Django directly, so those wishing to contribute
-complete JavaScript patches will need to download and install the library
-independently. The Closure Compiler library requires `Java`_ 7 or higher.
-
-Please don't forget to run ``compress.py`` and include the ``diff`` of the
-minified scripts when submitting patches for Django's JavaScript.
-
-.. _Closure Compiler: https://developers.google.com/closure/compiler/
-.. _list of tickets with patches: https://code.djangoproject.com/query?status=new&status=assigned&status=reopened&has_patch=1&order=priority
-.. _ticket tracker: https://code.djangoproject.com/newticket
-.. _Java: https://www.java.com
+For information on JavaScript patches, see the :ref:`javascript-patches`
+documentation.
 
 .. _patch-review-checklist:
 

+ 10 - 0
package.json

@@ -0,0 +1,10 @@
+{
+  "name": "Django",
+  "private": true,
+  "scripts": {
+    "pretest": "eslint django/"
+  },
+  "devDependencies": {
+    "eslint": "^0.22.1"
+  }
+}