');
}
- $(this.element).after(this.progress.element);
+ $(this.element).after(jqXHR.progressElement);
}
+
+ // Register the AJAX request so it can be cancelled if needed.
+ this.currentRequests.push(jqXHR);
};
/**
* Handler for the form redirection completion.
*/
-Drupal.ajax.prototype.success = function (response, status) {
- // Remove the progress element.
- if (this.progress.element) {
- $(this.progress.element).remove();
- }
- if (this.progress.object) {
- this.progress.object.stopMonitoring();
- }
- $(this.element).removeClass('progress-disabled').prop('disabled', false);
-
- Drupal.freezeHeight();
+Backdrop.ajax.prototype.success = function (response, status, jqXHR) {
+ // Remove the throbber and progress elements.
+ this.cleanUp(jqXHR);
+ // Process the response.
+ Backdrop.freezeHeight();
for (var i in response) {
- if (response.hasOwnProperty(i) && response[i]['command'] && this.commands[response[i]['command']]) {
- this.commands[response[i]['command']](this, response[i], status);
+ if (response.hasOwnProperty(i) && response[i].command && this.commands[response[i].command]) {
+ this.commands[response[i].command](this, response[i], status);
}
}
@@ -438,21 +444,43 @@ Drupal.ajax.prototype.success = function (response, status) {
// commands is not sufficient, because behaviors from the entire form need
// to be reattached.
if (this.form) {
- var settings = this.settings || Drupal.settings;
- Drupal.attachBehaviors(this.form, settings);
+ var settings = this.settings || Backdrop.settings;
+ Backdrop.attachBehaviors(this.form, settings);
}
- Drupal.unfreezeHeight();
+ Backdrop.unfreezeHeight();
// Remove any response-specific settings so they don't get used on the next
// call by mistake.
this.settings = null;
};
+/**
+ * Clean up after an AJAX response, success or failure.
+ */
+Backdrop.ajax.prototype.cleanUp = function (jqXHR) {
+ // Remove the AJAX request from the current list.
+ var index = this.currentRequests.indexOf(jqXHR);
+ if (index > -1) {
+ this.currentRequests.splice(index, 1);
+ }
+
+ // Remove the progress element.
+ if (jqXHR.progressElement) {
+ $(jqXHR.progressElement).remove();
+ }
+ if (jqXHR.progressBar) {
+ jqXHR.progressBar.stopMonitoring();
+ }
+
+ // Reactivate the triggering element.
+ $(this.element).removeClass('progress-disabled').prop('disabled', false);
+}
+
/**
* Build an effect object which tells us how to apply the effect when adding new HTML.
*/
-Drupal.ajax.prototype.getEffect = function (response) {
+Backdrop.ajax.prototype.getEffect = function (response) {
var type = response.effect || this.effect;
var speed = response.speed || this.speed;
@@ -479,30 +507,20 @@ Drupal.ajax.prototype.getEffect = function (response) {
/**
* Handler for the form redirection error.
*/
-Drupal.ajax.prototype.error = function (response, uri) {
- alert(Drupal.ajaxError(response, uri));
- // Remove the progress element.
- if (this.progress.element) {
- $(this.progress.element).remove();
- }
- if (this.progress.object) {
- this.progress.object.stopMonitoring();
- }
- // Undo hide.
- $(this.wrapper).show();
- // Re-enable the element.
- $(this.element).removeClass('progress-disabled').removeAttr('disabled');
+Backdrop.ajax.prototype.error = function (jqXHR, uri) {
+ this.cleanUp(jqXHR);
+
// Reattach behaviors, if they were detached in beforeSerialize().
if (this.form) {
- var settings = response.settings || this.settings || Drupal.settings;
- Drupal.attachBehaviors(this.form, settings);
+ var settings = this.settings || Backdrop.settings;
+ Backdrop.attachBehaviors(this.form, settings);
}
};
/**
* Provide a series of commands that the server can request the client perform.
*/
-Drupal.ajax.prototype.commands = {
+Backdrop.ajax.prototype.commands = {
/**
* Command to insert new content into the DOM.
*/
@@ -512,13 +530,14 @@ Drupal.ajax.prototype.commands = {
var wrapper = response.selector ? $(response.selector) : $(ajax.wrapper);
var method = response.method || ajax.method;
var effect = ajax.getEffect(response);
+ var settings;
// We don't know what response.data contains: it might be a string of text
// without HTML, so don't rely on jQuery correctly iterpreting
// $(response.data) as new HTML rather than a CSS selector. Also, if
// response.data contains top-level text nodes, they get lost with either
// $(response.data) or $('').replaceWith(response.data).
- var new_content_wrapped = $('').html(response.data);
+ var new_content_wrapped = $('').html(response.data.replace(/^\s+|\s+$/g, ''));
var new_content = new_content_wrapped.contents();
// For legacy reasons, the effects processing code assumes that new_content
@@ -542,8 +561,8 @@ Drupal.ajax.prototype.commands = {
case 'replaceAll':
case 'empty':
case 'remove':
- var settings = response.settings || ajax.settings || Drupal.settings;
- Drupal.detachBehaviors(wrapper, settings);
+ settings = response.settings || ajax.settings || Backdrop.settings;
+ Backdrop.detachBehaviors(wrapper, settings);
}
// Add the new content to the page.
@@ -570,8 +589,8 @@ Drupal.ajax.prototype.commands = {
// optional.
if (new_content.parents('html').length > 0) {
// Apply any settings from the returned JSON if available.
- var settings = response.settings || ajax.settings || Drupal.settings;
- Drupal.attachBehaviors(new_content, settings);
+ settings = response.settings || ajax.settings || Backdrop.settings;
+ Backdrop.attachBehaviors(new_content, settings);
}
},
@@ -579,8 +598,8 @@ Drupal.ajax.prototype.commands = {
* Command to remove a chunk from the page.
*/
remove: function (ajax, response, status) {
- var settings = response.settings || ajax.settings || Drupal.settings;
- Drupal.detachBehaviors($(response.selector), settings);
+ var settings = response.settings || ajax.settings || Backdrop.settings;
+ Backdrop.detachBehaviors($(response.selector), settings);
$(response.selector).remove();
},
@@ -591,7 +610,7 @@ Drupal.ajax.prototype.commands = {
if (!$(response.selector).hasClass('ajax-changed')) {
$(response.selector).addClass('ajax-changed');
if (response.asterisk) {
- $(response.selector).find(response.asterisk).append(' * ');
+ $(response.selector).find(response.asterisk).append(' * ');
}
}
},
@@ -600,7 +619,7 @@ Drupal.ajax.prototype.commands = {
* Command to provide an alert.
*/
alert: function (ajax, response, status) {
- alert(response.text, response.title);
+ window.alert(response.text, response.title);
},
/**
@@ -622,7 +641,7 @@ Drupal.ajax.prototype.commands = {
*/
settings: function (ajax, response, status) {
if (response.merge) {
- $.extend(true, Drupal.settings, response.settings);
+ $.extend(true, Backdrop.settings, response.settings);
}
else {
ajax.settings = response.settings;
@@ -641,7 +660,7 @@ Drupal.ajax.prototype.commands = {
*/
invoke: function (ajax, response, status) {
var $element = $(response.selector);
- $element[response.method].apply($element, response.arguments);
+ $element[response.method].apply($element, response.args);
},
/**
@@ -655,7 +674,34 @@ Drupal.ajax.prototype.commands = {
.removeClass('odd even')
.filter(':even').addClass('odd').end()
.filter(':odd').addClass('even');
+ },
+
+ /**
+ * Command to add css.
+ *
+ * Uses the proprietary addImport method if available as browsers which
+ * support that method ignore @import statements in dynamically added
+ * stylesheets.
+ */
+ addCss: function (ajax, response, status) {
+ // Add the styles in the normal way.
+ $('head').prepend(response.data);
+ // Add imports in the styles using the addImport method if available.
+ var match, importMatch = /^@import url\("(.*)"\);$/igm;
+ if (document.styleSheets[0].addImport && importMatch.test(response.data)) {
+ importMatch.lastIndex = 0;
+ while (match = importMatch.exec(response.data)) {
+ document.styleSheets[0].addImport(match[1]);
+ }
+ }
+ },
+
+ /**
+ * Command to update a form's build ID.
+ */
+ updateBuildId: function(ajax, response, status) {
+ $('input[name="form_build_id"][value="' + response['old'] + '"]').val(response['new']);
}
};
-})(jQuery);
+})(jQuery, this);
diff --git a/core/misc/arrow-asc.png b/core/misc/arrow-asc.png
old mode 100644
new mode 100755
diff --git a/core/misc/arrow-desc.png b/core/misc/arrow-desc.png
old mode 100644
new mode 100755
diff --git a/core/misc/autocomplete.js b/core/misc/autocomplete.js
old mode 100644
new mode 100755
index fb74c45..a62166f
--- a/core/misc/autocomplete.js
+++ b/core/misc/autocomplete.js
@@ -3,24 +3,24 @@
/**
* Attaches the autocomplete behavior to all required fields.
*/
-Drupal.behaviors.autocomplete = {
+Backdrop.behaviors.autocomplete = {
attach: function (context, settings) {
var acdb = [];
$('input.autocomplete', context).once('autocomplete', function () {
var uri = this.value;
if (!acdb[uri]) {
- acdb[uri] = new Drupal.ACDB(uri);
+ acdb[uri] = new Backdrop.ACDB(uri);
}
var $input = $('#' + this.id.substr(0, this.id.length - 13))
.attr('autocomplete', 'OFF')
.attr('aria-autocomplete', 'list');
- $($input[0].form).submit(Drupal.autocompleteSubmit);
+ $($input[0].form).submit(Backdrop.autocompleteSubmit);
$input.parent()
.attr('role', 'application')
.append($('')
.attr('id', $input[0].id + '-autocomplete-aria-live')
);
- new Drupal.jsAC($input, acdb[uri]);
+ new Backdrop.jsAC($input, acdb[uri]);
});
}
};
@@ -29,7 +29,7 @@ Drupal.behaviors.autocomplete = {
* Prevents the form from submitting if the suggestions popup is open
* and closes the suggestions popup when doing so.
*/
-Drupal.autocompleteSubmit = function () {
+Backdrop.autocompleteSubmit = function () {
var $autocomplete = $('#autocomplete');
if ($autocomplete.length !== 0) {
$autocomplete[0].owner.hidePopup();
@@ -40,7 +40,7 @@ Drupal.autocompleteSubmit = function () {
/**
* An AutoComplete object.
*/
-Drupal.jsAC = function ($input, db) {
+Backdrop.jsAC = function ($input, db) {
var ac = this;
this.input = $input[0];
this.ariaLive = $('#' + this.input.id + '-autocomplete-aria-live');
@@ -55,7 +55,7 @@ Drupal.jsAC = function ($input, db) {
/**
* Handler for the "keydown" event.
*/
-Drupal.jsAC.prototype.onkeydown = function (input, e) {
+Backdrop.jsAC.prototype.onkeydown = function (input, e) {
if (!e) {
e = window.event;
}
@@ -74,7 +74,7 @@ Drupal.jsAC.prototype.onkeydown = function (input, e) {
/**
* Handler for the "keyup" event.
*/
-Drupal.jsAC.prototype.onkeyup = function (input, e) {
+Backdrop.jsAC.prototype.onkeyup = function (input, e) {
if (!e) {
e = window.event;
}
@@ -113,14 +113,14 @@ Drupal.jsAC.prototype.onkeyup = function (input, e) {
/**
* Puts the currently highlighted suggestion into the autocomplete field.
*/
-Drupal.jsAC.prototype.select = function (node) {
+Backdrop.jsAC.prototype.select = function (node) {
this.input.value = $(node).data('autocompleteValue');
};
/**
* Highlights the next suggestion.
*/
-Drupal.jsAC.prototype.selectDown = function () {
+Backdrop.jsAC.prototype.selectDown = function () {
if (this.selected && this.selected.nextSibling) {
this.highlight(this.selected.nextSibling);
}
@@ -135,7 +135,7 @@ Drupal.jsAC.prototype.selectDown = function () {
/**
* Highlights the previous suggestion.
*/
-Drupal.jsAC.prototype.selectUp = function () {
+Backdrop.jsAC.prototype.selectUp = function () {
if (this.selected && this.selected.previousSibling) {
this.highlight(this.selected.previousSibling);
}
@@ -144,7 +144,7 @@ Drupal.jsAC.prototype.selectUp = function () {
/**
* Highlights a suggestion.
*/
-Drupal.jsAC.prototype.highlight = function (node) {
+Backdrop.jsAC.prototype.highlight = function (node) {
// Unhighlights a suggestion for "keyup" and "keydown" events.
if (this.selected !== false) {
$(this.selected).removeClass('selected');
@@ -157,7 +157,7 @@ Drupal.jsAC.prototype.highlight = function (node) {
/**
* Unhighlights a suggestion.
*/
-Drupal.jsAC.prototype.unhighlight = function (node) {
+Backdrop.jsAC.prototype.unhighlight = function (node) {
$(node).removeClass('selected');
this.selected = false;
$(this.ariaLive).empty();
@@ -166,7 +166,7 @@ Drupal.jsAC.prototype.unhighlight = function (node) {
/**
* Hides the autocomplete suggestions.
*/
-Drupal.jsAC.prototype.hidePopup = function (keycode) {
+Backdrop.jsAC.prototype.hidePopup = function (keycode) {
// Select item if the right key or mousebutton was pressed.
if (this.selected && ((keycode && keycode !== 46 && keycode !== 8 && keycode !== 27) || !keycode)) {
this.input.value = $(this.selected).data('autocompleteValue');
@@ -184,7 +184,7 @@ Drupal.jsAC.prototype.hidePopup = function (keycode) {
/**
* Positions the suggestions popup and starts a search.
*/
-Drupal.jsAC.prototype.populatePopup = function () {
+Backdrop.jsAC.prototype.populatePopup = function () {
var $input = $(this.input);
var position = $input.position();
// Show popup.
@@ -210,22 +210,22 @@ Drupal.jsAC.prototype.populatePopup = function () {
/**
* Fills the suggestion popup with any matches received.
*/
-Drupal.jsAC.prototype.found = function (matches) {
+Backdrop.jsAC.prototype.found = function (matches) {
// If no value in the textfield, do not show the popup.
if (!this.input.value.length) {
return false;
}
// Prepare matches.
- var ul = $('
');
var ac = this;
+ var ul = $('
')
+ .on('mousedown', 'li', function (e) { ac.select(this); })
+ .on('mouseover', 'li', function (e) { ac.highlight(this); })
+ .on('mouseout', 'li', function (e) { ac.unhighlight(this); });
for (var key in matches) {
if (matches.hasOwnProperty(key)) {
$('')
.html($('').html(matches[key]))
- .mousedown(function () { ac.select(this); })
- .mouseover(function () { ac.highlight(this); })
- .mouseout(function () { ac.unhighlight(this); })
.data('autocompleteValue', key)
.appendTo(ul);
}
@@ -235,7 +235,7 @@ Drupal.jsAC.prototype.found = function (matches) {
if (this.popup) {
if (ul.children().length) {
$(this.popup).empty().append(ul).show();
- $(this.ariaLive).html(Drupal.t('Autocomplete popup'));
+ $(this.ariaLive).html(Backdrop.t('Autocomplete popup'));
}
else {
$(this.popup).css({ visibility: 'hidden' });
@@ -244,11 +244,11 @@ Drupal.jsAC.prototype.found = function (matches) {
}
};
-Drupal.jsAC.prototype.setStatus = function (status) {
+Backdrop.jsAC.prototype.setStatus = function (status) {
switch (status) {
case 'begin':
$(this.input).addClass('throbbing');
- $(this.ariaLive).html(Drupal.t('Searching for matches...'));
+ $(this.ariaLive).html(Backdrop.t('Searching for matches...'));
break;
case 'cancel':
case 'error':
@@ -261,7 +261,7 @@ Drupal.jsAC.prototype.setStatus = function (status) {
/**
* An AutoComplete DataBase object.
*/
-Drupal.ACDB = function (uri) {
+Backdrop.ACDB = function (uri) {
this.uri = uri;
this.delay = 300;
this.cache = {};
@@ -270,7 +270,7 @@ Drupal.ACDB = function (uri) {
/**
* Performs a cached and delayed search.
*/
-Drupal.ACDB.prototype.search = function (searchString) {
+Backdrop.ACDB.prototype.search = function (searchString) {
var db = this;
this.searchString = searchString;
@@ -309,7 +309,7 @@ Drupal.ACDB.prototype.search = function (searchString) {
}
},
error: function (xmlhttp) {
- alert(Drupal.ajaxError(xmlhttp, db.uri));
+ alert(Backdrop.ajaxError(xmlhttp, db.uri));
}
});
}, this.delay);
@@ -318,7 +318,7 @@ Drupal.ACDB.prototype.search = function (searchString) {
/**
* Cancels the current autocomplete request.
*/
-Drupal.ACDB.prototype.cancel = function () {
+Backdrop.ACDB.prototype.cancel = function () {
if (this.owner) {
this.owner.setStatus('cancel');
}
diff --git a/core/misc/autosubmit.js b/core/misc/autosubmit.js
new file mode 100755
index 0000000..a4c4fed
--- /dev/null
+++ b/core/misc/autosubmit.js
@@ -0,0 +1,101 @@
+(function($){
+/**
+ * To make a form auto submit, all you have to do is 3 things:
+ *
+ * Attach the autosubmit library to the form.
+ *
+ * @code
+ * $form['#attached']['library'][] = array('system', 'backdrop.autosubmit');
+ * @endcode
+ *
+ * On elements you want to auto-submit when changed, add the autosubmit
+ * class. With FAPI, add:
+ * @code
+ * $form['field_name']['#attributes']['class'] = array('autosubmit');
+ * @endcode
+ *
+ * If you want to have auto-submit for every form element,
+ * add the autosubmit-full-form to the form. With FAPI, add:
+ * @code
+ * $form['#attributes']['class'] = array('autosubmit-full-form');
+ * @endcode
+ *
+ * If you want to exclude a field from the autosubmit-full-form auto submission,
+ * add the class autosubmit-exclude to the form element. With FAPI, add:
+ * @code
+ * $form['field_name']['#attributes']['class'] = array('autosubmit-exclude');
+ * @endcode
+ *
+ * Finally, you have to identify which button you want clicked for autosubmit.
+ * @code
+ * $form['my_button']['#attributes']['class'] = array('autosubmit-click');
+ * @endcode
+ *
+ * Currently only 'select', 'radio', 'checkbox' and 'textfield' types are
+ * supported.
+ */
+Backdrop.behaviors.autosubmit = {
+ attach: function(context) {
+ // 'this' references the form element
+ function triggerSubmit (e) {
+ var $this = $(this);
+ $this.find('.autosubmit-click').click();
+ }
+
+ // the change event bubbles so we only need to bind it to the outer form
+ $('form.autosubmit-full-form', context)
+ .add('.autosubmit', context)
+ .filter('form, select, input:not(:text, :submit)')
+ .once('autosubmit')
+ .change(function (e) {
+ // don't trigger on text change for full-form
+ if ($(e.target).is(':not(:text, :submit, .autosubmit-exclude)')) {
+ triggerSubmit.call(e.target.form);
+ }
+ });
+
+ // e.keyCode: key
+ var discardKeyCode = [
+ 16, // shift
+ 17, // ctrl
+ 18, // alt
+ 20, // caps lock
+ 33, // page up
+ 34, // page down
+ 35, // end
+ 36, // home
+ 37, // left arrow
+ 38, // up arrow
+ 39, // right arrow
+ 40, // down arrow
+ 9, // tab
+ 13, // enter
+ 27 // esc
+ ];
+ // Don't wait for change event on textfields
+ $('.autosubmit-full-form input:text, input:text.autosubmit', context)
+ .filter(':not(.autosubmit-exclude)')
+ .once('autosubmit', function () {
+ // each textinput element has his own timeout
+ var timeoutID = 0;
+ $(this)
+ .bind('keydown keyup', function (e) {
+ if ($.inArray(e.keyCode, discardKeyCode) === -1) {
+ timeoutID && clearTimeout(timeoutID);
+ }
+ })
+ .keyup(function(e) {
+ if ($.inArray(e.keyCode, discardKeyCode) === -1) {
+ timeoutID = setTimeout($.proxy(triggerSubmit, this.form), 500);
+ }
+ })
+ .bind('change', function (e) {
+ if ($.inArray(e.keyCode, discardKeyCode) === -1) {
+ timeoutID = setTimeout($.proxy(triggerSubmit, this.form), 500);
+ }
+ });
+ });
+ }
+}
+
+})(jQuery);
diff --git a/core/misc/backdrop.js b/core/misc/backdrop.js
new file mode 100755
index 0000000..7eb7339
--- /dev/null
+++ b/core/misc/backdrop.js
@@ -0,0 +1,420 @@
+/**
+ * Main JavaScript file for Backdrop CMS.
+ *
+ * This file provides central functionality to Backdrop, including attaching
+ * behaviors (the main way of interacting with JS in Backdrop), theming
+ * wrappers, and localization functions.
+ */
+(function ($, undefined) {
+
+// Define the main Backdrop object that holds settings, behavior callbacks, and
+// translated strings.
+window.Backdrop = window.Backdrop || {};
+$.extend(true, window.Backdrop, { 'settings': {}, 'behaviors': {}, 'locale': {} });
+
+// Alias the Backdrop namespace to Drupal if compatibility is enabled.
+if (Backdrop.settings.drupalCompatibility) {
+ window.Drupal = Backdrop;
+}
+
+/**
+ * Attach all registered behaviors to a page element.
+ *
+ * Behaviors are event-triggered actions that attach to page elements, enhancing
+ * default non-JavaScript UIs. Behaviors are registered in the Backdrop.behaviors
+ * object using the method 'attach' and optionally also 'detach' as follows:
+ * @code
+ * Backdrop.behaviors.behaviorName = {
+ * attach: function (context, settings) {
+ * ...
+ * },
+ * detach: function (context, settings, trigger) {
+ * ...
+ * }
+ * };
+ * @endcode
+ *
+ * Backdrop.attachBehaviors is added below to the jQuery.ready event and
+ * therefore runs on initial page load. Developers implementing Ajax in their
+ * solutions should also call this function after new page content has been
+ * loaded, feeding in an element to be processed, in order to attach all
+ * behaviors to the new content.
+ *
+ * Behaviors should use
+ * @code
+ * var elements = $(context).find(selector).once('behavior-name');
+ * @endcode
+ * to ensure the behavior is attached only once to a given element. (Doing so
+ * enables the reprocessing of given elements, which may be needed on occasion
+ * despite the ability to limit behavior attachment to a particular element.)
+ *
+ * @param context
+ * An element to attach behaviors to. If none is given, the document element
+ * is used.
+ * @param settings
+ * An object containing settings for the current context. If none is given,
+ * the global Backdrop.settings object is used.
+ */
+Backdrop.attachBehaviors = function (context, settings) {
+ context = context || document;
+ settings = settings || Backdrop.settings;
+ // Execute all of them.
+ $.each(Backdrop.behaviors, function () {
+ if ($.isFunction(this.attach)) {
+ this.attach(context, settings);
+ }
+ });
+};
+
+/**
+ * Detach registered behaviors from a page element.
+ *
+ * Developers implementing AHAH/Ajax in their solutions should call this
+ * function before page content is about to be removed, feeding in an element
+ * to be processed, in order to allow special behaviors to detach from the
+ * content.
+ *
+ * Such implementations should look for the class name that was added in their
+ * corresponding Backdrop.behaviors.behaviorName.attach implementation, i.e.
+ * behaviorName-processed, to ensure the behavior is detached only from
+ * previously processed elements.
+ *
+ * @param context
+ * An element to detach behaviors from. If none is given, the document element
+ * is used.
+ * @param settings
+ * An object containing settings for the current context. If none given, the
+ * global Backdrop.settings object is used.
+ * @param trigger
+ * A string containing what's causing the behaviors to be detached. The
+ * possible triggers are:
+ * - unload: (default) The context element is being removed from the DOM.
+ * - move: The element is about to be moved within the DOM (for example,
+ * during a tabledrag row swap). After the move is completed,
+ * Backdrop.attachBehaviors() is called, so that the behavior can undo
+ * whatever it did in response to the move. Many behaviors won't need to
+ * do anything simply in response to the element being moved, but because
+ * IFRAME elements reload their "src" when being moved within the DOM,
+ * behaviors bound to IFRAME elements (like WYSIWYG editors) may need to
+ * take some action.
+ * - serialize: When an Ajax form is submitted, this is called with the
+ * form as the context. This provides every behavior within the form an
+ * opportunity to ensure that the field elements have correct content
+ * in them before the form is serialized. The canonical use-case is so
+ * that WYSIWYG editors can update the hidden textarea to which they are
+ * bound.
+ *
+ * @see Backdrop.attachBehaviors
+ */
+Backdrop.detachBehaviors = function (context, settings, trigger) {
+ context = context || document;
+ settings = settings || Backdrop.settings;
+ trigger = trigger || 'unload';
+ // Execute all of them.
+ $.each(Backdrop.behaviors, function () {
+ if ($.isFunction(this.detach)) {
+ this.detach(context, settings, trigger);
+ }
+ });
+};
+
+/**
+ * Encode special characters in a plain-text string for display as HTML.
+ *
+ * @param str
+ * The string to be encoded.
+ * @return
+ * The encoded string.
+ * @ingroup sanitization
+ */
+Backdrop.checkPlain = function (str) {
+ str = str.toString()
+ .replace(/&/g, '&')
+ .replace(/"/g, '"')
+ .replace(//g, '>');
+ return str;
+};
+
+/**
+ * Replace placeholders with sanitized values in a string.
+ *
+ * @param str
+ * A string with placeholders.
+ * @param args
+ * An object of replacements pairs to make. Incidences of any key in this
+ * array are replaced with the corresponding value. Based on the first
+ * character of the key, the value is escaped and/or themed:
+ * - !variable: inserted as is
+ * - @variable: escape plain text to HTML (Backdrop.checkPlain)
+ * - %variable: escape text and theme as a placeholder for user-submitted
+ * content (checkPlain + Backdrop.theme('placeholder'))
+ *
+ * @see Backdrop.t()
+ * @ingroup sanitization
+ */
+Backdrop.formatString = function(str, args) {
+ // Transform arguments before inserting them.
+ for (var key in args) {
+ if (args.hasOwnProperty(key)) {
+ switch (key.charAt(0)) {
+ // Escaped only.
+ case '@':
+ args[key] = Backdrop.checkPlain(args[key]);
+ break;
+ // Pass-through.
+ case '!':
+ break;
+ // Escaped and placeholder.
+ case '%':
+ default:
+ args[key] = Backdrop.theme('placeholder', args[key]);
+ break;
+ }
+ str = str.replace(key, args[key]);
+ }
+ }
+ return str;
+};
+
+/**
+ * Translate strings to the page language or a given language.
+ *
+ * See the documentation of the server-side t() function for further details.
+ *
+ * @param str
+ * A string containing the English string to translate.
+ * @param args
+ * An object of replacements pairs to make after translation. Incidences
+ * of any key in this array are replaced with the corresponding value.
+ * See Backdrop.formatString().
+ *
+ * @param options
+ * - 'context' (defaults to the empty context): The context the source string
+ * belongs to.
+ *
+ * @return
+ * The translated string.
+ */
+Backdrop.t = function (str, args, options) {
+ options = options || {};
+ options.context = options.context || '';
+
+ // Fetch the localized version of the string.
+ if (Backdrop.locale.strings && Backdrop.locale.strings[options.context] && Backdrop.locale.strings[options.context][str]) {
+ str = Backdrop.locale.strings[options.context][str];
+ }
+
+ if (args) {
+ str = Backdrop.formatString(str, args);
+ }
+ return str;
+};
+
+/**
+ * Format a string containing a count of items.
+ *
+ * This function ensures that the string is pluralized correctly. Since Backdrop.t() is
+ * called by this function, make sure not to pass already-localized strings to it.
+ *
+ * See the documentation of the server-side format_plural() function for further details.
+ *
+ * @param count
+ * The item count to display.
+ * @param singular
+ * The string for the singular case. Please make sure it is clear this is
+ * singular, to ease translation (e.g. use "1 new comment" instead of "1 new").
+ * Do not use @count in the singular string.
+ * @param plural
+ * The string for the plural case. Please make sure it is clear this is plural,
+ * to ease translation. Use @count in place of the item count, as in "@count
+ * new comments".
+ * @param args
+ * An object of replacements pairs to make after translation. Incidences
+ * of any key in this array are replaced with the corresponding value.
+ * See Backdrop.formatString().
+ * Note that you do not need to include @count in this array.
+ * This replacement is done automatically for the plural case.
+ * @param options
+ * The options to pass to the Backdrop.t() function.
+ * @return
+ * A translated string.
+ */
+Backdrop.formatPlural = function (count, singular, plural, args, options) {
+ args = args || {};
+ args['@count'] = count;
+ // Determine the index of the plural form.
+ var index = Backdrop.locale.pluralFormula ? Backdrop.locale.pluralFormula(args['@count']) : ((args['@count'] == 1) ? 0 : 1);
+
+ if (index == 0) {
+ return Backdrop.t(singular, args, options);
+ }
+ else if (index == 1) {
+ return Backdrop.t(plural, args, options);
+ }
+ else {
+ args['@count[' + index + ']'] = args['@count'];
+ delete args['@count'];
+ return Backdrop.t(plural.replace('@count', '@count[' + index + ']'), args, options);
+ }
+};
+
+/**
+ * Generate the themed representation of a Backdrop object.
+ *
+ * All requests for themed output must go through this function. It examines
+ * the request and routes it to the appropriate theme function. If the current
+ * theme does not provide an override function, the generic theme function is
+ * called.
+ *
+ * For example, to retrieve the HTML for text that should be emphasized and
+ * displayed as a placeholder inside a sentence, call
+ * Backdrop.theme('placeholder', text).
+ *
+ * @param func
+ * The name of the theme function to call.
+ * @param ...
+ * Additional arguments to pass along to the theme function.
+ * @return
+ * Any data the theme function returns. This could be a plain HTML string,
+ * but also a complex object.
+ */
+Backdrop.theme = function (func) {
+ var args = Array.prototype.slice.apply(arguments, [1]);
+
+ return (Backdrop.theme[func] || Backdrop.theme.prototype[func]).apply(this, args);
+};
+
+/**
+ * Freeze the current body height (as minimum height). Used to prevent
+ * unnecessary upwards scrolling when doing DOM manipulations.
+ */
+Backdrop.freezeHeight = function () {
+ Backdrop.unfreezeHeight();
+ $('').css({
+ position: 'absolute',
+ top: '0px',
+ left: '0px',
+ width: '1px',
+ height: $('body').css('height')
+ }).appendTo('body');
+};
+
+/**
+ * Unfreeze the body height.
+ */
+Backdrop.unfreezeHeight = function () {
+ $('#freeze-height').remove();
+};
+
+/**
+ * Encodes a Backdrop path for use in a URL.
+ *
+ * For aesthetic reasons slashes are not escaped.
+ */
+Backdrop.encodePath = function (item) {
+ return window.encodeURIComponent(item).replace(/%2F/g, '/');
+};
+
+/**
+ * Get the text selection in a textarea.
+ */
+Backdrop.getSelection = function (element) {
+ var range1, range2, start, end;
+ if (typeof element.selectionStart != 'number' && document.selection) {
+ // The current selection.
+ range1 = document.selection.createRange();
+ range2 = range1.duplicate();
+ // Select all text.
+ range2.moveToElementText(element);
+ // Now move 'dummy' end point to end point of original range.
+ range2.setEndPoint('EndToEnd', range1);
+ // Now we can calculate start and end points.
+ start = range2.text.length - range1.text.length;
+ end = start + range1.text.length;
+ return { 'start': start, 'end': end };
+ }
+ return { 'start': element.selectionStart, 'end': element.selectionEnd };
+};
+
+/**
+ * Build an error message from an Ajax response.
+ */
+Backdrop.ajaxError = function (xmlhttp, uri) {
+ var statusCode, statusText, pathText, responseText, readyStateText, message;
+ if (xmlhttp.status) {
+ statusCode = "\n" + Backdrop.t("An AJAX HTTP error occurred.") + "\n" + Backdrop.t("HTTP Result Code: !status", {'!status': xmlhttp.status});
+ }
+ else {
+ statusCode = "\n" + Backdrop.t("An AJAX HTTP request terminated abnormally.");
+ }
+ statusCode += "\n" + Backdrop.t("Debugging information follows.");
+ pathText = "\n" + Backdrop.t("Path: !uri", {'!uri': uri} );
+ statusText = '';
+ // In some cases, when statusCode == 0, xmlhttp.statusText may not be defined.
+ // Unfortunately, testing for it with typeof, etc, doesn't seem to catch that
+ // and the test causes an exception. So we need to catch the exception here.
+ try {
+ statusText = "\n" + Backdrop.t("StatusText: !statusText", {'!statusText': $.trim(xmlhttp.statusText)});
+ }
+ catch (e) {}
+
+ responseText = '';
+ // Again, we don't have a way to know for sure whether accessing
+ // xmlhttp.responseText is going to throw an exception. So we'll catch it.
+ try {
+ responseText = "\n" + Backdrop.t("ResponseText: !responseText", {'!responseText': $.trim(xmlhttp.responseText) } );
+ } catch (e) {}
+
+ // Make the responseText more readable by stripping HTML tags and newlines.
+ responseText = responseText.replace(/<("[^"]*"|'[^']*'|[^'">])*>/gi,"");
+ responseText = responseText.replace(/[\n]+\s+/g,"\n");
+
+ // We don't need readyState except for status == 0.
+ readyStateText = xmlhttp.status == 0 ? ("\n" + Backdrop.t("ReadyState: !readyState", {'!readyState': xmlhttp.readyState})) : "";
+
+ message = statusCode + pathText + statusText + responseText + readyStateText;
+ return message;
+};
+
+// Class indicating that JS is enabled; used for styling purpose.
+$('html').addClass('js');
+
+/**
+ * Additions to jQuery.support.
+ */
+$(function () {
+ /**
+ * Boolean indicating whether or not position:fixed is supported.
+ */
+ if (jQuery.support.positionFixed === undefined) {
+ var el = $('').appendTo(document.body);
+ jQuery.support.positionFixed = el[0].offsetTop === 10;
+ el.remove();
+ }
+});
+
+// On page ready, attach behaviors in which all other Backdrop JS is handled.
+$(function () {
+ Backdrop.attachBehaviors(document, Backdrop.settings);
+});
+
+/**
+ * The default themes.
+ */
+Backdrop.theme.prototype = {
+
+ /**
+ * Formats text for emphasized display in a placeholder inside a sentence.
+ *
+ * @param str
+ * The text to format (plain-text).
+ * @return
+ * The formatted text (html).
+ */
+ placeholder: function (str) {
+ return '' + Backdrop.checkPlain(str) + '';
+ }
+};
+
+})(jQuery);
diff --git a/core/misc/batch.js b/core/misc/batch.js
new file mode 100755
index 0000000..5a0dafc
--- /dev/null
+++ b/core/misc/batch.js
@@ -0,0 +1,37 @@
+(function ($) {
+
+/**
+ * Attaches the batch behavior to progress bars.
+ */
+Backdrop.behaviors.batch = {
+ attach: function (context, settings) {
+ var batch = settings.batch;
+ var $progress = $('#progress').once('batch');
+ var progressBar;
+
+ // Success: redirect to the summary.
+ function updateCallback(progress, status, pb) {
+ if (progress === '100') {
+ pb.stopMonitoring();
+ window.location = batch.uri + '&op=finished';
+ }
+ }
+
+ function errorCallback(pb) {
+ $progress.prepend($('').html(batch.errorMessage));
+ $('#wait').hide();
+ }
+
+ if ($progress.length) {
+ progressBar = new Backdrop.progressBar('updateprogress', updateCallback, 'POST', errorCallback);
+ progressBar.setProgress(-1, batch.initMessage);
+ progressBar.startMonitoring(batch.uri + '&op=do', 10);
+ // Remove HTML from no-js progress bar.
+ $progress.empty();
+ // Append the JS progressbar element.
+ $progress.append(progressBar.element);
+ }
+ }
+};
+
+})(jQuery);
diff --git a/core/misc/collapse.js b/core/misc/collapse.js
old mode 100644
new mode 100755
index 512ff2f..baf555d
--- a/core/misc/collapse.js
+++ b/core/misc/collapse.js
@@ -3,33 +3,35 @@
/**
* Toggle the visibility of a fieldset using smooth animations.
*/
-Drupal.toggleFieldset = function (fieldset) {
+Backdrop.toggleFieldset = function (fieldset) {
var $fieldset = $(fieldset);
if ($fieldset.is('.collapsed')) {
var $content = $('> .fieldset-wrapper', fieldset).hide();
$fieldset
.removeClass('collapsed')
- .trigger({ type: 'collapsed', value: false })
- .find('> legend span.fieldset-legend-prefix').html(Drupal.t('Hide'));
+ .find('> legend span.fieldset-legend-prefix').html(Backdrop.t('Hide'));
$content.slideDown({
duration: 'fast',
easing: 'linear',
complete: function () {
- Drupal.collapseScrollIntoView(fieldset);
+ Backdrop.collapseScrollIntoView(fieldset);
+ $fieldset.trigger({ type: 'collapsed', value: false });
+ $(window).triggerHandler('resize');
fieldset.animating = false;
},
step: function () {
// Scroll the fieldset into view.
- Drupal.collapseScrollIntoView(fieldset);
+ Backdrop.collapseScrollIntoView(fieldset);
}
});
}
else {
- $fieldset.trigger({ type: 'collapsed', value: true });
$('> .fieldset-wrapper', fieldset).slideUp('fast', function () {
$fieldset
.addClass('collapsed')
- .find('> legend span.fieldset-legend-prefix').html(Drupal.t('Show'));
+ .find('> legend span.fieldset-legend-prefix').html(Backdrop.t('Show'));
+ $fieldset.trigger({ type: 'collapsed', value: true });
+ $(window).triggerHandler('resize');
fieldset.animating = false;
});
}
@@ -38,7 +40,7 @@ Drupal.toggleFieldset = function (fieldset) {
/**
* Scroll a given fieldset into view as much as possible.
*/
-Drupal.collapseScrollIntoView = function (node) {
+Backdrop.collapseScrollIntoView = function (node) {
var h = document.documentElement.clientHeight || document.body.clientHeight || 0;
var offset = document.documentElement.scrollTop || document.body.scrollTop || 0;
var posY = $(node).offset().top;
@@ -53,13 +55,14 @@ Drupal.collapseScrollIntoView = function (node) {
}
};
-Drupal.behaviors.collapse = {
+Backdrop.behaviors.collapse = {
attach: function (context, settings) {
+ var hasHash = location.hash && location.hash != '#' && $(window).find(location.hash).length;
$('fieldset.collapsible', context).once('collapse', function () {
var $fieldset = $(this);
// Expand fieldset if there are errors inside, or if it contains an
// element that is targeted by the URI fragment identifier.
- var anchor = location.hash && location.hash != '#' ? ', ' + location.hash : '';
+ var anchor = hasHash ? ', ' + location.hash : '';
if ($fieldset.find('.error' + anchor).length) {
$fieldset.removeClass('collapsed');
}
@@ -67,7 +70,7 @@ Drupal.behaviors.collapse = {
var summary = $('');
$fieldset.
bind('summaryUpdated', function () {
- var text = $.trim($fieldset.drupalGetSummary());
+ var text = $.trim($fieldset.backdropGetSummary());
summary.html(text ? ' (' + text + ')' : '');
})
.trigger('summaryUpdated');
@@ -77,9 +80,9 @@ Drupal.behaviors.collapse = {
var $legend = $('> legend .fieldset-legend', this);
$('')
- .append($fieldset.hasClass('collapsed') ? Drupal.t('Show') : Drupal.t('Hide'))
+ .append($fieldset.hasClass('collapsed') ? Backdrop.t('Show') : Backdrop.t('Hide'))
.prependTo($legend)
- .after(' ');
+ .after(document.createTextNode(' '));
// .wrapInner() does not retain bound events.
var $link = $('')
@@ -90,7 +93,7 @@ Drupal.behaviors.collapse = {
// Don't animate multiple times.
if (!fieldset.animating) {
fieldset.animating = true;
- Drupal.toggleFieldset(fieldset);
+ Backdrop.toggleFieldset(fieldset);
}
return false;
});
diff --git a/core/misc/configure.png b/core/misc/configure.png
old mode 100644
new mode 100755
diff --git a/core/misc/dialog.ajax.js b/core/misc/dialog.ajax.js
new file mode 100755
index 0000000..71287c3
--- /dev/null
+++ b/core/misc/dialog.ajax.js
@@ -0,0 +1,163 @@
+/**
+ * @file
+ * Extends the Backdrop AJAX functionality to integrate the dialog API.
+ */
+
+(function ($) {
+
+"use strict";
+
+Backdrop.behaviors.dialog = {
+ attach: function (context) {
+ // Provide a known 'backdrop-modal' DOM element for Backdrop-based modal
+ // dialogs. Non-modal dialogs are responsible for creating their own
+ // elements, since there can be multiple non-modal dialogs at a time.
+ if (!$('#backdrop-modal').length) {
+ $('').hide().appendTo('body');
+ }
+
+ // Special behaviors specific when attaching content within a dialog.
+ // These behaviors usually fire after a validation error inside a dialog.
+ var $dialog = $(context).closest('.ui-dialog-content');
+ if ($dialog.length) {
+ // Remove and replace the dialog buttons with those from the new form.
+ if ($dialog.dialog('option', 'backdropAutoButtons')) {
+ // Trigger an event to detect/sync changes to buttons.
+ $dialog.trigger('dialogButtonsChange');
+ }
+
+ // Force focus on the modal when the behavior is run.
+ $dialog.dialog('widget').trigger('focus');
+ }
+ },
+
+ /**
+ * Scan a dialog for any primary buttons and move them to the button area.
+ *
+ * @param $dialog
+ * An jQuery object containing the element that is the dialog target.
+ * @return
+ * An array of buttons that need to be added to the button area.
+ */
+ prepareDialogButtons: function ($dialog) {
+ var buttons = [];
+ var $buttons = $dialog.find('.form-actions input[type=submit]');
+ $buttons.each(function () {
+ // Hidden form buttons need special attention. For browser consistency,
+ // the button needs to be "visible" in order to have the enter key fire
+ // the form submit event. So instead of a simple "hide" or
+ // "display: none", we set its dimensions to zero.
+ // See http://mattsnider.com/how-forms-submit-when-pressing-enter/
+ var $originalButton = $(this).css({
+ width: 0,
+ height: 0,
+ padding: 0,
+ border: 0
+ });
+ buttons.push({
+ 'text': $originalButton.html() || $originalButton.attr('value'),
+ 'class': $originalButton.attr('class'),
+ 'click': function (e) {
+ $originalButton.trigger('mousedown').trigger('click').trigger('mouseup');
+ e.preventDefault();
+ }
+ });
+ });
+ return buttons;
+ }
+};
+
+/**
+ * Command to open a dialog.
+ */
+Backdrop.ajax.prototype.commands.openDialog = function (ajax, response, status) {
+ if (!response.selector) {
+ return false;
+ }
+ var $dialog = $(response.selector);
+ if (!$dialog.length) {
+ // Create the element if needed.
+ $dialog = $('').appendTo('body');
+ }
+ // Set up the wrapper, if there isn't one.
+ if (!ajax.wrapper) {
+ ajax.wrapper = $dialog.attr('id');
+ }
+
+ // Use the ajax.js insert command to populate the dialog contents.
+ response.command = 'insert';
+ response.method = 'html';
+ ajax.commands.insert(ajax, response, status);
+
+ // Move the buttons to the jQuery UI dialog buttons area.
+ if (!response.dialogOptions.buttons) {
+ response.dialogOptions.backdropAutoButtons = true;
+ response.dialogOptions.buttons = Backdrop.behaviors.dialog.prepareDialogButtons($dialog);
+ }
+
+ // Bind dialogButtonsChange
+ $dialog.on('dialogButtonsChange', function () {
+ var buttons = Backdrop.behaviors.dialog.prepareDialogButtons($dialog);
+ $dialog.dialog('option', 'buttons', buttons);
+ });
+
+ // Open the dialog itself.
+ response.dialogOptions = response.dialogOptions || {};
+ var dialog = Backdrop.dialog($dialog, response.dialogOptions);
+ if (response.dialogOptions.modal) {
+ dialog.showModal();
+ }
+ else {
+ dialog.show();
+ }
+
+ // Add the standard Backdrop class for buttons for style consistency.
+ $dialog.parent().find('.ui-dialog-buttonset').addClass('form-actions');
+};
+
+/**
+ * Command to close a dialog.
+ *
+ * If no selector is given, it defaults to trying to close the modal.
+ */
+Backdrop.ajax.prototype.commands.closeDialog = function (ajax, response, status) {
+ var $dialog = $(response.selector);
+ if ($dialog.length) {
+ Backdrop.dialog($dialog).close();
+ }
+
+ // Unbind dialogButtonsChange
+ $dialog.off('dialogButtonsChange');
+};
+
+/**
+ * Command to set a dialog property.
+ *
+ * jQuery UI specific way of setting dialog options.
+ */
+Backdrop.ajax.prototype.commands.setDialogOption = function (ajax, response, status) {
+ var $dialog = $(response.selector);
+ if ($dialog.length) {
+ $dialog.dialog('option', response.optionName, response.optionValue);
+ }
+};
+
+/**
+ * Binds a listener on dialog creation to handle the cancel link.
+ */
+$(window).on('dialog:aftercreate', function (e, dialog, $element, settings) {
+ $element.on('click.dialog', '.dialog-cancel', function (e) {
+ dialog.close('cancel');
+ e.preventDefault();
+ e.stopPropagation();
+ });
+});
+
+/**
+ * Removes all 'dialog' listeners.
+ */
+$(window).on('dialog:beforeclose', function (e, dialog, $element) {
+ $element.off('.dialog');
+});
+
+})(jQuery);
\ No newline at end of file
diff --git a/core/misc/dialog.js b/core/misc/dialog.js
old mode 100644
new mode 100755
index 5333b26..efddf6b
--- a/core/misc/dialog.js
+++ b/core/misc/dialog.js
@@ -8,7 +8,7 @@
"use strict";
-Drupal.settings.dialog = {
+Backdrop.settings.dialog = {
// This option will turn off resizable and draggable.
autoResize: true,
// jQuery UI does not support percentage heights, but we convert this property
@@ -17,23 +17,26 @@ Drupal.settings.dialog = {
// jQuery UI defaults to 300px, use 100% width to allow for responsive
// dialogs. CSS can override by specifying a max-width.
width: '100%',
- position: 'center',
+ position: { my: "center", at: "center", of: window },
close: function (e) {
- Drupal.detachBehaviors(e.target, null, 'unload');
+ Backdrop.detachBehaviors(e.target, null, 'unload');
}
};
-Drupal.dialog = function (element, options) {
+Backdrop.dialog = function (element, options) {
function openDialog (settings) {
- settings = $.extend({}, Drupal.settings.dialog, options, settings);
+ settings = $.extend({}, Backdrop.settings.dialog, options, settings);
// Trigger a global event to allow scripts to bind events to the dialog.
$(window).trigger('dialog:beforecreate', [dialog, $element, settings]);
$element.dialog(settings);
if (settings.autoResize === true || settings.autoResize === 'true') {
$element
- .dialog('option', { resizable: false, draggable: false })
+ .dialog('option', {
+ resizable: false,
+ draggable: false
+ })
.dialog('widget').css('position', 'fixed');
$(window)
.on('resize.dialogResize scroll.dialogResize', settings, autoResizeCallback)
@@ -58,7 +61,7 @@ Drupal.dialog = function (element, options) {
* This is used as a window resize and scroll callback to reposition the
* jQuery UI dialog. Although not a built-in jQuery UI option, this can
* be disabled by setting autoResize: false in the options array when creating
- * a new Drupal.dialog().
+ * a new Backdrop.dialog().
*/
function resetPosition (event) {
var positionOptions = ['width', 'height', 'minWidth', 'minHeight', 'maxHeight', 'maxWidth', 'position'];
diff --git a/core/misc/dialog.theme.css b/core/misc/dialog.theme.css
old mode 100644
new mode 100755
index 8a46dc1..a828847
--- a/core/misc/dialog.theme.css
+++ b/core/misc/dialog.theme.css
@@ -37,43 +37,7 @@
}
.ui-dialog .ui-dialog-buttonpane .ui-button-text-only .ui-button-text {
padding: 0;
-}
-
-.ui-dialog .ui-dialog-buttonpane button {
- background: #fefefe;
- background-image: -webkit-linear-gradient(top, #fefefe, #e0e0e0);
- background-image: -moz-linear-gradient(top, #fefefe, #e0e0e0);
- background-image: -o-linear-gradient(top, #fefefe, #e0e0e0);
- background-image: linear-gradient(to bottom, #fefefe, #e0e0e0);
- border: 1px solid #c8c8c8;
- border-radius: 3px;
- text-decoration: none;
- padding: 6px 17px 6px 17px;
-}
-.ui-dialog .ui-dialog-buttonpane button:hover,
-.ui-dialog .ui-dialog-buttonpane button:focus {
- background: #fefefe;
- background-image: -webkit-linear-gradient(top, #fefefe, #eaeaea);
- background-image: -moz-linear-gradient(top, #fefefe, #eaeaea);
- background-image: -o-linear-gradient(top, #fefefe, #eaeaea);
- background-image: linear-gradient(to bottom, #fefefe, #eaeaea);
- -webkit-box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
- box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
- color: #2e2e2e;
- text-decoration: none;
-}
-.ui-dialog .ui-dialog-buttonpane button:active {
- border: 1px solid #c8c8c8;
- background: #fefefe;
- background-image: -webkit-linear-gradient(top, #eaeaea, #fefefe);
- background-image: -moz-linear-gradient(top, #eaeaea, #fefefe);
- background-image: -o-linear-gradient(top, #eaeaea, #fefefe);
- background-image: linear-gradient(to bottom, #eaeaea, #fefefe);
- -webkit-box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
- box-shadow: 1px 1px 3px rgba(50, 50, 50, 0.1);
- color: #2e2e2e;
- text-decoration: none;
- text-shadow: none;
+ line-height: inherit;
}
/* Form action buttons are moved in dialogs. Remove empty space. */
diff --git a/core/misc/draggable.png b/core/misc/draggable.png
old mode 100644
new mode 100755
diff --git a/core/misc/dropbutton.css b/core/misc/dropbutton.css
old mode 100644
new mode 100755
index c7ef328..b0c2bc9
--- a/core/misc/dropbutton.css
+++ b/core/misc/dropbutton.css
@@ -1,4 +1,3 @@
-
/**
* @file
* Base styles for dropbuttons.
@@ -9,36 +8,29 @@
*/
.dropbutton-wrapper,
.dropbutton-wrapper div {
- -moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.js .dropbutton-wrapper {
- display: block;
min-height: 2em;
position: relative;
+ width: auto;
}
-.js .dropbutton-wrapper,
-.js .dropbutton-widget {
- max-width: 100%;
-}
+
@media screen and (max-width:600px) {
.js .dropbutton-wrapper {
width: 100%;
}
}
-/* Splitbuttons */
-.form-actions .dropbutton-wrapper {
- float: left;
-}
-.js .form-actions .dropbutton-widget {
- position: static;
-}
-
-
.js .dropbutton-widget {
position: absolute;
+ top: 0;
+ right: 0; /* LTR */
+}
+[dir="rtl"].js .dropbutton-widget {
+ left: 0;
+ right: auto;
}
/* UL styles are over-scoped in core, so this selector needs weight parity. */
.js .dropbutton-widget .dropbutton {
@@ -51,6 +43,11 @@
.js .dropbutton li,
.js .dropbutton a {
display: block;
+ text-align: right; /* LTR */
+}
+[dir="rtl"].js .dropbutton li,
+[dir="rtl"].js .dropbutton a {
+ text-align: left;
}
/**
@@ -63,10 +60,15 @@
* The arrow is created using border on a zero-width, zero-height span.
* The arrow inherits the link color, but can be overridden with border colors.
*/
+.js .dropbutton-widget {
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
.js .dropbutton-multiple .dropbutton-widget {
padding-right: 2em; /* LTR */
}
-.js[dir="rtl"] .dropbutton-multiple .dropbutton-widget {
+[dir="rtl"].js .dropbutton-multiple .dropbutton-widget {
padding-left: 2em;
padding-right: 0;
}
@@ -77,6 +79,40 @@
.dropbutton-multiple.open {
z-index: 100;
}
+.dropbutton a {
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+.dropbutton-multiple .dropbutton a {
+ /* LTR */
+ -webkit-border-top-left-radius: 5px;
+ -webkit-border-bottom-left-radius: 5px;
+ -moz-border-radius-topleft: 5px;
+ -moz-border-radius-bottomleft: 5px;
+ border-top-left-radius: 5px;
+ border-bottom-left-radius: 5px;
+ -webkit-border-top-right-radius: 0;
+ -webkit-border-bottom-right-radius: 0;
+ -moz-border-radius-topright: 0;
+ -moz-border-radius-bottomright: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+[dir="rtl"] .dropbutton-multiple .dropbutton a {
+ -webkit-border-top-left-radius: 0;
+ -webkit-border-bottom-left-radius: 0;
+ -moz-border-radius-topleft: 0;
+ -moz-border-radius-bottomleft: 0;
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -webkit-border-top-right-radius:5px;
+ -webkit-border-bottom-right-radius: 5px;
+ -moz-border-radius-topright: 5px;
+ -moz-border-radius-bottomright: 5px;
+ border-top-right-radius: 5px;
+ border-bottom-right-radius: 5px;
+}
.dropbutton-multiple .dropbutton .secondary-action {
display: none;
}
@@ -98,6 +134,13 @@
right: auto;
}
.dropbutton-toggle button {
+ /* LTR */
+ -webkit-border-top-right-radius: 5px;
+ -webkit-border-bottom-right-radius: 5px;
+ -moz-border-radius-topright: 5px;
+ -moz-border-radius-bottomright: 5px;
+ border-top-right-radius: 5px;
+ border-bottom-right-radius: 5px;
background: none;
border: 0;
cursor: pointer;
@@ -107,18 +150,31 @@
padding: 0;
width: 100%;
}
+[dir="rtl"] .dropbutton-toggle button {
+ -webkit-border-top-right-radius: 0;
+ -webkit-border-bottom-right-radius: 0;
+ -moz-border-radius-topright: 0;
+ -moz-border-radius-bottomright: 0;
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ -webkit-border-top-left-radius: 5px;
+ -webkit-border-bottom-left-radius: 5px;
+ -moz-border-radius-topleft: 5px;
+ -moz-border-radius-bottomleft: 5px;
+ border-top-left-radius: 5px;
+ border-bottom-left-radius: 5px;
+}
.dropbutton-arrow {
- border-color: #0D68A6;
border-bottom-color: transparent;
border-left-color: transparent;
border-right-color: transparent;
border-style: solid;
- border-width: 0.45em 0.45em 0;
+ border-width: 0.3333em 0.3333em 0;
display: block;
height: 0;
line-height: 0;
position: absolute;
- right: 35%; /* 0.6667em; */ /* LTR */
+ right: 40%; /* 0.6667em; */ /* LTR */
top: 50%;
margin-top: -0.1666em;
width: 0;
diff --git a/core/misc/dropbutton.js b/core/misc/dropbutton.js
old mode 100644
new mode 100755
index 199cdd2..d05472b
--- a/core/misc/dropbutton.js
+++ b/core/misc/dropbutton.js
@@ -1,11 +1,11 @@
-(function ($, Drupal) {
+(function ($) {
"use strict";
/**
* Process elements with the .dropbutton class on page load.
*/
-Drupal.behaviors.dropButton = {
+Backdrop.behaviors.dropButton = {
attach: function (context, settings) {
var $dropbuttons = $(context).find('.dropbutton-wrapper').once('dropbutton');
if ($dropbuttons.length) {
@@ -46,7 +46,7 @@ function dropbuttonClickHandler (e) {
*/
function DropButton (dropbutton, settings) {
// Merge defaults with settings.
- var options = $.extend({'title': Drupal.t('List additional actions')}, settings);
+ var options = $.extend({'title': Backdrop.t('List additional actions')}, settings);
var $dropbutton = $(dropbutton);
this.$dropbutton = $dropbutton;
this.$list = $dropbutton.find('.dropbutton');
@@ -61,7 +61,7 @@ function DropButton (dropbutton, settings) {
var $secondary = this.$actions.slice(1);
$secondary.addClass('secondary-action');
// Add toggle link.
- $primary.after(Drupal.theme('dropbuttonToggle', options));
+ $primary.after(Backdrop.theme('dropbuttonToggle', options));
// Bind mouse events.
this.$dropbutton
.addClass('dropbutton-multiple')
@@ -151,11 +151,11 @@ $.extend(DropButton.prototype, {
* @return {String}
* A string representing a DOM fragment.
*/
-Drupal.theme.prototype.dropbuttonToggle = function (options) {
+Backdrop.theme.prototype.dropbuttonToggle = function (options) {
return '';
}
// Expose constructor in the public space.
-Drupal.DropButton = DropButton;
+Backdrop.DropButton = DropButton;
-})(jQuery, Drupal);
+})(jQuery);
diff --git a/core/misc/dropbutton.theme.css b/core/misc/dropbutton.theme.css
old mode 100644
new mode 100755
index f3801d0..97fd178
--- a/core/misc/dropbutton.theme.css
+++ b/core/misc/dropbutton.theme.css
@@ -4,38 +4,30 @@
*/
.js .dropbutton-widget {
- background-color: #fff;
- border: 1px solid #E9E8E8;
- border-radius: 5px;
- text-transform: uppercase;
-}
-.js .dropbutton-multiple.open .dropbutton-widget {
- background-color: #fff;
- border: 1px solid #0d68a6;
+ background-color: white;
+ border: 1px solid #cccccc;
}
.js .dropbutton-widget:hover {
- border-color: #e3e2e2;
+ border-color: #b8b8b8;
}
.dropbutton .dropbutton-action > * {
- padding: 0.25em 1em;
+ padding: 0.1em 0.5em;
white-space: nowrap;
}
-.dropbutton a:hover,
-.dropbutton-toggle button:hover {
- background-color: #F7F7F7;
- text-decoration: none;
-}
.dropbutton .secondary-action {
- border-top: 1px solid #E9E8E8;
+ border-top: 1px solid #e8e8e8;
}
.dropbutton-multiple .dropbutton {
- border-right: 1px solid #E9E8E8; /* LTR */
+ border-right: 1px solid #e8e8e8; /* LTR */
}
[dir="rtl"] .dropbutton-multiple .dropbutton {
- border-left: 1px solid #E9E8E8;
+ border-left: 1px solid #e8e8e8;
border-right: 0 none;
}
-[dir="rtl"] .dropbutton-multiple .dropbutton .dropbutton-action > * {
- margin-left: 0.25em;
- margin-right: 0;
+
+.js .dropbutton button:hover,
+.js .dropbutton a:hover,
+.js .dropbutton button:focus,
+.js .dropbutton a:focus {
+ background-color: #f7f7f7;
}
diff --git a/core/misc/farbtastic/farbtastic.css b/core/misc/farbtastic/farbtastic.css
new file mode 100755
index 0000000..25a68eb
--- /dev/null
+++ b/core/misc/farbtastic/farbtastic.css
@@ -0,0 +1,36 @@
+
+.farbtastic {
+ position: relative;
+}
+.farbtastic * {
+ position: absolute;
+ cursor: crosshair;
+}
+.farbtastic,
+.farbtastic .wheel {
+ width: 195px;
+ height: 195px;
+}
+.farbtastic .color,
+.farbtastic .overlay {
+ top: 47px;
+ left: 47px;
+ width: 101px;
+ height: 101px;
+}
+.farbtastic .wheel {
+ background: url(wheel.png) no-repeat;
+ width: 195px;
+ height: 195px;
+}
+.farbtastic .overlay {
+ background: url(mask.png) no-repeat;
+}
+.farbtastic .marker {
+ width: 17px;
+ height: 17px;
+ margin: -8px 0 0 -8px;
+ overflow: hidden;
+ background: url(marker.png) no-repeat;
+}
+
diff --git a/core/misc/farbtastic/farbtastic.js b/core/misc/farbtastic/farbtastic.js
new file mode 100755
index 0000000..10c9e76
--- /dev/null
+++ b/core/misc/farbtastic/farbtastic.js
@@ -0,0 +1,8 @@
+(function(e){e.fn.farbtastic=function(f){e.farbtastic(this,f);return this};e.farbtastic=function(f,l){f=e(f).get(0);return f.farbtastic||(f.farbtastic=new e._farbtastic(f,l))};e._farbtastic=function(f,l){var a=this;e(f).html('
');var k=e(".farbtastic",f);a.wheel=e(".wheel",f).get(0);a.radius=84;a.square=100;a.width=194;navigator.appVersion.match(/MSIE [0-6]\./)&&
+e("*",k).each(function(){if(this.currentStyle.backgroundImage!="none"){var b=this.currentStyle.backgroundImage;b=this.currentStyle.backgroundImage.substring(5,b.length-2);e(this).css({backgroundImage:"none",filter:"progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='"+b+"')"})}});a.linkTo=function(b){typeof a.callback=="object"&&e(a.callback).unbind("keyup",a.updateValue);a.color=null;if(typeof b=="function")a.callback=b;else if(typeof b=="object"||typeof b=="string"){a.callback=
+e(b);a.callback.bind("keyup",a.updateValue);a.callback.get(0).value&&a.setColor(a.callback.get(0).value)}return this};a.updateValue=function(){this.value&&this.value!=a.color&&a.setColor(this.value)};a.setColor=function(b){var c=a.unpack(b);if(a.color!=b&&c){a.color=b;a.rgb=c;a.hsl=a.RGBToHSL(a.rgb);a.updateDisplay()}return this};a.setHSL=function(b){a.hsl=b;a.rgb=a.HSLToRGB(b);a.color=a.pack(a.rgb);a.updateDisplay();return this};a.widgetCoords=function(b){var c=e(a.wheel).offset();return{x:b.pageX-
+c.left-a.width/2,y:b.pageY-c.top-a.width/2}};a.mousedown=function(b){if(!document.dragging){e(document).bind("mousemove",a.mousemove).bind("mouseup",a.mouseup);document.dragging=true}var c=a.widgetCoords(b);a.circleDrag=Math.max(Math.abs(c.x),Math.abs(c.y))*2>a.square;a.mousemove(b);return false};a.mousemove=function(b){var c=a.widgetCoords(b);if(a.circleDrag){b=Math.atan2(c.x,-c.y)/6.28;if(b<0)b+=1;a.setHSL([b,a.hsl[1],a.hsl[2]])}else{b=Math.max(0,Math.min(1,-(c.x/a.square)+0.5));c=Math.max(0,Math.min(1,
+-(c.y/a.square)+0.5));a.setHSL([a.hsl[0],b,c])}return false};a.mouseup=function(){e(document).unbind("mousemove",a.mousemove);e(document).unbind("mouseup",a.mouseup);document.dragging=false};a.updateDisplay=function(){var b=a.hsl[0]*6.28;e(".h-marker",k).css({left:Math.round(Math.sin(b)*a.radius+a.width/2)+"px",top:Math.round(-Math.cos(b)*a.radius+a.width/2)+"px"});e(".sl-marker",k).css({left:Math.round(a.square*(0.5-a.hsl[1])+a.width/2)+"px",top:Math.round(a.square*(0.5-a.hsl[2])+a.width/2)+"px"});
+e(".color",k).css("backgroundColor",a.pack(a.HSLToRGB([a.hsl[0],1,0.5])));if(typeof a.callback=="object"){e(a.callback).css({backgroundColor:a.color,color:a.hsl[2]>0.5?"#000":"#fff"});e(a.callback).each(function(){if(this.value&&this.value!=a.color)this.value=a.color})}else typeof a.callback=="function"&&a.callback.call(a,a.color)};a.pack=function(b){var c=Math.round(b[0]*255),d=Math.round(b[1]*255);b=Math.round(b[2]*255);return"#"+(c<16?"0":"")+c.toString(16)+(d<16?"0":"")+d.toString(16)+(b<16?"0":
+"")+b.toString(16)};a.unpack=function(b){if(b.length==7)return[parseInt("0x"+b.substring(1,3))/255,parseInt("0x"+b.substring(3,5))/255,parseInt("0x"+b.substring(5,7))/255];else if(b.length==4)return[parseInt("0x"+b.substring(1,2))/15,parseInt("0x"+b.substring(2,3))/15,parseInt("0x"+b.substring(3,4))/15]};a.HSLToRGB=function(b){var c,d=b[0];c=b[1];b=b[2];c=b<=0.5?b*(c+1):b+c-b*c;b=b*2-c;return[this.hueToRGB(b,c,d+0.33333),this.hueToRGB(b,c,d),this.hueToRGB(b,c,d-0.33333)]};a.hueToRGB=function(b,c,
+d){d=d<0?d+1:d>1?d-1:d;if(d*6<1)return b+(c-b)*d*6;if(d*2<1)return c;if(d*3<2)return b+(c-b)*(0.66666-d)*6;return b};a.RGBToHSL=function(b){var c,d,m,g,h=b[0],i=b[1],j=b[2];c=Math.min(h,Math.min(i,j));b=Math.max(h,Math.max(i,j));d=b-c;g=(c+b)/2;m=0;if(g>0&&g<1)m=d/(g<0.5?2*g:2-2*g);c=0;if(d>0){if(b==h&&b!=i)c+=(i-j)/d;if(b==i&&b!=j)c+=2+(j-h)/d;if(b==j&&b!=h)c+=4+(h-i)/d;c/=6}return[c,m,g]};e("*",k).mousedown(a.mousedown);a.setColor("#000000");l&&a.linkTo(l)}})(jQuery);
diff --git a/core/misc/farbtastic/marker.png b/core/misc/farbtastic/marker.png
new file mode 100755
index 0000000..f9773d1
Binary files /dev/null and b/core/misc/farbtastic/marker.png differ
diff --git a/core/misc/farbtastic/mask.png b/core/misc/farbtastic/mask.png
new file mode 100755
index 0000000..0fc9cbe
Binary files /dev/null and b/core/misc/farbtastic/mask.png differ
diff --git a/core/misc/farbtastic/wheel.png b/core/misc/farbtastic/wheel.png
new file mode 100755
index 0000000..4a905e9
Binary files /dev/null and b/core/misc/farbtastic/wheel.png differ
diff --git a/core/misc/favicon.ico b/core/misc/favicon.ico
new file mode 100755
index 0000000..421e87a
Binary files /dev/null and b/core/misc/favicon.ico differ
diff --git a/core/misc/feed.png b/core/misc/feed.png
old mode 100644
new mode 100755
diff --git a/core/misc/form.js b/core/misc/form.js
old mode 100644
new mode 100755
index d959c6b..2d2b5c8
--- a/core/misc/form.js
+++ b/core/misc/form.js
@@ -3,7 +3,7 @@
/**
* Retrieves the summary for the first element.
*/
-$.fn.drupalGetSummary = function () {
+$.fn.backdropGetSummary = function () {
var callback = this.data('summaryCallback');
return (this[0] && callback) ? $.trim(callback(this[0])) : '';
};
@@ -15,7 +15,7 @@ $.fn.drupalGetSummary = function () {
* Either a function that will be called each time the summary is
* retrieved or a string (which is returned each time).
*/
-$.fn.drupalSetSummary = function (callback) {
+$.fn.backdropSetSummary = function (callback) {
var self = this;
// To facilitate things, the callback should always be a function. If it's
@@ -41,7 +41,7 @@ $.fn.drupalSetSummary = function (callback) {
/**
* Sends a 'formUpdated' event each time a form element is modified.
*/
-Drupal.behaviors.formUpdated = {
+Backdrop.behaviors.formUpdated = {
attach: function (context) {
// These events are namespaced so that we can remove them later.
var events = 'change.formUpdated click.formUpdated blur.formUpdated keyup.formUpdated';
@@ -60,13 +60,13 @@ Drupal.behaviors.formUpdated = {
/**
* Prepopulate form fields with information from the visitor cookie.
*/
-Drupal.behaviors.fillUserInfoFromCookie = {
+Backdrop.behaviors.fillUserInfoFromCookie = {
attach: function (context, settings) {
$('form.user-info-from-cookie').once('user-info-from-cookie', function () {
var formContext = this;
$.each(['name', 'mail', 'homepage'], function () {
var $element = $('[name=' + this + ']', formContext);
- var cookie = $.cookie('Drupal.visitor.' + this);
+ var cookie = $.cookie('Backdrop.visitor.' + this);
if ($element.length && cookie) {
$element.val(cookie);
}
diff --git a/core/misc/grippie.png b/core/misc/grippie.png
old mode 100644
new mode 100755
diff --git a/core/misc/help.png b/core/misc/help.png
old mode 100644
new mode 100755
diff --git a/core/misc/html5.js b/core/misc/html5.js
new file mode 100755
index 0000000..c2e7da6
--- /dev/null
+++ b/core/misc/html5.js
@@ -0,0 +1,4 @@
+/**
+* @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
+*/
+!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document);
diff --git a/core/misc/jquery.cookie.js b/core/misc/jquery.cookie.js
old mode 100644
new mode 100755
diff --git a/core/misc/jquery.form.js b/core/misc/jquery.form.js
old mode 100644
new mode 100755
diff --git a/core/misc/jquery.js b/core/misc/jquery.js
old mode 100644
new mode 100755
index 73f33fb..ab28a24
--- a/core/misc/jquery.js
+++ b/core/misc/jquery.js
@@ -1,4 +1,4 @@
-/*! jQuery v1.11.0 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
-!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k="".trim,l={},m="1.11.0",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(l.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:k&&!k.call("\ufeff\xa0")?function(a){return null==a?"":k.call(a)}:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||n.guid++,e):void 0},now:function(){return+new Date},support:l}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s="sizzle"+-new Date,t=a.document,u=0,v=0,w=eb(),x=eb(),y=eb(),z=function(a,b){return a===b&&(j=!0),0},A="undefined",B=1<<31,C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=D.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",M=L.replace("w","w#"),N="\\["+K+"*("+L+")"+K+"*(?:([*^$|!~]?=)"+K+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+M+")|)|)"+K+"*\\]",O=":("+L+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+N.replace(3,8)+")*)|.*)\\)|)",P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(O),U=new RegExp("^"+M+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L.replace("w","w*")+")"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=/'|\\/g,ab=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),bb=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{G.apply(D=H.call(t.childNodes),t.childNodes),D[t.childNodes.length].nodeType}catch(cb){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function db(a,b,d,e){var f,g,h,i,j,m,p,q,u,v;if((b?b.ownerDocument||b:t)!==l&&k(b),b=b||l,d=d||[],!a||"string"!=typeof a)return d;if(1!==(i=b.nodeType)&&9!==i)return[];if(n&&!e){if(f=Z.exec(a))if(h=f[1]){if(9===i){if(g=b.getElementById(h),!g||!g.parentNode)return d;if(g.id===h)return d.push(g),d}else if(b.ownerDocument&&(g=b.ownerDocument.getElementById(h))&&r(b,g)&&g.id===h)return d.push(g),d}else{if(f[2])return G.apply(d,b.getElementsByTagName(a)),d;if((h=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(h)),d}if(c.qsa&&(!o||!o.test(a))){if(q=p=s,u=b,v=9===i&&a,1===i&&"object"!==b.nodeName.toLowerCase()){m=ob(a),(p=b.getAttribute("id"))?q=p.replace(_,"\\$&"):b.setAttribute("id",q),q="[id='"+q+"'] ",j=m.length;while(j--)m[j]=q+pb(m[j]);u=$.test(a)&&mb(b.parentNode)||b,v=m.join(",")}if(v)try{return G.apply(d,u.querySelectorAll(v)),d}catch(w){}finally{p||b.removeAttribute("id")}}}return xb(a.replace(P,"$1"),b,d,e)}function eb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function fb(a){return a[s]=!0,a}function gb(a){var b=l.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function hb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function ib(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||B)-(~a.sourceIndex||B);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function jb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function kb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function lb(a){return fb(function(b){return b=+b,fb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function mb(a){return a&&typeof a.getElementsByTagName!==A&&a}c=db.support={},f=db.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},k=db.setDocument=function(a){var b,e=a?a.ownerDocument||a:t,g=e.defaultView;return e!==l&&9===e.nodeType&&e.documentElement?(l=e,m=e.documentElement,n=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){k()},!1):g.attachEvent&&g.attachEvent("onunload",function(){k()})),c.attributes=gb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=gb(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(e.getElementsByClassName)&&gb(function(a){return a.innerHTML="",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=gb(function(a){return m.appendChild(a).id=s,!e.getElementsByName||!e.getElementsByName(s).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==A&&n){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){var c=typeof a.getAttributeNode!==A&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==A?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==A&&n?b.getElementsByClassName(a):void 0},p=[],o=[],(c.qsa=Y.test(e.querySelectorAll))&&(gb(function(a){a.innerHTML="",a.querySelectorAll("[t^='']").length&&o.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||o.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll(":checked").length||o.push(":checked")}),gb(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&o.push("name"+K+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||o.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),o.push(",.*:")})),(c.matchesSelector=Y.test(q=m.webkitMatchesSelector||m.mozMatchesSelector||m.oMatchesSelector||m.msMatchesSelector))&&gb(function(a){c.disconnectedMatch=q.call(a,"div"),q.call(a,"[s!='']:x"),p.push("!=",O)}),o=o.length&&new RegExp(o.join("|")),p=p.length&&new RegExp(p.join("|")),b=Y.test(m.compareDocumentPosition),r=b||Y.test(m.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},z=b?function(a,b){if(a===b)return j=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===t&&r(t,a)?-1:b===e||b.ownerDocument===t&&r(t,b)?1:i?I.call(i,a)-I.call(i,b):0:4&d?-1:1)}:function(a,b){if(a===b)return j=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],k=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:i?I.call(i,a)-I.call(i,b):0;if(f===g)return ib(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)k.unshift(c);while(h[d]===k[d])d++;return d?ib(h[d],k[d]):h[d]===t?-1:k[d]===t?1:0},e):l},db.matches=function(a,b){return db(a,null,null,b)},db.matchesSelector=function(a,b){if((a.ownerDocument||a)!==l&&k(a),b=b.replace(S,"='$1']"),!(!c.matchesSelector||!n||p&&p.test(b)||o&&o.test(b)))try{var d=q.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return db(b,l,null,[a]).length>0},db.contains=function(a,b){return(a.ownerDocument||a)!==l&&k(a),r(a,b)},db.attr=function(a,b){(a.ownerDocument||a)!==l&&k(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!n):void 0;return void 0!==f?f:c.attributes||!n?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},db.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},db.uniqueSort=function(a){var b,d=[],e=0,f=0;if(j=!c.detectDuplicates,i=!c.sortStable&&a.slice(0),a.sort(z),j){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return i=null,a},e=db.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=db.selectors={cacheLength:50,createPseudo:fb,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ab,bb),a[3]=(a[4]||a[5]||"").replace(ab,bb),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||db.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&db.error(a[0]),a},PSEUDO:function(a){var b,c=!a[5]&&a[2];return V.CHILD.test(a[0])?null:(a[3]&&void 0!==a[4]?a[2]=a[4]:c&&T.test(c)&&(b=ob(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ab,bb).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=w[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&w(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==A&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=db.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),t=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&t){k=q[s]||(q[s]={}),j=k[a]||[],n=j[0]===u&&j[1],m=j[0]===u&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[u,n,m];break}}else if(t&&(j=(b[s]||(b[s]={}))[a])&&j[0]===u)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(t&&((l[s]||(l[s]={}))[a]=[u,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||db.error("unsupported pseudo: "+a);return e[s]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?fb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:fb(function(a){var b=[],c=[],d=g(a.replace(P,"$1"));return d[s]?fb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:fb(function(a){return function(b){return db(a,b).length>0}}),contains:fb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:fb(function(a){return U.test(a||"")||db.error("unsupported lang: "+a),a=a.replace(ab,bb).toLowerCase(),function(b){var c;do if(c=n?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===m},focus:function(a){return a===l.activeElement&&(!l.hasFocus||l.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:lb(function(){return[0]}),last:lb(function(a,b){return[b-1]}),eq:lb(function(a,b,c){return[0>c?c+b:c]}),even:lb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:lb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:lb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:lb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function qb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=v++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[u,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[s]||(b[s]={}),(h=i[d])&&h[0]===u&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function rb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function sb(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function tb(a,b,c,d,e,f){return d&&!d[s]&&(d=tb(d)),e&&!e[s]&&(e=tb(e,f)),fb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||wb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:sb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=sb(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=sb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ub(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],i=g||d.relative[" "],j=g?1:0,k=qb(function(a){return a===b},i,!0),l=qb(function(a){return I.call(b,a)>-1},i,!0),m=[function(a,c,d){return!g&&(d||c!==h)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>j;j++)if(c=d.relative[a[j].type])m=[qb(rb(m),c)];else{if(c=d.filter[a[j].type].apply(null,a[j].matches),c[s]){for(e=++j;f>e;e++)if(d.relative[a[e].type])break;return tb(j>1&&rb(m),j>1&&pb(a.slice(0,j-1).concat({value:" "===a[j-2].type?"*":""})).replace(P,"$1"),c,e>j&&ub(a.slice(j,e)),f>e&&ub(a=a.slice(e)),f>e&&pb(a))}m.push(c)}return rb(m)}function vb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,i,j,k){var m,n,o,p=0,q="0",r=f&&[],s=[],t=h,v=f||e&&d.find.TAG("*",k),w=u+=null==t?1:Math.random()||.1,x=v.length;for(k&&(h=g!==l&&g);q!==x&&null!=(m=v[q]);q++){if(e&&m){n=0;while(o=a[n++])if(o(m,g,i)){j.push(m);break}k&&(u=w)}c&&((m=!o&&m)&&p--,f&&r.push(m))}if(p+=q,c&&q!==p){n=0;while(o=b[n++])o(r,s,g,i);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=E.call(j));s=sb(s)}G.apply(j,s),k&&!f&&s.length>0&&p+b.length>1&&db.uniqueSort(j)}return k&&(u=w,h=t),r};return c?fb(f):f}g=db.compile=function(a,b){var c,d=[],e=[],f=y[a+" "];if(!f){b||(b=ob(a)),c=b.length;while(c--)f=ub(b[c]),f[s]?d.push(f):e.push(f);f=y(a,vb(e,d))}return f};function wb(a,b,c){for(var d=0,e=b.length;e>d;d++)db(a,b[d],c);return c}function xb(a,b,e,f){var h,i,j,k,l,m=ob(a);if(!f&&1===m.length){if(i=m[0]=m[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&c.getById&&9===b.nodeType&&n&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(ab,bb),b)||[])[0],!b)return e;a=a.slice(i.shift().value.length)}h=V.needsContext.test(a)?0:i.length;while(h--){if(j=i[h],d.relative[k=j.type])break;if((l=d.find[k])&&(f=l(j.matches[0].replace(ab,bb),$.test(i[0].type)&&mb(b.parentNode)||b))){if(i.splice(h,1),a=f.length&&pb(i),!a)return G.apply(e,f),e;break}}}return g(a,m)(f,b,!n,e,$.test(a)&&mb(b.parentNode)||b),e}return c.sortStable=s.split("").sort(z).join("")===s,c.detectDuplicates=!!j,k(),c.sortDetached=gb(function(a){return 1&a.compareDocumentPosition(l.createElement("div"))}),gb(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||hb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&gb(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||hb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),gb(function(a){return null==a.getAttribute("disabled")})||hb(J,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),db}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=a.document,A=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,B=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:A.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:z,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=z.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return y.find(a);this.length=1,this[0]=d}return this.context=z,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};B.prototype=n.fn,y=n(z);var C=/^(?:parents|prev(?:Until|All))/,D={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!n(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function E(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return E(a,"nextSibling")},prev:function(a){return E(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(D[a]||(e=n.unique(e)),C.test(a)&&(e=e.reverse())),this.pushStack(e)}});var F=/\S+/g,G={};function H(a){var b=G[a]={};return n.each(a.match(F)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?G[a]||H(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&n.each(arguments,function(a,c){var d;while((d=n.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){if(a===!0?!--n.readyWait:!n.isReady){if(!z.body)return setTimeout(n.ready);n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(z,[n]),n.fn.trigger&&n(z).trigger("ready").off("ready"))}}});function J(){z.addEventListener?(z.removeEventListener("DOMContentLoaded",K,!1),a.removeEventListener("load",K,!1)):(z.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(z.addEventListener||"load"===event.type||"complete"===z.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===z.readyState)setTimeout(n.ready);else if(z.addEventListener)z.addEventListener("DOMContentLoaded",K,!1),a.addEventListener("load",K,!1);else{z.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&z.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!n.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}J(),n.ready()}}()}return I.promise(b)};var L="undefined",M;for(M in n(l))break;l.ownLast="0"!==M,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c=z.getElementsByTagName("body")[0];c&&(a=z.createElement("div"),a.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",b=z.createElement("div"),c.appendChild(a).appendChild(b),typeof b.style.zoom!==L&&(b.style.cssText="border:0;margin:0;width:1px;padding:1px;display:inline;zoom:1",(l.inlineBlockNeedsLayout=3===b.offsetWidth)&&(c.style.zoom=1)),c.removeChild(a),a=b=null)}),function(){var a=z.createElement("div");if(null==l.deleteExpando){l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}}a=null}(),n.acceptData=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(n.acceptData(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f
-}}function S(a,b,c){if(n.acceptData(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d]));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},X=/^(?:checkbox|radio)$/i;!function(){var a=z.createDocumentFragment(),b=z.createElement("div"),c=z.createElement("input");if(b.setAttribute("className","t"),b.innerHTML="
").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m});
diff --git a/core/misc/jquery.once.js b/core/misc/jquery.once.js
old mode 100644
new mode 100755
diff --git a/core/misc/jquery.ui.touch-punch.js b/core/misc/jquery.ui.touch-punch.js
new file mode 100755
index 0000000..8c8ceb4
--- /dev/null
+++ b/core/misc/jquery.ui.touch-punch.js
@@ -0,0 +1,11 @@
+/*!
+ * jQuery UI Touch Punch 0.2.3
+ *
+ * Copyright 2011–2014, Dave Furfero
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ *
+ * Depends:
+ * jquery.ui.widget.js
+ * jquery.ui.mouse.js
+ */
+!function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery);
diff --git a/core/misc/loading-small.gif b/core/misc/loading-small.gif
old mode 100644
new mode 100755
diff --git a/core/misc/loading.gif b/core/misc/loading.gif
old mode 100644
new mode 100755
diff --git a/core/misc/machine-name.js b/core/misc/machine-name.js
old mode 100644
new mode 100755
index 4678e0b..9acc11c
--- a/core/misc/machine-name.js
+++ b/core/misc/machine-name.js
@@ -3,7 +3,7 @@
/**
* Attach the machine-readable name form element behavior.
*/
-Drupal.behaviors.machineName = {
+Backdrop.behaviors.machineName = {
/**
* Attaches the behavior.
*
@@ -26,80 +26,101 @@ Drupal.behaviors.machineName = {
*/
attach: function (context, settings) {
var self = this;
- $.each(settings.machineName, function (source_id, options) {
- var $source = $(source_id, context).addClass('machine-name-source');
- var $target = $(options.target, context).addClass('machine-name-target');
- var $suffix = $(options.suffix, context);
- var $wrapper = $target.closest('.form-item');
- // All elements have to exist.
- if (!$source.length || !$target.length || !$suffix.length || !$wrapper.length) {
- return;
- }
- // Skip processing upon a form validation error on the machine name.
- if ($target.hasClass('error')) {
- return;
- }
- // Figure out the maximum length for the machine name.
- options.maxlength = $target.attr('maxlength');
- // Hide the form item container of the machine name form element.
- $wrapper.hide();
- // Determine the initial machine name value. Unless the machine name form
- // element is disabled or not empty, the initial default value is based on
- // the human-readable form element value.
- if ($target.is(':disabled') || $target.val() != '') {
- var machine = $target.val();
- }
- else {
- var machine = self.transliterate($source.val(), options);
- }
- // Append the machine name preview to the source field.
- var $preview = $('' + options.field_prefix + Drupal.checkPlain(machine) + options.field_suffix + '');
- $suffix.empty();
- if (options.label) {
- $suffix.append(' ').append('' + options.label + ':');
- }
- $suffix.append(' ').append($preview);
+ var source_id, options, machine, eventData;
+ var $context = $(context);
- // If the machine name cannot be edited, stop further processing.
- if ($target.is(':disabled')) {
- return;
- }
+ function clickEditHandler(e) {
+ var data = e.data;
+ e.preventDefault();
+ data.$wrapper.show();
+ data.$target.focus();
+ data.$suffix.hide();
+ data.$source.unbind('.machineName');
+ }
+
+ function machineNameHandler(e) {
+ var data = e.data;
+ machine = self.transliterate($(e.target).val(), data.options);
+ // Set the machine name to the transliterated value.
+ if (machine !== '') {
+ if (machine !== data.options.replace) {
+ data.$target.val(machine);
+ data.$preview.html(data.options.field_prefix + Backdrop.checkPlain(machine) + data.options.field_suffix);
+ }
+ data.$suffix.show();
+ }
+ else {
+ data.$suffix.hide();
+ data.$target.val(machine);
+ data.$preview.empty();
+ }
+ }
+
+ for (source_id in settings.machineName) {
+ if (settings.machineName.hasOwnProperty(source_id)) {
+ options = settings.machineName[source_id];
+
+ var $source = $context.find(source_id).addClass('machine-name-source');
+ var $target = $context.find(options.target).addClass('machine-name-target');
+ var $suffix = $context.find(options.suffix);
+ var $wrapper = $target.closest('.form-item');
+ // All elements have to exist.
+ if (!$source.length || !$target.length || !$suffix.length || !$wrapper.length) {
+ return;
+ }
+ // Skip processing upon a form validation error on the machine name.
+ if ($target.hasClass('error')) {
+ return;
+ }
+ // Figure out the maximum length for the machine name.
+ options.maxlength = $target.attr('maxlength');
+ // Hide the form item container of the machine name form element.
+ $wrapper.hide();
+ // Determine the initial machine name value. Unless the machine name form
+ // element is disabled or not empty, the initial default value is based on
+ // the human-readable form element value.
+ if ($target.is(':disabled') || $target.val() !== '') {
+ machine = $target.val();
+ }
+ else {
+ machine = self.transliterate($source.val(), options);
+ }
+ // Append the machine name preview to the source field.
+ var $preview = $('' + options.field_prefix + Backdrop.checkPlain(machine) + options.field_suffix + '');
+ $suffix.empty();
+ if (options.label) {
+ $suffix.append(' ').append('' + options.label + ':');
+ }
+ $suffix.append(' ').append($preview);
+
+ // If the machine name cannot be edited, stop further processing.
+ if ($target.is(':disabled')) {
+ return;
+ }
+
+ eventData = {
+ $source: $source,
+ $target: $target,
+ $suffix: $suffix,
+ $wrapper: $wrapper,
+ $preview: $preview,
+ options: options
+ };
- // If it is editable, append an edit link.
- var $link = $('' + Drupal.t('Edit') + '')
- .click(function () {
- $wrapper.show();
- $target.focus();
- $suffix.hide();
- $source.unbind('.machineName');
- return false;
- });
- $suffix.append(' ').append($link);
+ // If it is editable, append an edit link.
+ var $link = $('' + Backdrop.t('Edit') + '').bind('click', eventData, clickEditHandler);
+ $suffix.append(' ').append($link);
- // Preview the machine name in realtime when the human-readable name
- // changes, but only if there is no machine name yet; i.e., only upon
- // initial creation, not when editing.
- if ($target.val() == '') {
- $source.bind('keyup.machineName change.machineName input.machineName', function () {
- machine = self.transliterate($(this).val(), options);
- // Set the machine name to the transliterated value.
- if (machine != '') {
- if (machine != options.replace) {
- $target.val(machine);
- $preview.html(options.field_prefix + Drupal.checkPlain(machine) + options.field_suffix);
- }
- $suffix.show();
- }
- else {
- $suffix.hide();
- $target.val(machine);
- $preview.empty();
- }
- });
- // Initialize machine name preview.
- $source.keyup();
+ // Preview the machine name in realtime when the human-readable name
+ // changes, but only if there is no machine name yet; i.e., only upon
+ // initial creation, not when editing.
+ if ($target.val() === '') {
+ $source.bind('keyup.machineName change.machineName', eventData, machineNameHandler)
+ // Initialize machine name preview.
+ .keyup();
+ }
}
- });
+ }
},
/**
diff --git a/core/misc/menu-collapsed-rtl.png b/core/misc/menu-collapsed-rtl.png
old mode 100644
new mode 100755
diff --git a/core/misc/menu-collapsed.png b/core/misc/menu-collapsed.png
old mode 100644
new mode 100755
diff --git a/core/misc/menu-expanded.png b/core/misc/menu-expanded.png
old mode 100644
new mode 100755
diff --git a/core/misc/menu-leaf.png b/core/misc/menu-leaf.png
old mode 100644
new mode 100755
diff --git a/core/misc/message-16-error.png b/core/misc/message-16-error.png
old mode 100644
new mode 100755
index 486390c..b5669d8
Binary files a/core/misc/message-16-error.png and b/core/misc/message-16-error.png differ
diff --git a/core/misc/message-16-help.png b/core/misc/message-16-help.png
old mode 100644
new mode 100755
index fc44326..a1ddaed
Binary files a/core/misc/message-16-help.png and b/core/misc/message-16-help.png differ
diff --git a/core/misc/message-16-info.png b/core/misc/message-16-info.png
old mode 100644
new mode 100755
index f47924f..ea2c9c2
Binary files a/core/misc/message-16-info.png and b/core/misc/message-16-info.png differ
diff --git a/core/misc/message-16-ok.png b/core/misc/message-16-ok.png
old mode 100644
new mode 100755
index 9edebe6..f9c9ab3
Binary files a/core/misc/message-16-ok.png and b/core/misc/message-16-ok.png differ
diff --git a/core/misc/message-16-warning.png b/core/misc/message-16-warning.png
old mode 100644
new mode 100755
index ffc4317..3407d7c
Binary files a/core/misc/message-16-warning.png and b/core/misc/message-16-warning.png differ
diff --git a/core/misc/message-24-error.png b/core/misc/message-24-error.png
old mode 100644
new mode 100755
index b094180..4e69089
Binary files a/core/misc/message-24-error.png and b/core/misc/message-24-error.png differ
diff --git a/core/misc/message-24-help.png b/core/misc/message-24-help.png
old mode 100644
new mode 100755
index 66b89ce..f7fae94
Binary files a/core/misc/message-24-help.png and b/core/misc/message-24-help.png differ
diff --git a/core/misc/message-24-info.png b/core/misc/message-24-info.png
old mode 100644
new mode 100755
index be599ca..cf5bc1f
Binary files a/core/misc/message-24-info.png and b/core/misc/message-24-info.png differ
diff --git a/core/misc/message-24-ok.png b/core/misc/message-24-ok.png
old mode 100644
new mode 100755
index bd36669..af32a8a
Binary files a/core/misc/message-24-ok.png and b/core/misc/message-24-ok.png differ
diff --git a/core/misc/message-24-warning.png b/core/misc/message-24-warning.png
old mode 100644
new mode 100755
index 183297d..0ec9f05
Binary files a/core/misc/message-24-warning.png and b/core/misc/message-24-warning.png differ
diff --git a/core/misc/message-32-error.png b/core/misc/message-32-error.png
new file mode 100755
index 0000000..a4f3f8f
Binary files /dev/null and b/core/misc/message-32-error.png differ
diff --git a/core/misc/message-32-help.png b/core/misc/message-32-help.png
new file mode 100755
index 0000000..fb0cd17
Binary files /dev/null and b/core/misc/message-32-help.png differ
diff --git a/core/misc/message-32-info.png b/core/misc/message-32-info.png
new file mode 100755
index 0000000..5968869
Binary files /dev/null and b/core/misc/message-32-info.png differ
diff --git a/core/misc/message-32-ok.png b/core/misc/message-32-ok.png
new file mode 100755
index 0000000..649da30
Binary files /dev/null and b/core/misc/message-32-ok.png differ
diff --git a/core/misc/message-32-warning.png b/core/misc/message-32-warning.png
new file mode 100755
index 0000000..5a70e8f
Binary files /dev/null and b/core/misc/message-32-warning.png differ
diff --git a/core/misc/message-error.svg b/core/misc/message-error.svg
new file mode 100755
index 0000000..5e97049
--- /dev/null
+++ b/core/misc/message-error.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/core/misc/message-help.svg b/core/misc/message-help.svg
new file mode 100755
index 0000000..7482716
--- /dev/null
+++ b/core/misc/message-help.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/core/misc/message-info.svg b/core/misc/message-info.svg
new file mode 100755
index 0000000..5dfe6be
--- /dev/null
+++ b/core/misc/message-info.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/core/misc/message-ok.svg b/core/misc/message-ok.svg
new file mode 100755
index 0000000..4dbf2d8
--- /dev/null
+++ b/core/misc/message-ok.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/core/misc/message-warning.svg b/core/misc/message-warning.svg
new file mode 100755
index 0000000..d7d2f12
--- /dev/null
+++ b/core/misc/message-warning.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/core/misc/normalize.css b/core/misc/normalize.css
new file mode 100755
index 0000000..08f8950
--- /dev/null
+++ b/core/misc/normalize.css
@@ -0,0 +1,425 @@
+/*! normalize.css v3.0.1 | MIT License | git.io/normalize */
+
+/**
+ * 1. Set default font family to sans-serif.
+ * 2. Prevent iOS text size adjust after orientation change, without disabling
+ * user zoom.
+ */
+
+html {
+ font-family: sans-serif; /* 1 */
+ -ms-text-size-adjust: 100%; /* 2 */
+ -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/**
+ * Remove default margin.
+ */
+
+body {
+ margin: 0;
+}
+
+/* HTML5 display definitions
+ ========================================================================== */
+
+/**
+ * Correct `block` display not defined for any HTML5 element in IE 8/9.
+ * Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox.
+ * Correct `block` display not defined for `main` in IE 11.
+ */
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+nav,
+section,
+summary {
+ display: block;
+}
+
+/**
+ * 1. Correct `inline-block` display not defined in IE 8/9.
+ * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
+ */
+
+audio,
+canvas,
+progress,
+video {
+ display: inline-block; /* 1 */
+ vertical-align: baseline; /* 2 */
+}
+
+/**
+ * Prevent modern browsers from displaying `audio` without controls.
+ * Remove excess height in iOS 5 devices.
+ */
+
+audio:not([controls]) {
+ display: none;
+ height: 0;
+}
+
+/**
+ * Address `[hidden]` styling not present in IE 8/9/10.
+ * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
+ */
+
+[hidden],
+template {
+ display: none;
+}
+
+/* Links
+ ========================================================================== */
+
+/**
+ * Remove the gray background color from active links in IE 10.
+ */
+
+a {
+ background: transparent;
+}
+
+/**
+ * Improve readability when focused and also mouse hovered in all browsers.
+ */
+
+a:active,
+a:hover {
+ outline: 0;
+}
+
+/* Text-level semantics
+ ========================================================================== */
+
+/**
+ * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
+ */
+
+abbr[title] {
+ border-bottom: 1px dotted;
+}
+
+/**
+ * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
+ */
+
+b,
+strong {
+ font-weight: bold;
+}
+
+/**
+ * Address styling not present in Safari and Chrome.
+ */
+
+dfn {
+ font-style: italic;
+}
+
+/**
+ * Address variable `h1` font-size and margin within `section` and `article`
+ * contexts in Firefox 4+, Safari, and Chrome.
+ */
+
+h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+}
+
+/**
+ * Address styling not present in IE 8/9.
+ */
+
+mark {
+ background: #ff0;
+ color: #000;
+}
+
+/**
+ * Address inconsistent and variable font size in all browsers.
+ */
+
+small {
+ font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` affecting `line-height` in all browsers.
+ */
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+/* Embedded content
+ ========================================================================== */
+
+/**
+ * Remove border when inside `a` element in IE 8/9/10.
+ */
+
+img {
+ border: 0;
+}
+
+/**
+ * Correct overflow not hidden in IE 9/10/11.
+ */
+
+svg:not(:root) {
+ overflow: hidden;
+}
+
+/* Grouping content
+ ========================================================================== */
+
+/**
+ * Address margin not present in IE 8/9 and Safari.
+ */
+
+figure {
+ margin: 1em 40px;
+}
+
+/**
+ * Address differences between Firefox and other browsers.
+ */
+
+hr {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ height: 0;
+}
+
+/**
+ * Contain overflow in all browsers.
+ */
+
+pre {
+ overflow: auto;
+}
+
+/**
+ * Address odd `em`-unit font size rendering in all browsers.
+ */
+
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, monospace;
+ font-size: 1em;
+}
+
+/* Forms
+ ========================================================================== */
+
+/**
+ * Known limitation: by default, Chrome and Safari on OS X allow very limited
+ * styling of `select`, unless a `border` property is set.
+ */
+
+/**
+ * 1. Correct color not being inherited.
+ * Known issue: affects color of disabled elements.
+ * 2. Correct font properties not being inherited.
+ * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ color: inherit; /* 1 */
+ font: inherit; /* 2 */
+ margin: 0; /* 3 */
+}
+
+/**
+ * Address `overflow` set to `hidden` in IE 8/9/10/11.
+ */
+
+button {
+ overflow: visible;
+}
+
+/**
+ * Address inconsistent `text-transform` inheritance for `button` and `select`.
+ * All other form control elements do not inherit `text-transform` values.
+ * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
+ * Correct `select` style inheritance in Firefox.
+ */
+
+button,
+select {
+ text-transform: none;
+}
+
+/**
+ * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
+ * and `video` controls.
+ * 2. Correct inability to style clickable `input` types in iOS.
+ * 3. Improve usability and consistency of cursor style between image-type
+ * `input` and others.
+ */
+
+button,
+html input[type="button"], /* 1 */
+input[type="reset"],
+input[type="submit"] {
+ -webkit-appearance: button; /* 2 */
+ cursor: pointer; /* 3 */
+}
+
+/**
+ * Re-set default cursor for disabled elements.
+ */
+
+button[disabled],
+html input[disabled] {
+ cursor: default;
+}
+
+/**
+ * Remove inner padding and border in Firefox 4+.
+ */
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+/**
+ * Address Firefox 4+ setting `line-height` on `input` using `!important` in
+ * the UA stylesheet.
+ */
+
+input {
+ line-height: normal;
+}
+
+/**
+ * It's recommended that you don't attempt to style these elements.
+ * Firefox's implementation doesn't respect box-sizing, padding, or width.
+ *
+ * 1. Address box sizing set to `content-box` in IE 8/9/10.
+ * 2. Remove excess padding in IE 8/9/10.
+ */
+
+input[type="checkbox"],
+input[type="radio"] {
+ box-sizing: border-box; /* 1 */
+ padding: 0; /* 2 */
+}
+
+/**
+ * Fix the cursor style for Chrome's increment/decrement buttons. For certain
+ * `font-size` values of the `input`, it causes the cursor style of the
+ * decrement button to change from `default` to `text`.
+ */
+
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/**
+ * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
+ * 2. Address `box-sizing` set to `border-box` in Safari and Chrome
+ * (include `-moz` to future-proof).
+ */
+
+input[type="search"] {
+ -webkit-appearance: textfield; /* 1 */
+ -moz-box-sizing: content-box;
+ -webkit-box-sizing: content-box; /* 2 */
+ box-sizing: content-box;
+}
+
+/**
+ * Remove inner padding and search cancel button in Safari and Chrome on OS X.
+ * Safari (but not Chrome) clips the cancel button when the search input has
+ * padding (and `textfield` appearance).
+ */
+
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/**
+ * Define consistent border, margin, and padding.
+ */
+
+fieldset {
+ border: 1px solid #c0c0c0;
+ margin: 0 2px;
+ padding: 0.35em 0.625em 0.75em;
+}
+
+/**
+ * 1. Correct `color` not being inherited in IE 8/9/10/11.
+ * 2. Remove padding so people aren't caught out if they zero out fieldsets.
+ */
+
+legend {
+ border: 0; /* 1 */
+ padding: 0; /* 2 */
+}
+
+/**
+ * Remove default vertical scrollbar in IE 8/9/10/11.
+ */
+
+textarea {
+ overflow: auto;
+}
+
+/**
+ * Don't inherit the `font-weight` (applied by a rule above).
+ * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
+ */
+
+optgroup {
+ font-weight: bold;
+}
+
+/* Tables
+ ========================================================================== */
+
+/**
+ * Remove most spacing between table cells.
+ */
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+td,
+th {
+ padding: 0;
+}
diff --git a/core/misc/permissions.png b/core/misc/permissions.png
old mode 100644
new mode 100755
diff --git a/core/misc/print.css b/core/misc/print.css
new file mode 100755
index 0000000..b524ff0
--- /dev/null
+++ b/core/misc/print.css
@@ -0,0 +1,31 @@
+
+body {
+ margin: 1em;
+ background-color: #fff;
+}
+[dir="rtl"] body {
+ direction: rtl;
+}
+th {
+ text-align: left; /* LTR */
+ color: #006;
+ border-bottom: 1px solid #ccc;
+}
+[dir="rtl"] th {
+ text-align: right;
+}
+tr.odd {
+ background-color: #ddd;
+}
+tr.even {
+ background-color: #fff;
+}
+td {
+ padding: 5px;
+}
+#menu {
+ visibility: hidden;
+}
+#main {
+ margin: 1em;
+}
diff --git a/core/misc/progress.gif b/core/misc/progress.gif
old mode 100644
new mode 100755
index 0188eae..f84a9de
Binary files a/core/misc/progress.gif and b/core/misc/progress.gif differ
diff --git a/core/misc/progress.js b/core/misc/progress.js
old mode 100644
new mode 100755
index 822666a..e24c043
--- a/core/misc/progress.js
+++ b/core/misc/progress.js
@@ -10,8 +10,7 @@
* e.g. pb = new progressBar('myProgressBar');
* some_element.appendChild(pb.element);
*/
-Drupal.progressBar = function (id, updateCallback, method, errorCallback) {
- var pb = this;
+Backdrop.progressBar = function (id, updateCallback, method, errorCallback) {
this.id = id;
this.method = method || 'GET';
this.updateCallback = updateCallback;
@@ -28,7 +27,7 @@ Drupal.progressBar = function (id, updateCallback, method, errorCallback) {
/**
* Set the percentage and status message for the progressbar.
*/
-Drupal.progressBar.prototype.setProgress = function (percentage, message) {
+Backdrop.progressBar.prototype.setProgress = function (percentage, message) {
if (percentage >= 0 && percentage <= 100) {
$('div.filled', this.element).css('width', percentage + '%');
$('div.percentage', this.element).html(percentage + '%');
@@ -42,7 +41,7 @@ Drupal.progressBar.prototype.setProgress = function (percentage, message) {
/**
* Start monitoring progress via Ajax.
*/
-Drupal.progressBar.prototype.startMonitoring = function (uri, delay) {
+Backdrop.progressBar.prototype.startMonitoring = function (uri, delay) {
this.delay = delay;
this.uri = uri;
this.sendPing();
@@ -51,7 +50,7 @@ Drupal.progressBar.prototype.startMonitoring = function (uri, delay) {
/**
* Stop monitoring progress via Ajax.
*/
-Drupal.progressBar.prototype.stopMonitoring = function () {
+Backdrop.progressBar.prototype.stopMonitoring = function () {
clearTimeout(this.timer);
// This allows monitoring to be stopped from within the callback.
this.uri = null;
@@ -60,7 +59,7 @@ Drupal.progressBar.prototype.stopMonitoring = function () {
/**
* Request progress data from server.
*/
-Drupal.progressBar.prototype.sendPing = function () {
+Backdrop.progressBar.prototype.sendPing = function () {
if (this.timer) {
clearTimeout(this.timer);
}
@@ -85,7 +84,7 @@ Drupal.progressBar.prototype.sendPing = function () {
pb.timer = setTimeout(function () { pb.sendPing(); }, pb.delay);
},
error: function (xmlhttp) {
- pb.displayError(Drupal.ajaxError(xmlhttp, pb.uri));
+ pb.displayError(Backdrop.ajaxError(xmlhttp, pb.uri));
}
});
}
@@ -94,7 +93,7 @@ Drupal.progressBar.prototype.sendPing = function () {
/**
* Display errors on the page.
*/
-Drupal.progressBar.prototype.displayError = function (string) {
+Backdrop.progressBar.prototype.displayError = function (string) {
var error = $('').html(string);
$(this.element).before(error).hide();
diff --git a/core/misc/states.js b/core/misc/states.js
new file mode 100755
index 0000000..cd87777
--- /dev/null
+++ b/core/misc/states.js
@@ -0,0 +1,565 @@
+(function ($) {
+
+/**
+ * The base States namespace.
+ *
+ * Having the local states variable allows us to use the States namespace
+ * without having to always declare "Backdrop.states".
+ */
+var states = Backdrop.states = {
+ // An array of functions that should be postponed.
+ postponed: []
+};
+
+/**
+ * Attaches the states.
+ */
+Backdrop.behaviors.states = {
+ attach: function (context, settings) {
+ var $context = $(context);
+ for (var selector in settings.states) {
+ if (settings.states.hasOwnProperty(selector)) {
+ for (var state in settings.states[selector]) {
+ if (settings.states[selector].hasOwnProperty(state)) {
+ new states.Dependent({
+ element: $context.find(selector),
+ state: states.State.sanitize(state),
+ constraints: settings.states[selector][state]
+ });
+ }
+ }
+ }
+ }
+
+ // Execute all postponed functions now.
+ while (states.postponed.length) {
+ (states.postponed.shift())();
+ }
+ }
+};
+
+/**
+ * Object representing an element that depends on other elements.
+ *
+ * @param args
+ * Object with the following keys (all of which are required):
+ * - element: A jQuery object of the dependent element
+ * - state: A State object describing the state that is dependent
+ * - constraints: An object with dependency specifications. Lists all elements
+ * that this element depends on. It can be nested and can contain arbitrary
+ * AND and OR clauses.
+ */
+states.Dependent = function (args) {
+ $.extend(this, { values: {}, oldValue: null }, args);
+
+ this.dependees = this.getDependees();
+ for (var selector in this.dependees) {
+ if (this.dependees.hasOwnProperty(selector)) {
+ this.initializeDependee(selector, this.dependees[selector]);
+ }
+ }
+};
+
+/**
+ * Comparison functions for comparing the value of an element with the
+ * specification from the dependency settings. If the object type can't be
+ * found in this list, the === operator is used by default.
+ */
+states.Dependent.comparisons = {
+ 'RegExp': function (reference, value) {
+ return reference.test(value);
+ },
+ 'Function': function (reference, value) {
+ // The "reference" variable is a comparison function.
+ return reference(value);
+ },
+ 'Number': function (reference, value) {
+ // If "reference" is a number and "value" is a string, then cast reference
+ // as a string before applying the strict comparison in compare(). Otherwise
+ // numeric keys in the form's #states array fail to match string values
+ // returned from jQuery's val().
+ return (typeof value === 'string') ? compare(reference.toString(), value) : compare(reference, value);
+ }
+};
+
+states.Dependent.prototype = {
+ /**
+ * Initializes one of the elements this dependent depends on.
+ *
+ * @param selector
+ * The CSS selector describing the dependee.
+ * @param dependeeStates
+ * The list of states that have to be monitored for tracking the
+ * dependee's compliance status.
+ */
+ initializeDependee: function (selector, dependeeStates) {
+ var state, self = this;
+
+ function stateEventHandler(e) {
+ self.update(e.data.selector, e.data.state, e.value);
+ }
+
+ // Cache for the states of this dependee.
+ this.values[selector] = {};
+
+ for (var i in dependeeStates) {
+ if (dependeeStates.hasOwnProperty(i)) {
+ state = dependeeStates[i];
+ // Make sure we're not initializing this selector/state combination twice.
+ if ($.inArray(state, dependeeStates) === -1) {
+ continue;
+ }
+
+ state = states.State.sanitize(state);
+
+ // Initialize the value of this state.
+ this.values[selector][state.name] = null;
+
+ // Monitor state changes of the specified state for this dependee.
+ $(selector).bind('state:' + state, {selector: selector, state: state}, stateEventHandler);
+
+ // Make sure the event we just bound ourselves to is actually fired.
+ new states.Trigger({ selector: selector, state: state });
+ }
+ }
+ },
+
+ /**
+ * Compares a value with a reference value.
+ *
+ * @param reference
+ * The value used for reference.
+ * @param selector
+ * CSS selector describing the dependee.
+ * @param state
+ * A State object describing the dependee's updated state.
+ *
+ * @return
+ * true or false.
+ */
+ compare: function (reference, selector, state) {
+ var value = this.values[selector][state.name];
+ if (reference.constructor.name in states.Dependent.comparisons) {
+ // Use a custom compare function for certain reference value types.
+ return states.Dependent.comparisons[reference.constructor.name](reference, value);
+ }
+ else {
+ // Do a plain comparison otherwise.
+ return compare(reference, value);
+ }
+ },
+
+ /**
+ * Update the value of a dependee's state.
+ *
+ * @param selector
+ * CSS selector describing the dependee.
+ * @param state
+ * A State object describing the dependee's updated state.
+ * @param value
+ * The new value for the dependee's updated state.
+ */
+ update: function (selector, state, value) {
+ // Only act when the 'new' value is actually new.
+ if (value !== this.values[selector][state.name]) {
+ this.values[selector][state.name] = value;
+ this.reevaluate();
+ }
+ },
+
+ /**
+ * Triggers change events in case a state changed.
+ */
+ reevaluate: function () {
+ // Check whether any constraint for this dependent state is satisifed.
+ var value = this.verifyConstraints(this.constraints);
+
+ // Only invoke a state change event when the value actually changed.
+ if (value !== this.oldValue) {
+ // Store the new value so that we can compare later whether the value
+ // actually changed.
+ this.oldValue = value;
+
+ // Normalize the value to match the normalized state name.
+ value = invert(value, this.state.invert);
+
+ // By adding "trigger: true", we ensure that state changes don't go into
+ // infinite loops.
+ this.element.trigger({ type: 'state:' + this.state, value: value, trigger: true });
+ }
+ },
+
+ /**
+ * Evaluates child constraints to determine if a constraint is satisfied.
+ *
+ * @param constraints
+ * A constraint object or an array of constraints.
+ * @param selector
+ * The selector for these constraints. If undefined, there isn't yet a
+ * selector that these constraints apply to. In that case, the keys of the
+ * object are interpreted as the selector if encountered.
+ *
+ * @return
+ * true or false, depending on whether these constraints are satisfied.
+ */
+ verifyConstraints: function(constraints, selector) {
+ var result;
+ if ($.isArray(constraints)) {
+ // This constraint is an array (OR or XOR).
+ var hasXor = $.inArray('xor', constraints) === -1;
+ for (var i = 0, len = constraints.length; i < len; i++) {
+ if (constraints[i] != 'xor') {
+ var constraint = this.checkConstraints(constraints[i], selector, i);
+ // Return if this is OR and we have a satisfied constraint or if this
+ // is XOR and we have a second satisfied constraint.
+ if (constraint && (hasXor || result)) {
+ return hasXor;
+ }
+ result = result || constraint;
+ }
+ }
+ }
+ // Make sure we don't try to iterate over things other than objects. This
+ // shouldn't normally occur, but in case the condition definition is bogus,
+ // we don't want to end up with an infinite loop.
+ else if ($.isPlainObject(constraints)) {
+ // This constraint is an object (AND).
+ for (var n in constraints) {
+ if (constraints.hasOwnProperty(n)) {
+ result = ternary(result, this.checkConstraints(constraints[n], selector, n));
+ // False and anything else will evaluate to false, so return when any
+ // false condition is found.
+ if (result === false) { return false; }
+ }
+ }
+ }
+ return result;
+ },
+
+ /**
+ * Checks whether the value matches the requirements for this constraint.
+ *
+ * @param value
+ * Either the value of a state or an array/object of constraints. In the
+ * latter case, resolving the constraint continues.
+ * @param selector
+ * The selector for this constraint. If undefined, there isn't yet a
+ * selector that this constraint applies to. In that case, the state key is
+ * propagates to a selector and resolving continues.
+ * @param state
+ * The state to check for this constraint. If undefined, resolving
+ * continues.
+ * If both selector and state aren't undefined and valid non-numeric
+ * strings, a lookup for the actual value of that selector's state is
+ * performed. This parameter is not a State object but a pristine state
+ * string.
+ *
+ * @return
+ * true or false, depending on whether this constraint is satisfied.
+ */
+ checkConstraints: function(value, selector, state) {
+ // Normalize the last parameter. If it's non-numeric, we treat it either as
+ // a selector (in case there isn't one yet) or as a trigger/state.
+ if (typeof state !== 'string' || (/[0-9]/).test(state[0])) {
+ state = null;
+ }
+ else if (typeof selector === 'undefined') {
+ // Propagate the state to the selector when there isn't one yet.
+ selector = state;
+ state = null;
+ }
+
+ if (state !== null) {
+ // constraints is the actual constraints of an element to check for.
+ state = states.State.sanitize(state);
+ return invert(this.compare(value, selector, state), state.invert);
+ }
+ else {
+ // Resolve this constraint as an AND/OR operator.
+ return this.verifyConstraints(value, selector);
+ }
+ },
+
+ /**
+ * Gathers information about all required triggers.
+ */
+ getDependees: function() {
+ var cache = {};
+ // Swivel the lookup function so that we can record all available selector-
+ // state combinations for initialization.
+ var _compare = this.compare;
+ this.compare = function(reference, selector, state) {
+ (cache[selector] || (cache[selector] = [])).push(state.name);
+ // Return nothing (=== undefined) so that the constraint loops are not
+ // broken.
+ };
+
+ // This call doesn't actually verify anything but uses the resolving
+ // mechanism to go through the constraints array, trying to look up each
+ // value. Since we swivelled the compare function, this comparison returns
+ // undefined and lookup continues until the very end. Instead of lookup up
+ // the value, we record that combination of selector and state so that we
+ // can initialize all triggers.
+ this.verifyConstraints(this.constraints);
+ // Restore the original function.
+ this.compare = _compare;
+
+ return cache;
+ }
+};
+
+states.Trigger = function (args) {
+ $.extend(this, args);
+
+ if (this.state in states.Trigger.states) {
+ this.element = $(this.selector);
+
+ // Only call the trigger initializer when it wasn't yet attached to this
+ // element. Otherwise we'd end up with duplicate events.
+ if (!this.element.data('trigger:' + this.state)) {
+ this.initialize();
+ }
+ }
+};
+
+states.Trigger.prototype = {
+ initialize: function () {
+ var trigger = states.Trigger.states[this.state];
+
+ if (typeof trigger == 'function') {
+ // We have a custom trigger initialization function.
+ trigger.call(window, this.element);
+ }
+ else {
+ for (var event in trigger) {
+ if (trigger.hasOwnProperty(event)) {
+ this.defaultTrigger(event, trigger[event]);
+ }
+ }
+ }
+
+ // Mark this trigger as initialized for this element.
+ this.element.data('trigger:' + this.state, true);
+ },
+
+ defaultTrigger: function (event, valueFn) {
+ var oldValue = valueFn.call(this.element);
+
+ // Attach the event callback.
+ this.element.bind(event, $.proxy(function (e) {
+ var value = valueFn.call(this.element, e);
+ // Only trigger the event if the value has actually changed.
+ if (oldValue !== value) {
+ this.element.trigger({ type: 'state:' + this.state, value: value, oldValue: oldValue });
+ oldValue = value;
+ }
+ }, this));
+
+ states.postponed.push($.proxy(function () {
+ // Trigger the event once for initialization purposes.
+ this.element.trigger({ type: 'state:' + this.state, value: oldValue, oldValue: null });
+ }, this));
+ }
+};
+
+/**
+ * This list of states contains functions that are used to monitor the state
+ * of an element. Whenever an element depends on the state of another element,
+ * one of these trigger functions is added to the dependee so that the
+ * dependent element can be updated.
+ */
+states.Trigger.states = {
+ // 'empty' describes the state to be monitored
+ empty: {
+ // 'keyup' is the (native DOM) event that we watch for.
+ 'keyup': function () {
+ // The function associated to that trigger returns the new value for the
+ // state.
+ return this.val() == '';
+ }
+ },
+
+ checked: {
+ 'change': function () {
+ // prop() and attr() only takes the first element into account. To support
+ // selectors matching multiple checkboxes, iterate over all and return
+ // whether any is checked.
+ var checked = false;
+ this.each(function () {
+ // Use prop() here as we want a boolean of the checkbox state.
+ // @see http://api.jquery.com/prop/
+ checked = $(this).prop('checked');
+ // Break the each() loop if this is checked.
+ return !checked;
+ });
+ return checked;
+ }
+ },
+
+ // For radio buttons, only return the value if the radio button is selected.
+ value: {
+ 'keyup': function () {
+ // Radio buttons share the same :input[name="key"] selector.
+ if (this.length > 1) {
+ // Initial checked value of radios is undefined, so we return false.
+ return this.filter(':checked').val() || false;
+ }
+ return this.val();
+ },
+ 'change': function () {
+ // Radio buttons share the same :input[name="key"] selector.
+ if (this.length > 1) {
+ // Initial checked value of radios is undefined, so we return false.
+ return this.filter(':checked').val() || false;
+ }
+ return this.val();
+ }
+ },
+
+ collapsed: {
+ 'collapsed': function(e) {
+ return (typeof e !== 'undefined' && 'value' in e) ? e.value : this.is('.collapsed');
+ }
+ }
+};
+
+
+/**
+ * A state object is used for describing the state and performing aliasing.
+ */
+states.State = function(state) {
+ // We may need the original unresolved name later.
+ this.pristine = this.name = state;
+
+ // Normalize the state name.
+ while (true) {
+ // Iteratively remove exclamation marks and invert the value.
+ while (this.name.charAt(0) == '!') {
+ this.name = this.name.substring(1);
+ this.invert = !this.invert;
+ }
+
+ // Replace the state with its normalized name.
+ if (this.name in states.State.aliases) {
+ this.name = states.State.aliases[this.name];
+ }
+ else {
+ break;
+ }
+ }
+};
+
+/**
+ * Creates a new State object by sanitizing the passed value.
+ */
+states.State.sanitize = function (state) {
+ if (state instanceof states.State) {
+ return state;
+ }
+ else {
+ return new states.State(state);
+ }
+};
+
+/**
+ * This list of aliases is used to normalize states and associates negated names
+ * with their respective inverse state.
+ */
+states.State.aliases = {
+ 'enabled': '!disabled',
+ 'invisible': '!visible',
+ 'invalid': '!valid',
+ 'untouched': '!touched',
+ 'optional': '!required',
+ 'filled': '!empty',
+ 'unchecked': '!checked',
+ 'irrelevant': '!relevant',
+ 'expanded': '!collapsed',
+ 'readwrite': '!readonly'
+};
+
+states.State.prototype = {
+ invert: false,
+
+ /**
+ * Ensures that just using the state object returns the name.
+ */
+ toString: function() {
+ return this.name;
+ }
+};
+
+/**
+ * Global state change handlers. These are bound to "document" to cover all
+ * elements whose state changes. Events sent to elements within the page
+ * bubble up to these handlers. We use this system so that themes and modules
+ * can override these state change handlers for particular parts of a page.
+ */
+
+$(document).bind('state:disabled', function(e) {
+ // Only act when this change was triggered by a dependency and not by the
+ // element monitoring itself.
+ if (e.trigger) {
+ $(e.target)
+ .prop('disabled', e.value)
+ .closest('.form-item, .form-submit, .form-wrapper').toggleClass('form-disabled', e.value)
+ .find('select, input, textarea').prop('disabled', e.value);
+
+ // Note: WebKit nightlies don't reflect that change correctly.
+ // See https://bugs.webkit.org/show_bug.cgi?id=23789
+ }
+});
+
+$(document).bind('state:required', function(e) {
+ if (e.trigger) {
+ if (e.value) {
+ $(e.target).closest('.form-item, .form-wrapper').find('label').append('*');
+ }
+ else {
+ $(e.target).closest('.form-item, .form-wrapper').find('label .form-required').remove();
+ }
+ }
+});
+
+$(document).bind('state:visible', function(e) {
+ if (e.trigger) {
+ $(e.target).closest('.form-item, .form-submit, .form-wrapper').toggle(e.value);
+ }
+});
+
+$(document).bind('state:checked', function(e) {
+ if (e.trigger) {
+ $(e.target).prop('checked', e.value);
+ }
+});
+
+$(document).bind('state:collapsed', function(e) {
+ if (e.trigger) {
+ if ($(e.target).is('.collapsed') !== e.value) {
+ $('> legend a', e.target).click();
+ }
+ }
+});
+
+
+/**
+ * These are helper functions implementing addition "operators" and don't
+ * implement any logic that is particular to states.
+ */
+
+// Bitwise AND with a third undefined state.
+function ternary (a, b) {
+ return typeof a === 'undefined' ? b : (typeof b === 'undefined' ? a : a && b);
+}
+
+// Inverts a (if it's not undefined) when invertState is true.
+function invert (a, invertState) {
+ return (invertState && typeof a !== 'undefined') ? !a : a;
+}
+
+// Compares two values while ignoring undefined values.
+function compare (a, b) {
+ return (a === b) ? (typeof a === 'undefined' ? a : true) : (typeof a === 'undefined' || typeof b === 'undefined');
+}
+
+})(jQuery);
diff --git a/core/misc/tabledrag.js b/core/misc/tabledrag.js
new file mode 100755
index 0000000..74583f9
--- /dev/null
+++ b/core/misc/tabledrag.js
@@ -0,0 +1,1241 @@
+(function ($) {
+
+/**
+ * Drag and drop table rows with field manipulation.
+ *
+ * Using the backdrop_add_tabledrag() function, any table with weights or parent
+ * relationships may be made into draggable tables. Columns containing a field
+ * may optionally be hidden, providing a better user experience.
+ *
+ * Created tableDrag instances may be modified with custom behaviors by
+ * overriding the .onDrag, .onDrop, .row.onSwap, and .row.onIndent methods.
+ * See blocks.js for an example of adding additional functionality to tableDrag.
+ */
+Backdrop.behaviors.tableDrag = {
+ attach: function (context, settings) {
+ function initTableDrag(table, base) {
+ if (table.length) {
+ // Create the new tableDrag instance. Save in the Backdrop variable
+ // to allow other scripts access to the object.
+ Backdrop.tableDrag[base] = new Backdrop.tableDrag(table[0], settings.tableDrag[base]);
+ }
+ }
+
+ for (var base in settings.tableDrag) {
+ if (settings.tableDrag.hasOwnProperty(base)) {
+ initTableDrag($(context).find('#' + base).once('tabledrag'), base);
+ }
+ }
+ }
+};
+
+/**
+ * Constructor for the tableDrag object. Provides table and field manipulation.
+ *
+ * @param table
+ * DOM object for the table to be made draggable.
+ * @param tableSettings
+ * Settings for the table added via backdrop_add_tabledrag().
+ */
+Backdrop.tableDrag = function (table, tableSettings) {
+ var self = this;
+
+ // Required object variables.
+ this.table = table;
+ this.tableSettings = tableSettings;
+ this.dragObject = null; // Used to hold information about a current drag operation.
+ this.rowObject = null; // Provides operations for row manipulation.
+ this.oldRowElement = null; // Remember the previous element.
+ this.oldY = 0; // Used to determine up or down direction from last pointer move.
+ this.changed = false; // Whether anything in the entire table has changed.
+ this.maxDepth = 0; // Maximum amount of allowed parenting.
+ this.rtl = $(this.table).css('direction') == 'rtl' ? -1 : 1; // Direction of the table.
+
+ // Touch support, borrowed from Modernizer.touch.
+ this.touchSupport = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
+
+ // Configure the scroll settings.
+ this.scrollSettings = { amount: 4, interval: 50, trigger: 70 };
+ this.scrollInterval = null;
+ this.scrollY = 0;
+ this.windowHeight = 0;
+
+ // Check this table's settings to see if there are parent relationships in
+ // this table. For efficiency, large sections of code can be skipped if we
+ // don't need to track horizontal movement and indentations.
+ this.indentEnabled = false;
+ for (var group in tableSettings) {
+ if (tableSettings.hasOwnProperty(group)) {
+ for (var n in tableSettings[group]) {
+ if (tableSettings[group].hasOwnProperty(n)) {
+ if (tableSettings[group][n].relationship === 'parent') {
+ this.indentEnabled = true;
+ }
+ if (tableSettings[group][n].limit > 0) {
+ this.maxDepth = tableSettings[group][n].limit;
+ }
+ }
+ }
+ }
+ }
+ if (this.indentEnabled) {
+ this.indentCount = 1; // Total width of indents, set in makeDraggable.
+ // Find the width of indentations to measure pointer movements against.
+ // Because the table doesn't need to start with any indentations, we
+ // manually append 2 indentations in the first draggable row, measure
+ // the offset, then remove.
+ var indent = Backdrop.theme('tableDragIndentation');
+ var testRow = $('
').addClass('draggable').appendTo(table);
+ var testCell = $('
').appendTo(testRow).prepend(indent).prepend(indent);
+ this.indentAmount = $('.indentation', testCell).get(1).offsetLeft - $('.indentation', testCell).get(0).offsetLeft;
+ testRow.remove();
+ }
+
+ // Make each applicable row draggable.
+ // Match immediate children of the parent element to allow nesting.
+ $('> tr.draggable, > tbody > tr.draggable', table).each(function () { self.makeDraggable(this); });
+
+ // Add a link before the table for users to show or hide weight columns.
+ $(table).before($('')
+ .attr('title', Backdrop.t('Re-order rows by numerical weight instead of dragging.'))
+ .click(function () {
+ if ($.cookie('Backdrop.tableDrag.showWeight') == 1) {
+ self.hideColumns();
+ }
+ else {
+ self.showColumns();
+ }
+ return false;
+ })
+ .wrap('')
+ .parent()
+ );
+
+ // Initialize the specified columns (for example, weight or parent columns)
+ // to show or hide according to user preference. This aids accessibility
+ // so that, e.g., screen reader users can choose to enter weight values and
+ // manipulate form elements directly, rather than using drag-and-drop..
+ self.initColumns();
+
+ // Add event bindings to the document. The self variable is passed along
+ // as event handlers do not have direct access to the tableDrag object.
+ if (self.touchSupport) {
+ $(document).on('touchmove', function (event) { return self.dragRow(event.originalEvent.touches[0], self); });
+ $(document).on('touchend', function (event) { return self.dropRow(event.originalEvent.touches[0], self); });
+ }
+ else {
+ $(document).on('mousemove', function (event) { return self.dragRow(event, self); });
+ $(document).on('mouseup', function (event) { return self.dropRow(event, self); });
+ }
+};
+
+/**
+ * Initialize columns containing form elements to be hidden by default,
+ * according to the settings for this tableDrag instance.
+ *
+ * Identify and mark each cell with a CSS class so we can easily toggle
+ * show/hide it. Finally, hide columns if user does not have a
+ * 'Backdrop.tableDrag.showWeight' cookie.
+ */
+Backdrop.tableDrag.prototype.initColumns = function () {
+ var $table = $(this.table), hidden, cell, columnIndex;
+ for (var group in this.tableSettings) {
+ if (this.tableSettings.hasOwnProperty(group)) { // Find the first field in this group.
+ for (var d in this.tableSettings[group]) {
+ if (this.tableSettings[group].hasOwnProperty(d)) {
+ var field = $table.find('.' + this.tableSettings[group][d].target + ':first');
+ if (field.length && this.tableSettings[group][d].hidden) {
+ hidden = this.tableSettings[group][d].hidden;
+ cell = field.closest('td');
+ break;
+ }
+ }
+ }
+
+ // Mark the column containing this field so it can be hidden.
+ if (hidden && cell[0]) {
+ // Add 1 to our indexes. The nth-child selector is 1 based, not 0 based.
+ // Match immediate children of the parent element to allow nesting.
+ columnIndex = cell.parent().find('> td').index(cell.get(0)) + 1;
+ $table.find('> thead > tr, > tbody > tr, > tr').each(this.addColspanClass(columnIndex));
+ }
+ }
+ }
+
+ // Now hide cells and reduce colspans unless cookie indicates previous choice.
+ // Set a cookie if it is not already present.
+ if ($.cookie('Backdrop.tableDrag.showWeight') === null) {
+ $.cookie('Backdrop.tableDrag.showWeight', 0, {
+ path: Backdrop.settings.basePath,
+ // The cookie expires in one year.
+ expires: 365
+ });
+ this.hideColumns();
+ }
+ // Check cookie value and show/hide weight columns accordingly.
+ else {
+ if ($.cookie('Backdrop.tableDrag.showWeight') == 1) {
+ this.showColumns();
+ }
+ else {
+ this.hideColumns();
+ }
+ }
+};
+
+/**
+ * Mark cells that have colspan so we can adjust the colspan
+ * instead of hiding them altogether.
+ */
+Backdrop.tableDrag.prototype.addColspanClass = function(columnIndex) {
+ return function () {
+ // Get the columnIndex and adjust for any colspans in this row.
+ var $row = $(this);
+ var index = columnIndex;
+ var cells = $row.children();
+ var cell;
+ cells.each(function (n) {
+ if (n < index && this.colSpan && this.colSpan > 1) {
+ index -= this.colSpan - 1;
+ }
+ });
+ if (index > 0) {
+ cell = cells.filter(':nth-child(' + index + ')');
+ if (cell[0].colSpan && cell[0].colSpan > 1) {
+ // If this cell has a colspan, mark it so we can reduce the colspan.
+ cell.addClass('tabledrag-has-colspan');
+ }
+ else {
+ // Mark this cell so we can hide it.
+ cell.addClass('tabledrag-hide');
+ }
+ }
+ };
+};
+
+/**
+ * Hide the columns containing weight/parent form elements.
+ * Undo showColumns().
+ */
+Backdrop.tableDrag.prototype.hideColumns = function () {
+ // Hide weight/parent cells and headers.
+ $('.tabledrag-hide', 'table.tabledrag-processed').css('display', 'none');
+ // Show TableDrag handles.
+ $('.tabledrag-handle', 'table.tabledrag-processed').css('display', '');
+ // Reduce the colspan of any effected multi-span columns.
+ $('.tabledrag-has-colspan', 'table.tabledrag-processed').each(function () {
+ this.colSpan = this.colSpan - 1;
+ });
+ // Change link text.
+ $('.tabledrag-toggle-weight').text(Backdrop.t('Show row weights'));
+ // Change cookie.
+ $.cookie('Backdrop.tableDrag.showWeight', 0, {
+ path: Backdrop.settings.basePath,
+ // The cookie expires in one year.
+ expires: 365
+ });
+ // Trigger an event to allow other scripts to react to this display change.
+ $('table.tabledrag-processed').trigger('columnschange', 'hide');
+};
+
+/**
+ * Show the columns containing weight/parent form elements
+ * Undo hideColumns().
+ */
+Backdrop.tableDrag.prototype.showColumns = function () {
+ // Show weight/parent cells and headers.
+ $('.tabledrag-hide', 'table.tabledrag-processed').css('display', '');
+ // Hide TableDrag handles.
+ $('.tabledrag-handle', 'table.tabledrag-processed').css('display', 'none');
+ // Increase the colspan for any columns where it was previously reduced.
+ $('.tabledrag-has-colspan', 'table.tabledrag-processed').each(function () {
+ this.colSpan = this.colSpan + 1;
+ });
+ // Change link text.
+ $('.tabledrag-toggle-weight').text(Backdrop.t('Hide row weights'));
+ // Change cookie.
+ $.cookie('Backdrop.tableDrag.showWeight', 1, {
+ path: Backdrop.settings.basePath,
+ // The cookie expires in one year.
+ expires: 365
+ });
+ // Trigger an event to allow other scripts to react to this display change.
+ $('table.tabledrag-processed').trigger('columnschange', 'show');
+};
+
+/**
+ * Find the target used within a particular row and group.
+ */
+Backdrop.tableDrag.prototype.rowSettings = function (group, row) {
+ var field = $('.' + group, row);
+ var tableSettingsGroup = this.tableSettings[group];
+ for (var delta in tableSettingsGroup) {
+ if (tableSettingsGroup.hasOwnProperty(delta)) {
+ var targetClass = tableSettingsGroup[delta].target;
+ if (field.is('.' + targetClass)) {
+ // Return a copy of the row settings.
+ var rowSettings = {};
+ for (var n in tableSettingsGroup[delta]) {
+ if (tableSettingsGroup[delta].hasOwnProperty(n)) {
+ rowSettings[n] = tableSettingsGroup[delta][n];
+ }
+ }
+ return rowSettings;
+ }
+ }
+ }
+};
+
+/**
+ * Take an item and add event handlers to make it become draggable.
+ */
+Backdrop.tableDrag.prototype.makeDraggable = function (item) {
+ var self = this;
+ var $item = $(item);
+
+ // Create the handle.
+ var handle = $('
').attr('title', Backdrop.t('Drag to re-order'));
+ // Insert the handle after indentations (if any).
+ var $indentationLast = $item.find('td:first .indentation:last');
+ if ($indentationLast.length) {
+ $indentationLast.after(handle);
+ // Update the total width of indentation in this entire table.
+ self.indentCount = Math.max($item.find('.indentation').length, self.indentCount);
+ }
+ else {
+ $item.find('td:first').prepend(handle);
+ }
+
+ // Add hover action for the handle.
+ handle.hover(function () {
+ self.dragObject == null ? $(this).addClass('tabledrag-handle-hover') : null;
+ }, function () {
+ self.dragObject == null ? $(this).removeClass('tabledrag-handle-hover') : null;
+ });
+
+ if (this.touchSupport) {
+ handle.on('touchstart', function (event) {
+ event.preventDefault();
+ event = event.originalEvent.touches[0];
+ self.dragStart(event, self, item);
+ });
+ }
+ else {
+ handle.on('mousedown', function (event) {
+ event.preventDefault();
+ self.dragStart(event, self, item);
+ });
+ }
+
+ // Prevent the anchor tag from jumping us to the top of the page.
+ handle.on('click', function (e) {
+ e.preventDefault();
+ });
+
+ // Set blur cleanup when a handle is focused.
+ handle.on('focus', function () {
+ $(this).addClass('tabledrag-handle-hover');
+ self.safeBlur = true;
+ });
+
+ // On blur, fire the same function as a touchend/mouseup. This is used to
+ // update values after a row has been moved through the keyboard support.
+ handle.on('blur', function (event) {
+ $(this).removeClass('tabledrag-handle-hover');
+ if (self.rowObject && self.safeBlur) {
+ self.dropRow(event, self);
+ }
+ });
+
+ // Add arrow-key support to the handle.
+ handle.on('keydown', function (event) {
+ // If a rowObject doesn't yet exist and this isn't the tab key.
+ if (event.keyCode !== 9 && !self.rowObject) {
+ self.rowObject = new self.row(item, 'keyboard', self.indentEnabled, self.maxDepth, true);
+ }
+
+ var keyChange = false;
+ var groupHeight;
+ switch (event.keyCode) {
+ case 37: // Left arrow.
+ case 63234: // Safari left arrow.
+ keyChange = true;
+ self.rowObject.indent(-1 * self.rtl);
+ break;
+ case 38: // Up arrow.
+ case 63232: // Safari up arrow.
+ var $previousRow = $(self.rowObject.element).prev('tr').eq(0);
+ var previousRow = $previousRow.get(0);
+ while (previousRow && $previousRow.is(':hidden')) {
+ $previousRow = $(previousRow).prev('tr').eq(0);
+ previousRow = $previousRow.get(0);
+ }
+ if (previousRow) {
+ self.safeBlur = false; // Do not allow the onBlur cleanup.
+ self.rowObject.direction = 'up';
+ keyChange = true;
+
+ if ($(item).is('.tabledrag-root')) {
+ // Swap with the previous top-level row.
+ groupHeight = 0;
+ while (previousRow && $previousRow.find('.indentation').length) {
+ $previousRow = $(previousRow).prev('tr').eq(0);
+ previousRow = $previousRow.get(0);
+ groupHeight += $previousRow.is(':hidden') ? 0 : previousRow.offsetHeight;
+ }
+ if (previousRow) {
+ self.rowObject.swap('before', previousRow);
+ // No need to check for indentation, 0 is the only valid one.
+ window.scrollBy(0, -groupHeight);
+ }
+ }
+ else if (self.table.tBodies[0].rows[0] !== previousRow || $previousRow.is('.draggable')) {
+ // Swap with the previous row (unless previous row is the first one
+ // and undraggable).
+ self.rowObject.swap('before', previousRow);
+ self.rowObject.interval = null;
+ self.rowObject.indent(0);
+ window.scrollBy(0, -parseInt(item.offsetHeight, 10));
+ }
+ handle.trigger('focus'); // Regain focus after the DOM manipulation.
+ }
+ break;
+ case 39: // Right arrow.
+ case 63235: // Safari right arrow.
+ keyChange = true;
+ self.rowObject.indent(self.rtl);
+ break;
+ case 40: // Down arrow.
+ case 63233: // Safari down arrow.
+ var $nextRow = $(self.rowObject.group).filter(':last').next('tr').eq(0);
+ var nextRow = $nextRow.get(0);
+ while (nextRow && $nextRow.is(':hidden')) {
+ $nextRow = $(nextRow).next('tr').eq(0);
+ nextRow = $nextRow.get(0);
+ }
+ if (nextRow) {
+ self.safeBlur = false; // Do not allow the onBlur cleanup.
+ self.rowObject.direction = 'down';
+ keyChange = true;
+
+ if ($(item).is('.tabledrag-root')) {
+ // Swap with the next group (necessarily a top-level one).
+ groupHeight = 0;
+ var nextGroup = new self.row(nextRow, 'keyboard', self.indentEnabled, self.maxDepth, false);
+ if (nextGroup) {
+ $(nextGroup.group).each(function () {
+ groupHeight += $(this).is(':hidden') ? 0 : this.offsetHeight;
+ });
+ var nextGroupRow = $(nextGroup.group).filter(':last').get(0);
+ self.rowObject.swap('after', nextGroupRow);
+ // No need to check for indentation, 0 is the only valid one.
+ window.scrollBy(0, parseInt(groupHeight, 10));
+ }
+ }
+ else {
+ // Swap with the next row.
+ self.rowObject.swap('after', nextRow);
+ self.rowObject.interval = null;
+ self.rowObject.indent(0);
+ window.scrollBy(0, parseInt(item.offsetHeight, 10));
+ }
+ handle.trigger('focus'); // Regain focus after the DOM manipulation.
+ }
+ break;
+ }
+
+ if (self.rowObject && self.rowObject.changed === true) {
+ $(item).addClass('drag');
+ if (self.oldRowElement) {
+ $(self.oldRowElement).removeClass('drag-previous');
+ }
+ self.oldRowElement = item;
+ self.restripeTable();
+ self.onDrag();
+ }
+
+ // Returning false if we have an arrow key to prevent scrolling.
+ if (keyChange) {
+ return false;
+ }
+ });
+
+ // Compatibility addition, return false on keypress to prevent unwanted scrolling.
+ // IE and Safari will suppress scrolling on keydown, but all other browsers
+ // need to return false on keypress. http://www.quirksmode.org/js/keys.html
+ handle.on('keypress', function (event) {
+ switch (event.keyCode) {
+ case 37: // Left arrow.
+ case 38: // Up arrow.
+ case 39: // Right arrow.
+ case 40: // Down arrow.
+ return false;
+ }
+ });
+};
+
+/**
+ * Pointer event initiator, creates drag object and information.
+ *
+ * @param jQuery.Event event
+ * The event object that trigger the drag.
+ * @param Backdrop.tableDrag self
+ * The drag handle.
+ * @param DOM item
+ * The item that that is being dragged.
+ */
+Backdrop.tableDrag.prototype.dragStart = function (event, self, item) {
+ // Create a new dragObject recording the pointer information.
+ self.dragObject = {};
+ self.dragObject.initOffset = self.getPointerOffset(item, event);
+ self.dragObject.initPointerCoords = self.pointerCoords(event);
+ if (self.indentEnabled) {
+ self.dragObject.indentPointerPos = self.dragObject.initPointerCoords;
+ }
+
+ // If there's a lingering row object from the keyboard, remove its focus.
+ if (self.rowObject) {
+ $(self.rowObject.element).find('a.tabledrag-handle').trigger('blur');
+ }
+
+ // Create a new rowObject for manipulation of this row.
+ self.rowObject = new self.row(item, 'pointer', self.indentEnabled, self.maxDepth, true);
+
+ // Save the position of the table.
+ self.table.topY = $(self.table).offset().top;
+ self.table.bottomY = self.table.topY + self.table.offsetHeight;
+
+ // Add classes to the handle and row.
+ $(item).addClass('drag');
+
+ // Set the document to use the move cursor during drag.
+ $('body').addClass('drag');
+ if (self.oldRowElement) {
+ $(self.oldRowElement).removeClass('drag-previous');
+ }
+};
+
+/**
+ * Pointer movement handler, bound to document.
+ */
+Backdrop.tableDrag.prototype.dragRow = function (event, self) {
+ if (self.dragObject) {
+ self.currentPointerCoords = self.pointerCoords(event);
+ var y = self.currentPointerCoords.y - self.dragObject.initOffset.y;
+ var x = self.currentPointerCoords.x - self.dragObject.initOffset.x;
+
+ // Check for row swapping and vertical scrolling.
+ if (y !== self.oldY) {
+ self.rowObject.direction = y > self.oldY ? 'down' : 'up';
+ self.oldY = y; // Update the old value.
+
+ // Check if the window should be scrolled (and how fast).
+ var scrollAmount = self.checkScroll(self.currentPointerCoords.y);
+ // Stop any current scrolling.
+ clearInterval(self.scrollInterval);
+ // Continue scrolling if the pointer has moved in the scroll direction.
+ if (scrollAmount > 0 && self.rowObject.direction === 'down' || scrollAmount < 0 && self.rowObject.direction === 'up') {
+ self.setScroll(scrollAmount);
+ }
+
+ // If we have a valid target, perform the swap and restripe the table.
+ var currentRow = self.findDropTargetRow(x, y);
+ if (currentRow) {
+ if (self.rowObject.direction === 'down') {
+ self.rowObject.swap('after', currentRow, self);
+ }
+ else {
+ self.rowObject.swap('before', currentRow, self);
+ }
+ self.restripeTable();
+ }
+ }
+
+ // Similar to row swapping, handle indentations.
+ if (self.indentEnabled) {
+ var xDiff = self.currentPointerCoords.x - self.dragObject.indentPointerPos.x;
+ // Set the number of indentations the pointer has been moved left or right.
+ var indentDiff = Math.round(xDiff / self.indentAmount * self.rtl);
+ // Indent the row with our estimated diff, which may be further
+ // restricted according to the rows around this row.
+ var indentChange = self.rowObject.indent(indentDiff);
+ // Update table and pointer indentations.
+ self.dragObject.indentPointerPos.x += self.indentAmount * indentChange * self.rtl;
+ self.indentCount = Math.max(self.indentCount, self.rowObject.indents);
+ }
+
+ return false;
+ }
+};
+
+/**
+ * Pointerup behavior.
+ */
+Backdrop.tableDrag.prototype.dropRow = function (event, self) {
+ var droppedRow, $droppedRow;
+
+ // Drop row functionality.
+ if (self.rowObject !== null) {
+ droppedRow = self.rowObject.element;
+ $droppedRow = $(droppedRow);
+ // The row is already in the right place so we just release it.
+ if (self.rowObject.changed === true) {
+ // Update the fields in the dropped row.
+ self.updateFields(droppedRow);
+
+ // If a setting exists for affecting the entire group, update all the
+ // fields in the entire dragged group.
+ for (var group in self.tableSettings) {
+ if (self.tableSettings.hasOwnProperty(group)) {
+ var rowSettings = self.rowSettings(group, droppedRow);
+ if (rowSettings.relationship === 'group') {
+ for (var n in self.rowObject.children) {
+ if (self.rowObject.children.hasOwnProperty(n)) {
+ self.updateField(self.rowObject.children[n], group);
+ }
+ }
+ }
+ }
+ }
+
+ self.rowObject.markChanged();
+ if (self.changed === false) {
+ $(Backdrop.theme('tableDragChangedWarning')).insertBefore(self.table).hide().fadeIn('slow');
+ self.changed = true;
+ }
+ }
+
+ if (self.indentEnabled) {
+ self.rowObject.removeIndentClasses();
+ }
+ if (self.oldRowElement) {
+ $(self.oldRowElement).removeClass('drag-previous');
+ }
+ $droppedRow.removeClass('drag').addClass('drag-previous');
+ self.oldRowElement = droppedRow;
+ self.onDrop();
+ self.rowObject = null;
+ }
+
+ // Functionality specific only to pointerup events.
+ if (self.dragObject !== null) {
+ $('.tabledrag-handle', droppedRow).removeClass('tabledrag-handle-hover');
+
+ self.dragObject = null;
+ $('body').removeClass('drag');
+ clearInterval(self.scrollInterval);
+ }
+};
+
+
+/**
+ * Get the coordinates from the event (allowing for browser differences).
+ */
+Backdrop.tableDrag.prototype.pointerCoords = function (event) {
+ if (event.pageX || event.pageY) {
+ return { x: event.pageX, y: event.pageY };
+ }
+ return {
+ x: event.clientX + document.body.scrollLeft - document.body.clientLeft,
+ y: event.clientY + document.body.scrollTop - document.body.clientTop
+ };
+};
+
+/**
+ * Given a target element and a pointer event, get the event offset from that
+ * element. To do this we need the element's position and the target position.
+ */
+Backdrop.tableDrag.prototype.getPointerOffset = function (target, event) {
+ var docPos = $(target).offset();
+ var pointerPos = this.pointerCoords(event);
+ return { x: pointerPos.x - docPos.left, y: pointerPos.y - docPos.top };
+};
+
+/**
+ * Find the row the pointer is currently over. This row is then taken and
+ * swapped with the one being dragged.
+ *
+ * @param x
+ * The x coordinate of the pointer on the page (not the screen).
+ * @param y
+ * The y coordinate of the pointer on the page (not the screen).
+ */
+Backdrop.tableDrag.prototype.findDropTargetRow = function (x, y) {
+ var rows = $(this.table.tBodies[0].rows).not(':hidden');
+ for (var n = 0; n < rows.length; n++) {
+ var row = rows[n];
+ var rowY = $(row).offset().top;
+ var rowHeight;
+ // Because Safari does not report offsetHeight on table rows, but does on
+ // table cells, grab the firstChild of the row and use that instead.
+ // http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari.
+ if (row.offsetHeight == 0) {
+ rowHeight = parseInt(row.firstChild.offsetHeight, 10) / 2;
+ }
+ // Other browsers.
+ else {
+ rowHeight = parseInt(row.offsetHeight, 10) / 2;
+ }
+
+ // Because we always insert before, we need to offset the height a bit.
+ if ((y > (rowY - rowHeight)) && (y < (rowY + rowHeight))) {
+ if (this.indentEnabled) {
+ // Check that this row is not a child of the row being dragged.
+ for (n in this.rowObject.group) {
+ if (this.rowObject.group[n] == row) {
+ return null;
+ }
+ }
+ }
+ else {
+ // Do not allow a row to be swapped with itself.
+ if (row == this.rowObject.element) {
+ return null;
+ }
+ }
+
+ // Check that swapping with this row is allowed.
+ if (!this.rowObject.isValidSwap(row)) {
+ return null;
+ }
+
+ // We may have found the row the pointer just passed over, but it doesn't
+ // take into account hidden rows. Skip backwards until we find a draggable
+ // row.
+ while ($(row).is(':hidden') && $(row).prev('tr').is(':hidden')) {
+ row = $(row).prev('tr').get(0);
+ }
+ return row;
+ }
+ }
+ return null;
+};
+
+/**
+ * After the row is dropped, update the table fields according to the settings
+ * set for this table.
+ *
+ * @param changedRow
+ * DOM object for the row that was just dropped.
+ */
+Backdrop.tableDrag.prototype.updateFields = function (changedRow) {
+ for (var group in this.tableSettings) {
+ if (this.tableSettings.hasOwnProperty(group)) {
+ // Each group may have a different setting for relationship, so we find
+ // the source rows for each separately.
+ this.updateField(changedRow, group);
+ }
+ }
+};
+
+/**
+ * After the row is dropped, update a single table field according to specific
+ * settings.
+ *
+ * @param changedRow
+ * DOM object for the row that was just dropped.
+ * @param group
+ * The settings group on which field updates will occur.
+ */
+Backdrop.tableDrag.prototype.updateField = function (changedRow, group) {
+ var rowSettings = this.rowSettings(group, changedRow);
+ var sourceRow, nextRow, previousRow, useSibling;
+
+ // Set the row as its own target.
+ if (rowSettings.relationship == 'self' || rowSettings.relationship == 'group') {
+ sourceRow = changedRow;
+ }
+ // Siblings are easy, check previous and next rows.
+ else if (rowSettings.relationship == 'sibling') {
+ previousRow = $(changedRow).prev('tr').get(0);
+ nextRow = $(changedRow).next('tr').get(0);
+ sourceRow = changedRow;
+ if ($(previousRow).is('.draggable') && $('.' + group, previousRow).length) {
+ if (this.indentEnabled) {
+ if ($('.indentations', previousRow).length == $('.indentations', changedRow)) {
+ sourceRow = previousRow;
+ }
+ }
+ else {
+ sourceRow = previousRow;
+ }
+ }
+ else if ($(nextRow).is('.draggable') && $('.' + group, nextRow).length) {
+ if (this.indentEnabled) {
+ if ($('.indentations', nextRow).length == $('.indentations', changedRow)) {
+ sourceRow = nextRow;
+ }
+ }
+ else {
+ sourceRow = nextRow;
+ }
+ }
+ }
+ // Parents, look up the tree until we find a field not in this group.
+ // Go up as many parents as indentations in the changed row.
+ else if (rowSettings.relationship == 'parent') {
+ previousRow = $(changedRow).prev('tr');
+ while (previousRow.length && $('.indentation', previousRow).length >= this.rowObject.indents) {
+ previousRow = previousRow.prev('tr');
+ }
+ // If we found a row.
+ if (previousRow.length) {
+ sourceRow = previousRow[0];
+ }
+ // Otherwise we went all the way to the left of the table without finding
+ // a parent, meaning this item has been placed at the root level.
+ else {
+ // Use the first row in the table as source, because it's guaranteed to
+ // be at the root level. Find the first item, then compare this row
+ // against it as a sibling.
+ sourceRow = $(this.table).find('tr.draggable:first').get(0);
+ if (sourceRow == this.rowObject.element) {
+ sourceRow = $(this.rowObject.group[this.rowObject.group.length - 1]).next('tr.draggable').get(0);
+ }
+ useSibling = true;
+ }
+ }
+
+ // Because we may have moved the row from one category to another,
+ // take a look at our sibling and borrow its sources and targets.
+ this.copyDragClasses(sourceRow, changedRow, group);
+ rowSettings = this.rowSettings(group, changedRow);
+
+ // In the case that we're looking for a parent, but the row is at the top
+ // of the tree, copy our sibling's values.
+ if (useSibling) {
+ rowSettings.relationship = 'sibling';
+ rowSettings.source = rowSettings.target;
+ }
+
+ var targetClass = '.' + rowSettings.target;
+ var targetElement = $(targetClass, changedRow).get(0);
+
+ // Check if a target element exists in this row.
+ if (targetElement) {
+ var sourceClass = '.' + rowSettings.source;
+ var sourceElement = $(sourceClass, sourceRow).get(0);
+ switch (rowSettings.action) {
+ case 'depth':
+ // Get the depth of the target row.
+ targetElement.value = $('.indentation', $(sourceElement).closest('tr')).length;
+ break;
+ case 'match':
+ // Update the value.
+ targetElement.value = sourceElement.value;
+ break;
+ case 'order':
+ var siblings = this.rowObject.findSiblings(rowSettings);
+ if ($(targetElement).is('select')) {
+ // Get a list of acceptable values.
+ var values = [];
+ $('option', targetElement).each(function () {
+ values.push(this.value);
+ });
+ var maxVal = values[values.length - 1];
+ // Populate the values in the siblings.
+ $(targetClass, siblings).each(function () {
+ // If there are more items than possible values, assign the maximum value to the row.
+ if (values.length > 0) {
+ this.value = values.shift();
+ }
+ else {
+ this.value = maxVal;
+ }
+ });
+ }
+ else {
+ // Assume a numeric input field.
+ var weight = parseInt($(targetClass, siblings[0]).val(), 10) || 0;
+ $(targetClass, siblings).each(function () {
+ this.value = weight;
+ weight++;
+ });
+ }
+ break;
+ }
+ }
+};
+
+/**
+ * Copy all special tableDrag classes from one row's form elements to a
+ * different one, removing any special classes that the destination row
+ * may have had.
+ */
+Backdrop.tableDrag.prototype.copyDragClasses = function (sourceRow, targetRow, group) {
+ var sourceElement = $('.' + group, sourceRow);
+ var targetElement = $('.' + group, targetRow);
+ if (sourceElement.length && targetElement.length) {
+ targetElement[0].className = sourceElement[0].className;
+ }
+};
+
+Backdrop.tableDrag.prototype.checkScroll = function (cursorY) {
+ var de = document.documentElement;
+ var b = document.body;
+
+ var windowHeight = this.windowHeight = window.innerHeight || (de.clientHeight && de.clientWidth != 0 ? de.clientHeight : b.offsetHeight);
+ var scrollY = this.scrollY = (document.all ? (!de.scrollTop ? b.scrollTop : de.scrollTop) : (window.pageYOffset ? window.pageYOffset : window.scrollY));
+ var trigger = this.scrollSettings.trigger;
+ var delta = 0;
+
+ // Return a scroll speed relative to the edge of the screen.
+ if (cursorY - scrollY > windowHeight - trigger) {
+ delta = trigger / (windowHeight + scrollY - cursorY);
+ delta = (delta > 0 && delta < trigger) ? delta : trigger;
+ return delta * this.scrollSettings.amount;
+ }
+ else if (cursorY - scrollY < trigger) {
+ delta = trigger / (cursorY - scrollY);
+ delta = (delta > 0 && delta < trigger) ? delta : trigger;
+ return -delta * this.scrollSettings.amount;
+ }
+};
+
+Backdrop.tableDrag.prototype.setScroll = function (scrollAmount) {
+ var self = this;
+
+ this.scrollInterval = setInterval(function () {
+ // Update the scroll values stored in the object.
+ self.checkScroll(self.currentpointerCoords.y);
+ var aboveTable = self.scrollY > self.table.topY;
+ var belowTable = self.scrollY + self.windowHeight < self.table.bottomY;
+ if (scrollAmount > 0 && belowTable || scrollAmount < 0 && aboveTable) {
+ window.scrollBy(0, scrollAmount);
+ }
+ }, this.scrollSettings.interval);
+};
+
+Backdrop.tableDrag.prototype.restripeTable = function () {
+ // :even and :odd are reversed because jQuery counts from 0 and
+ // we count from 1, so we're out of sync.
+ // Match immediate children of the parent element to allow nesting.
+ $('> tbody > tr.draggable:visible, > tr.draggable:visible', this.table)
+ .removeClass('odd even')
+ .filter(':odd').addClass('even').end()
+ .filter(':even').addClass('odd');
+};
+
+/**
+ * Stub function. Allows a custom handler when a row begins dragging.
+ */
+Backdrop.tableDrag.prototype.onDrag = function () {
+ return null;
+};
+
+/**
+ * Stub function. Allows a custom handler when a row is dropped.
+ */
+Backdrop.tableDrag.prototype.onDrop = function () {
+ return null;
+};
+
+/**
+ * Constructor to make a new object to manipulate a table row.
+ *
+ * @param tableRow
+ * The DOM element for the table row we will be manipulating.
+ * @param method
+ * The method in which this row is being moved. Either 'keyboard' or 'pointer'.
+ * @param indentEnabled
+ * Whether the containing table uses indentations. Used for optimizations.
+ * @param maxDepth
+ * The maximum amount of indentations this row may contain.
+ * @param addClasses
+ * Whether we want to add classes to this row to indicate child relationships.
+ */
+Backdrop.tableDrag.prototype.row = function (tableRow, method, indentEnabled, maxDepth, addClasses) {
+ var $tableRow = $(tableRow)
+ this.element = tableRow;
+ this.method = method;
+ this.group = [tableRow];
+ this.groupDepth = $tableRow.find('.indentation').length;
+ this.changed = tableRow;
+ this.table = $tableRow.closest('table')[0];
+ this.indentEnabled = indentEnabled;
+ this.maxDepth = maxDepth;
+ this.direction = ''; // Direction the row is being moved.
+
+ if (this.indentEnabled) {
+ this.indents = this.groupDepth;
+ this.children = this.findChildren(addClasses);
+ this.group = $.merge(this.group, this.children);
+ // Find the depth of this entire group.
+ for (var n = 0; n < this.group.length; n++) {
+ this.groupDepth = Math.max($('.indentation', this.group[n]).length, this.groupDepth);
+ }
+ }
+};
+
+/**
+ * Find all children of rowObject by indentation.
+ *
+ * @param addClasses
+ * Whether we want to add classes to this row to indicate child relationships.
+ */
+Backdrop.tableDrag.prototype.row.prototype.findChildren = function (addClasses) {
+ var parentIndentation = this.indents;
+ var $currentRow = $(this.element, this.table).next('tr.draggable');
+ var rows = [];
+ var child = 0;
+ var rowIndentation;
+ while ($currentRow.length) {
+ rowIndentation = $currentRow.find('.indentation').length;
+ // A greater indentation indicates this is a child.
+ if (rowIndentation > parentIndentation) {
+ child++;
+ rows.push($currentRow[0]);
+ if (addClasses) {
+ $currentRow.find('.indentation').each(function (indentNum) {
+ if (child == 1 && (indentNum == parentIndentation)) {
+ $(this).addClass('tree-child-first');
+ }
+ if (indentNum == parentIndentation) {
+ $(this).addClass('tree-child');
+ }
+ else if (indentNum > parentIndentation) {
+ $(this).addClass('tree-child-horizontal');
+ }
+ });
+ }
+ }
+ else {
+ break;
+ }
+ $currentRow = $currentRow.next('tr.draggable');
+ }
+ if (addClasses && rows.length) {
+ $('.indentation:nth-child(' + (parentIndentation + 1) + ')', rows[rows.length - 1]).addClass('tree-child-last');
+ }
+ return rows;
+};
+
+/**
+ * Ensure that two rows are allowed to be swapped.
+ *
+ * @param row
+ * DOM object for the row being considered for swapping.
+ */
+Backdrop.tableDrag.prototype.row.prototype.isValidSwap = function (row) {
+ if (this.indentEnabled) {
+ var prevRow, nextRow;
+ if (this.direction == 'down') {
+ prevRow = row;
+ nextRow = $(row).next('tr').get(0);
+ }
+ else {
+ prevRow = $(row).prev('tr').get(0);
+ nextRow = row;
+ }
+ this.interval = this.validIndentInterval(prevRow, nextRow);
+
+ // We have an invalid swap if the valid indentations interval is empty.
+ if (this.interval.min > this.interval.max) {
+ return false;
+ }
+ }
+
+ // Do not let an un-draggable first row have anything put before it.
+ if (this.table.tBodies[0].rows[0] == row && $(row).is(':not(.draggable)')) {
+ return false;
+ }
+
+ return true;
+};
+
+/**
+ * Perform the swap between two rows.
+ *
+ * @param position
+ * Whether the swap will occur 'before' or 'after' the given row.
+ * @param row
+ * DOM element what will be swapped with the row group.
+ */
+Backdrop.tableDrag.prototype.row.prototype.swap = function (position, row) {
+ Backdrop.detachBehaviors(this.group, Backdrop.settings, 'move');
+ $(row)[position](this.group);
+ Backdrop.attachBehaviors(this.group, Backdrop.settings);
+ this.changed = true;
+ this.onSwap(row);
+};
+
+/**
+ * Determine the valid indentations interval for the row at a given position
+ * in the table.
+ *
+ * @param prevRow
+ * DOM object for the row before the tested position
+ * (or null for first position in the table).
+ * @param nextRow
+ * DOM object for the row after the tested position
+ * (or null for last position in the table).
+ */
+Backdrop.tableDrag.prototype.row.prototype.validIndentInterval = function (prevRow, nextRow) {
+ var minIndent, maxIndent;
+
+ // Minimum indentation:
+ // Do not orphan the next row.
+ minIndent = nextRow ? $('.indentation', nextRow).length : 0;
+
+ // Maximum indentation:
+ if (!prevRow || $(prevRow).is(':not(.draggable)') || $(this.element).is('.tabledrag-root')) {
+ // Do not indent:
+ // - the first row in the table,
+ // - rows dragged below a non-draggable row,
+ // - 'root' rows.
+ maxIndent = 0;
+ }
+ else {
+ // Do not go deeper than as a child of the previous row.
+ maxIndent = $('.indentation', prevRow).length + ($(prevRow).is('.tabledrag-leaf') ? 0 : 1);
+ // Limit by the maximum allowed depth for the table.
+ if (this.maxDepth) {
+ maxIndent = Math.min(maxIndent, this.maxDepth - (this.groupDepth - this.indents));
+ }
+ }
+
+ return { 'min': minIndent, 'max': maxIndent };
+};
+
+/**
+ * Indent a row within the legal bounds of the table.
+ *
+ * @param indentDiff
+ * The number of additional indentations proposed for the row (can be
+ * positive or negative). This number will be adjusted to nearest valid
+ * indentation level for the row.
+ */
+Backdrop.tableDrag.prototype.row.prototype.indent = function (indentDiff) {
+ // Determine the valid indentations interval if not available yet.
+ if (!this.interval) {
+ var prevRow = $(this.element).prev('tr').get(0);
+ var nextRow = $(this.group).filter(':last').next('tr').get(0);
+ this.interval = this.validIndentInterval(prevRow, nextRow);
+ }
+
+ // Adjust to the nearest valid indentation.
+ var indent = this.indents + indentDiff;
+ indent = Math.max(indent, this.interval.min);
+ indent = Math.min(indent, this.interval.max);
+ indentDiff = indent - this.indents;
+
+ for (var n = 1; n <= Math.abs(indentDiff); n++) {
+ // Add or remove indentations.
+ if (indentDiff < 0) {
+ $('.indentation:first', this.group).remove();
+ this.indents--;
+ }
+ else {
+ $('td:first', this.group).prepend(Backdrop.theme('tableDragIndentation'));
+ this.indents++;
+ }
+ }
+ if (indentDiff) {
+ // Update indentation for this row.
+ this.changed = true;
+ this.groupDepth += indentDiff;
+ this.onIndent();
+ }
+
+ return indentDiff;
+};
+
+/**
+ * Find all siblings for a row, either according to its subgroup or indentation.
+ * Note that the passed-in row is included in the list of siblings.
+ *
+ * @param settings
+ * The field settings we're using to identify what constitutes a sibling.
+ */
+Backdrop.tableDrag.prototype.row.prototype.findSiblings = function (rowSettings) {
+ var siblings = [];
+ var directions = ['prev', 'next'];
+ var rowIndentation = this.indents;
+ var checkRowIndentation;
+ for (var d = 0; d < directions.length; d++) {
+ var checkRow = $(this.element)[directions[d]]();
+ while (checkRow.length) {
+ // Check that the sibling contains a similar target field.
+ if ($('.' + rowSettings.target, checkRow)) {
+ // Either add immediately if this is a flat table, or check to ensure
+ // that this row has the same level of indentation.
+ if (this.indentEnabled) {
+ checkRowIndentation = checkRow.find('.indentation').length;
+ }
+
+ if (!(this.indentEnabled) || (checkRowIndentation == rowIndentation)) {
+ siblings.push(checkRow[0]);
+ }
+ else if (checkRowIndentation < rowIndentation) {
+ // No need to keep looking for siblings when we get to a parent.
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ checkRow = $(checkRow)[directions[d]]();
+ }
+ // Since siblings are added in reverse order for previous, reverse the
+ // completed list of previous siblings. Add the current row and continue.
+ if (directions[d] == 'prev') {
+ siblings.reverse();
+ siblings.push(this.element);
+ }
+ }
+ return siblings;
+};
+
+/**
+ * Remove indentation helper classes from the current row group.
+ */
+Backdrop.tableDrag.prototype.row.prototype.removeIndentClasses = function () {
+ for (var n in this.children) {
+ if (this.children.hasOwnProperty(n)) {
+ $(this.children[n]).find('.indentation')
+ .removeClass('tree-child')
+ .removeClass('tree-child-first')
+ .removeClass('tree-child-last')
+ .removeClass('tree-child-horizontal');
+ }
+ }
+};
+
+/**
+ * Add an asterisk or other marker to the changed row.
+ */
+Backdrop.tableDrag.prototype.row.prototype.markChanged = function () {
+ var marker = Backdrop.theme('tableDragChangedMarker');
+ var cell = $('td:first', this.element);
+ if ($('abbr.tabledrag-changed', cell).length == 0) {
+ cell.append(marker);
+ }
+};
+
+/**
+ * Stub function. Allows a custom handler when a row is indented.
+ */
+Backdrop.tableDrag.prototype.row.prototype.onIndent = function () {
+ return null;
+};
+
+/**
+ * Stub function. Allows a custom handler when a row is swapped.
+ */
+Backdrop.tableDrag.prototype.row.prototype.onSwap = function (swappedRow) {
+ return null;
+};
+
+Backdrop.theme.prototype.tableDragChangedMarker = function () {
+ return '*';
+};
+
+Backdrop.theme.prototype.tableDragIndentation = function () {
+ return '
' + Backdrop.theme('tableDragChangedMarker') + ' ' + Backdrop.t('Changes made in this table will not be saved until the form is submitted.') + '
';
+};
+
+})(jQuery);
diff --git a/core/misc/tableheader.js b/core/misc/tableheader.js
old mode 100644
new mode 100755
index c565615..c508b41
--- a/core/misc/tableheader.js
+++ b/core/misc/tableheader.js
@@ -1,117 +1,265 @@
(function ($) {
+"use strict";
+
/**
* Attaches sticky table headers.
*/
-Drupal.behaviors.tableHeader = {
- attach: function (context, settings) {
+Backdrop.behaviors.tableHeader = {
+ attach: function (context) {
if (!$.support.positionFixed) {
return;
}
-
- $('table.sticky-enabled', context).once('tableheader', function () {
- $(this).data("drupal-tableheader", new Drupal.tableHeader(this));
- });
+ var $tables = $(context).find('table.sticky-enabled:not(.sticky-processed)').addClass('sticky-processed');
+ for (var i = 0, il = $tables.length; i < il; i++) {
+ TableHeader.tables.push(new TableHeader($tables[i]));
+ }
}
};
+function scrollValue(position) {
+ return document.documentElement[position] || document.body[position];
+}
+
+// Helper method to loop through tables and execute a method.
+function forTables(method, arg) {
+ var tables = TableHeader.tables;
+ for (var i = 0, il = tables.length; i < il; i++) {
+ tables[i][method](arg);
+ }
+}
+
+var resizeTimer;
+function tableHeaderResizeHandler(e) {
+ clearTimeout(resizeTimer);
+ resizeTimer = setTimeout(function() {
+ forTables('recalculateSticky');
+ }, 50);
+}
+
+var scrollTimer;
+function tableHeaderOnScrollHandler(e) {
+ clearTimeout(scrollTimer);
+ scrollTimer = setTimeout(function() {
+ forTables('onScroll');
+ }, 50);
+}
+
+function tableHeaderOffsetChangeHandler(e) {
+ // Compute the new offset value.
+ TableHeader.computeOffsetTop();
+ forTables('stickyPosition', TableHeader.offsetTop);
+}
+
+// Bind event that need to change all tables.
+$(window).on({
+ /**
+ * When resizing table width and offset top can change, recalculate everything.
+ */
+ 'resize.TableHeader': tableHeaderResizeHandler,
+
+ /**
+ * Bind only one event to take care of calling all scroll callbacks.
+ */
+ 'scroll.TableHeader': tableHeaderOnScrollHandler
+});
+
+// Bind to custom Backdrop events.
+$(document).on({
+ /**
+ * Recalculate columns width when window is resized and when show/hide
+ * weight is triggered.
+ */
+ 'columnschange.TableHeader': tableHeaderResizeHandler,
+
+ /**
+ * Offset value vas changed by a third party script.
+ */
+ 'offsettopchange.TableHeader': tableHeaderOffsetChangeHandler
+});
+
/**
* Constructor for the tableHeader object. Provides sticky table headers.
+ * TableHeader will make the current table header stick to the top of the page
+ * if the table is very long.
+ *
+ * Fire a custom "topoffsetchange" event to make TableHeader compute the
+ * new offset value from the "data-offset-top" attributes of relevant elements.
*
* @param table
- * DOM object for the table to add a sticky header to.
+ * DOM object for the table to add a sticky header to.*
+ * @constructor
*/
-Drupal.tableHeader = function (table) {
- var self = this;
-
- this.originalTable = $(table);
- this.originalHeader = $(table).children('thead');
- this.originalHeaderCells = this.originalHeader.find('> tr > th');
+function TableHeader(table) {
+ var $table = $(table);
+ this.$originalTable = $table;
+ this.$originalHeader = $table.children('thead');
+ this.$originalHeaderCells = this.$originalHeader.find('> tr > th');
this.displayWeight = null;
+ this.$originalTable.addClass('sticky-table');
+ this.tableHeight = $table[0].clientHeight;
+ this.tableOffset = this.$originalTable.offset();
+
// React to columns change to avoid making checks in the scroll callback.
- this.originalTable.bind('columnschange', function (e, display) {
- // This will force header size to be calculated on scroll.
- self.widthCalculated = (self.displayWeight !== null && self.displayWeight === display);
- self.displayWeight = display;
+ this.$originalTable.bind('columnschange', {tableHeader: this}, function (e, display) {
+ var tableHeader = e.data.tableHeader;
+ if (tableHeader.displayWeight === null || tableHeader.displayWeight !== display) {
+ tableHeader.recalculateSticky();
+ }
+ tableHeader.displayWeight = display;
});
- // Clone the table header so it inherits original jQuery properties. Hide
- // the table to avoid a flash of the header clone upon page load.
- this.stickyTable = $('
')
- .insertBefore(this.originalTable)
- .css({ position: 'fixed', top: '0px' });
- this.stickyHeader = this.originalHeader.clone(true)
- .hide()
- .appendTo(this.stickyTable);
- this.stickyHeaderCells = this.stickyHeader.find('> tr > th');
-
- this.originalTable.addClass('sticky-table');
- $(window)
- .bind('scroll.drupal-tableheader', $.proxy(this, 'eventhandlerRecalculateStickyHeader'))
- .bind('resize.drupal-tableheader', { calculateWidth: true }, $.proxy(this, 'eventhandlerRecalculateStickyHeader'))
- // Make sure the anchor being scrolled into view is not hidden beneath the
- // sticky table header. Adjust the scrollTop if it does.
- .bind('drupalDisplaceAnchor.drupal-tableheader', function () {
- window.scrollBy(0, -self.stickyTable.outerHeight());
- })
- // Make sure the element being focused is not hidden beneath the sticky
- // table header. Adjust the scrollTop if it does.
- .bind('drupalDisplaceFocus.drupal-tableheader', function (event) {
- if (self.stickyVisible && event.clientY < (self.stickyOffsetTop + self.stickyTable.outerHeight()) && event.$target.closest('sticky-header').length === 0) {
- window.scrollBy(0, -self.stickyTable.outerHeight());
- }
- })
- .triggerHandler('resize.drupal-tableheader');
-
- // We hid the header to avoid it showing up erroneously on page load;
- // we need to unhide it now so that it will show up when expected.
- this.stickyHeader.show();
-};
+// Create and display sticky header.
+ this.createSticky();
+ }
/**
- * Event handler: recalculates position of the sticky table header.
- *
- * @param event
- * Event being triggered.
+ * Store the state of TableHeader.
*/
-Drupal.tableHeader.prototype.eventhandlerRecalculateStickyHeader = function (event) {
- var self = this;
- var calculateWidth = event.data && event.data.calculateWidth;
-
- // Reset top position of sticky table headers to the current top offset.
- this.stickyOffsetTop = Drupal.settings.tableHeaderOffset ? eval(Drupal.settings.tableHeaderOffset + '()') : 0;
- this.stickyTable.css('top', this.stickyOffsetTop + 'px');
-
- // Save positioning data.
- var viewHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
- if (calculateWidth || this.viewHeight !== viewHeight) {
- this.viewHeight = viewHeight;
- this.vPosition = this.originalTable.offset().top - 4 - this.stickyOffsetTop;
- this.hPosition = this.originalTable.offset().left;
- this.vLength = this.originalTable[0].clientHeight - 100;
- calculateWidth = true;
+$.extend(TableHeader, {
+ /**
+ * This will store the state of all processed tables.
+ *
+ * @type {Array}
+ */
+ tables: [],
+
+ /**
+ * Cache of computed offset value.
+ *
+ * @type {Number}
+ */
+ offsetTop: 0,
+
+ /**
+ * Sum all [data-offset-top] values and cache it.
+ */
+ computeOffsetTop: function () {
+ var $offsets = $('[data-offset-top]').not('.sticky-header');
+ var value, sum = 0;
+ for (var i = 0, il = $offsets.length; i < il; i++) {
+ value = parseInt($offsets[i].getAttribute('data-offset-top'), 10);
+ sum += !isNaN(value) ? value : 0;
+ }
+ this.offsetTop = sum;
+ return sum;
}
+});
- // Track horizontal positioning relative to the viewport and set visibility.
- var hScroll = document.documentElement.scrollLeft || document.body.scrollLeft;
- var vOffset = (document.documentElement.scrollTop || document.body.scrollTop) - this.vPosition;
- this.stickyVisible = vOffset > 0 && vOffset < this.vLength;
- this.stickyTable.css({ left: (-hScroll + this.hPosition) + 'px', visibility: this.stickyVisible ? 'visible' : 'hidden' });
+/**
+ * Extend TableHeader prototype.
+ */
+$.extend(TableHeader.prototype, {
+ /**
+ * Minimum height in pixels for the table to have a sticky header.
+ */
+ minHeight: 100,
+
+ /**
+ * Absolute position of the table on the page.
+ */
+ tableOffset: null,
+
+ /**
+ * Absolute position of the table on the page.
+ */
+ tableHeight: null,
+
+ /**
+ * Boolean storing the sticky header visibility state.
+ */
+ stickyVisible: false,
+
+ /**
+ * Create the duplicate header.
+ */
+ createSticky: function () {
+ // Clone the table header so it inherits original jQuery properties.
+ var $stickyHeader = this.$originalHeader.clone(true);
+ // Hide the table to avoid a flash of the header clone upon page load.
+ this.$stickyTable = $('
')
+ .css({
+ visibility: 'hidden',
+ position: 'fixed',
+ top: '0px'
+ })
+ .append($stickyHeader)
+ .insertBefore(this.$originalTable);
+
+ this.$stickyHeaderCells = $stickyHeader.find('> tr > th');
+
+ // Initialize all computations.
+ this.recalculateSticky();
+ },
+
+ /**
+ * Set absolute position of sticky.
+ *
+ * @param offsetTop
+ * @param offsetLeft
+ */
+ stickyPosition: function (offsetTop, offsetLeft) {
+ var css = {};
+ if (!isNaN(offsetTop) && offsetTop !== null) {
+ css.top = offsetTop + 'px';
+ }
+ if (!isNaN(offsetLeft) && offsetTop !== null) {
+ css.left = (this.tableOffset.left - offsetLeft) + 'px';
+ }
+ return this.$stickyTable.css(css);
+ },
- // Only perform expensive calculations if the sticky header is actually
- // visible or when forced.
- if (this.stickyVisible && (calculateWidth || !this.widthCalculated)) {
- this.widthCalculated = true;
+ /**
+ * Returns true if sticky is currently visible.
+ */
+ checkStickyVisible: function () {
+ var scrollTop = scrollValue('scrollTop');
+ var tableTop = this.tableOffset.top - TableHeader.offsetTop;
+ var tableBottom = tableTop + this.tableHeight;
+ var visible = false;
+
+ if (tableTop < scrollTop && scrollTop < (tableBottom - this.minHeight)) {
+ visible = true;
+ }
+
+ this.stickyVisible = visible;
+ return visible;
+ },
+
+ /**
+ * Check if sticky header should be displayed.
+ *
+ * This function is throttled to once every 250ms to avoid unnecessary calls.
+ */
+ onScroll: function (e) {
+ // Track horizontal positioning relative to the viewport.
+ this.stickyPosition(null, scrollValue('scrollLeft'));
+ this.$stickyTable.css('visibility', this.checkStickyVisible() ? 'visible' : 'hidden');
+ },
+
+ /**
+ * Event handler: recalculates position of the sticky table header.
+ */
+ recalculateSticky: function (e) {
+ // Update table size.
+ this.tableHeight = this.$originalTable[0].clientHeight;
+
+ // Update offset top.
+ TableHeader.computeOffsetTop();
+ this.tableOffset = this.$originalTable.offset();
+ this.stickyPosition(TableHeader.offsetTop);
+
+ // Update columns width.
var $that = null;
var $stickyCell = null;
var display = null;
// Resize header and its cell widths.
// Only apply width to visible table cells. This prevents the header from
// displaying incorrectly when the sticky header is no longer visible.
- for (var i = 0, il = this.originalHeaderCells.length; i < il; i += 1) {
- $that = $(this.originalHeaderCells[i]);
- $stickyCell = this.stickyHeaderCells.eq($that.index());
+ for (var i = 0, il = this.$originalHeaderCells.length; i < il; i++) {
+ $that = $(this.$originalHeaderCells[i]);
+ $stickyCell = this.$stickyHeaderCells.eq($that.index());
display = $that.css('display');
if (display !== 'none') {
$stickyCell.css({'width': $that.css('width'), 'display': display});
@@ -120,8 +268,16 @@ Drupal.tableHeader.prototype.eventhandlerRecalculateStickyHeader = function (eve
$stickyCell.css('display', 'none');
}
}
- this.stickyTable.css('width', this.originalTable.outerWidth());
+ this.$stickyTable.css('width', this.$originalTable.outerWidth());
}
-};
+});
+
+// Calculate the table header positions on page load.
+window.setTimeout(function() {
+ $(window).triggerHandler('scroll.TableHeader');
+}, 100);
+
+// Expose constructor in the public space.
+Backdrop.TableHeader = TableHeader;
-})(jQuery);
+}(jQuery));
diff --git a/core/misc/tableresponsive.js b/core/misc/tableresponsive.js
new file mode 100755
index 0000000..2cc5dff
--- /dev/null
+++ b/core/misc/tableresponsive.js
@@ -0,0 +1,120 @@
+(function ($) {
+
+"use strict";
+
+/**
+ * Attach the tableResponsive function to Backdrop.behaviors.
+ */
+Backdrop.behaviors.tableResponsive = {
+ attach: function (context, settings) {
+ var $tables = $(context).find('table.responsive-enabled').once('tableresponsive');
+ if ($tables.length) {
+ for (var i = 0, il = $tables.length; i < il; i++) {
+ TableResponsive.tables.push(new TableResponsive($tables[i]));
+ }
+ }
+ }
+};
+
+/**
+ * The TableResponsive object optimizes table presentation for all screen sizes.
+ *
+ * A responsive table hides columns at small screen sizes, leaving the most
+ * important columns visible to the end user. Users should not be prevented from
+ * accessing all columns, however. This class adds a toggle to a table with
+ * hidden columns that exposes the columns. Exposing the columns will likely
+ * break layouts, but it provides the user with a means to access data, which
+ * is a guiding principle of responsive design.
+ */
+function TableResponsive (table) {
+ this.table = table;
+ this.$table = $(table);
+ this.showText = Backdrop.t('Show all columns');
+ this.hideText = Backdrop.t('Hide less important columns');
+ // Store a reference to the header elements of the table so that the DOM is
+ // traversed only once to find them.
+ this.$headers = this.$table.find('th');
+ // Add a link before the table for users to show or hide weight columns.
+ this.$link = $('')
+ .attr({
+ 'title': Backdrop.t('Show table cells that were hidden to make the table fit within a small screen.')
+ })
+ .on('click', $.proxy(this, 'eventhandlerToggleColumns'));
+
+ this.$table.before($('').append(this.$link));
+
+ // Attach a resize handler to the window.
+ $(window)
+ .on('resize.tableresponsive', $.proxy(this, 'eventhandlerEvaluateColumnVisibility'))
+ .trigger('resize.tableresponsive');
+}
+
+/**
+ * Extend the TableResponsive function with a list of managed tables.
+ */
+$.extend(TableResponsive, {
+ tables: []
+});
+
+/**
+ * Associates an action link with the table that will show hidden columns.
+ *
+ * Columns are assumed to be hidden if their header has the class priority-low
+ * or priority-medium.
+ */
+$.extend(TableResponsive.prototype, {
+ eventhandlerEvaluateColumnVisibility: function (e) {
+ var pegged = parseInt(this.$link.data('pegged'), 10);
+ var hiddenLength = this.$headers.filter('.priority-medium:hidden, .priority-low:hidden').length;
+ // If the table has hidden columns, associate an action link with the table
+ // to show the columns.
+ if (hiddenLength > 0) {
+ this.$link.show().text(this.showText);
+ }
+ // When the toggle is pegged, its presence is maintained because the user
+ // has interacted with it. This is necessary to keep the link visible if the
+ // user adjusts screen size and changes the visibilty of columns.
+ if (!pegged && hiddenLength === 0) {
+ this.$link.hide().text(this.hideText);
+ }
+ },
+ // Toggle the visibility of columns classed with either 'priority-low' or
+ // 'priority-medium'.
+ eventhandlerToggleColumns: function (e) {
+ e.preventDefault();
+ var self = this;
+ var $hiddenHeaders = this.$headers.filter('.priority-medium:hidden, .priority-low:hidden');
+ this.$revealedCells = this.$revealedCells || $();
+ // Reveal hidden columns.
+ if ($hiddenHeaders.length > 0) {
+ $hiddenHeaders.each(function (index, element) {
+ var $header = $(this);
+ var position = $header.prevAll('th').length;
+ self.$table.find('tbody tr').each(function () {
+ var $cells = $(this).find('td:eq(' + position + ')');
+ $cells.show();
+ // Keep track of the revealed cells, so they can be hidden later.
+ self.$revealedCells = $().add(self.$revealedCells).add($cells);
+ });
+ $header.show();
+ // Keep track of the revealed headers, so they can be hidden later.
+ self.$revealedCells = $().add(self.$revealedCells).add($header);
+ });
+ this.$link.text(this.hideText).data('pegged', 1);
+ }
+ // Hide revealed columns.
+ else {
+ this.$revealedCells.hide();
+ // Strip the 'display:none' declaration from the style attributes of
+ // the table cells that .hide() added.
+ this.$revealedCells.css('display', '');
+ this.$link.text(this.showText).data('pegged', 0);
+ // Refresh the toggle link.
+ $(window).trigger('resize.tableresponsive');
+ }
+ }
+});
+// Make the TableResponsive object available in the Backdrop namespace.
+Backdrop.TableResponsive = TableResponsive;
+
+})(jQuery);
diff --git a/core/misc/tableselect.js b/core/misc/tableselect.js
new file mode 100755
index 0000000..ca6eadc
--- /dev/null
+++ b/core/misc/tableselect.js
@@ -0,0 +1,91 @@
+(function ($) {
+
+Backdrop.behaviors.tableSelect = {
+ attach: function (context, settings) {
+ // Select the inner-most table in case of nested tables.
+ $('th.select-all', context).closest('table').once('table-select', Backdrop.tableSelect);
+ }
+};
+
+Backdrop.tableSelect = function () {
+ // Do not add a "Select all" checkbox if there are no rows with checkboxes in the table
+ if ($(this).find('td input[type="checkbox"]').length == 0) {
+ return;
+ }
+
+ // Keep track of the table, which checkbox is checked and alias the settings.
+ var table = this, checkboxes, lastChecked;
+ var strings = { 'selectAll': Backdrop.t('Select all rows in this table'), 'selectNone': Backdrop.t('Deselect all rows in this table') };
+ var updateSelectAll = function (state) {
+ // Update table's select-all checkbox (and sticky header's if available).
+ $(table).prev('table.sticky-header').addBack().find('th.select-all input[type="checkbox"]').each(function() {
+ $(this).attr('title', state ? strings.selectNone : strings.selectAll);
+ this.checked = state;
+ });
+ };
+
+ // Find all