Browse Source

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

Trey Hunner 10 năm trước cách đây
mục cha
commit
ec4f219ecb
26 tập tin đã thay đổi với 1007 bổ sung842 xóa
  1. 37 0
      .editorconfig
  2. 3 0
      .eslintignore
  3. 51 0
      .eslintrc
  4. 1 0
      .gitignore
  5. 8 9
      django/contrib/admin/static/admin/js/SelectBox.js
  6. 183 182
      django/contrib/admin/static/admin/js/SelectFilter2.js
  7. 142 141
      django/contrib/admin/static/admin/js/actions.js
  8. 35 34
      django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js
  9. 12 11
      django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js
  10. 22 19
      django/contrib/admin/static/admin/js/calendar.js
  11. 23 22
      django/contrib/admin/static/admin/js/collapse.js
  12. 2 2
      django/contrib/admin/static/admin/js/collapse.min.js
  13. 29 28
      django/contrib/admin/static/admin/js/core.js
  14. 237 234
      django/contrib/admin/static/admin/js/inlines.js
  15. 9 9
      django/contrib/admin/static/admin/js/inlines.min.js
  16. 1 0
      django/contrib/admin/static/admin/js/jquery.init.js
  17. 1 0
      django/contrib/admin/static/admin/js/prepopulate.js
  18. 1 1
      django/contrib/admin/static/admin/js/prepopulate.min.js
  19. 7 7
      django/contrib/admin/static/admin/js/timeparse.js
  20. 22 20
      django/contrib/gis/static/gis/js/OLMapWidget.js
  21. 90 90
      django/contrib/gis/templates/gis/admin/openlayers.js
  22. 12 0
      docs/internals/contributing/writing-code/coding-style.txt
  23. 1 0
      docs/internals/contributing/writing-code/index.txt
  24. 64 0
      docs/internals/contributing/writing-code/javascript.txt
  25. 4 33
      docs/internals/contributing/writing-code/submitting-patches.txt
  26. 10 0
      package.json

+ 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"
+  }
+}