Anfänglicher Commit: Producion Version Stand: Oktober 2014
This commit is contained in:
157
htdocs/grappelli/js/grappelli.js
Normal file
157
htdocs/grappelli/js/grappelli.js
Normal file
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* GRAPPELLI UTILS
|
||||
* functions needed for Grappelli
|
||||
*/
|
||||
|
||||
// grp jQuery namespace
|
||||
var grp = {
|
||||
"jQuery": jQuery.noConflict(true)
|
||||
};
|
||||
|
||||
// django jQuery namespace
|
||||
var django = {
|
||||
"jQuery": grp.jQuery.noConflict(true)
|
||||
};
|
||||
|
||||
// var jQuery = grp.jQuery.noConflict(true);
|
||||
|
||||
(function($) {
|
||||
|
||||
// dateformat
|
||||
grappelli.getFormat = function(type) {
|
||||
if (type == "date") {
|
||||
var format = DATE_FORMAT.toLowerCase().replace(/%\w/g, function(str) {
|
||||
str = str.replace(/%/, '');
|
||||
return str + str;
|
||||
});
|
||||
return format;
|
||||
}
|
||||
};
|
||||
|
||||
// datepicker, timepicker init
|
||||
grappelli.initDateAndTimePicker = function() {
|
||||
|
||||
// HACK: get rid of text after DateField (hardcoded in django.admin)
|
||||
$('p.datetime').each(function() {
|
||||
var text = $(this).html();
|
||||
text = text.replace(/^\w*: /, "");
|
||||
text = text.replace(/<br>[^<]*: /g, "<br>");
|
||||
$(this).html(text);
|
||||
});
|
||||
|
||||
var options = {
|
||||
//appendText: '(mm/dd/yyyy)',
|
||||
constrainInput: false,
|
||||
showOn: 'button',
|
||||
buttonImageOnly: false,
|
||||
buttonText: '',
|
||||
dateFormat: grappelli.getFormat('date'),
|
||||
showButtonPanel: true,
|
||||
showAnim: '',
|
||||
// HACK: sets the current instance to a global var.
|
||||
// needed to actually select today if the today-button is clicked.
|
||||
// see onClick handler for ".ui-datepicker-current"
|
||||
beforeShow: function(year, month, inst) {
|
||||
grappelli.datepicker_instance = this;
|
||||
}
|
||||
};
|
||||
var dateFields = $("input[class*='vDateField']:not([id*='__prefix__'])");
|
||||
dateFields.datepicker(options);
|
||||
|
||||
if (typeof IS_POPUP != "undefined" && IS_POPUP) {
|
||||
dateFields.datepicker('disable');
|
||||
}
|
||||
|
||||
// HACK: adds an event listener to the today button of datepicker
|
||||
// if clicked today gets selected and datepicker hides.
|
||||
// use on() because couldn't find hook after datepicker generates it's complete dom.
|
||||
$(".ui-datepicker-current").on('click', function() {
|
||||
$.datepicker._selectDate(grappelli.datepicker_instance);
|
||||
grappelli.datepicker_instance = null;
|
||||
});
|
||||
|
||||
// init timepicker
|
||||
$("input[class*='vTimeField']:not([id*='__prefix__'])").grp_timepicker();
|
||||
|
||||
// now-button for both date and time
|
||||
// $("<button class='ui-datetime-now' />").insertAfter("button.ui-timepicker-trigger");
|
||||
// $(".ui-datetime-now").on('click', function() {
|
||||
// alert("Now for date and time: grappelli.js line 68 ff.");
|
||||
// return false
|
||||
// });
|
||||
|
||||
};
|
||||
|
||||
// changelist: filter
|
||||
grappelli.initFilter = function() {
|
||||
$("a.grp-pulldown-handler").click(function() {
|
||||
var pulldownContainer = $(this).closest(".grp-pulldown-container");
|
||||
$(pulldownContainer).toggleClass("grp-pulldown-state-open").children(".grp-pulldown-content").toggle();
|
||||
});
|
||||
$("a.grp-pulldown-handler").bind('mouseout', function() {
|
||||
$(this).blur();
|
||||
});
|
||||
$(".grp-filter-choice").change(function(){
|
||||
location.href = $(this).val();
|
||||
});
|
||||
};
|
||||
|
||||
// changelist: searchbar
|
||||
grappelli.initSearchbar = function() {
|
||||
var searchbar = $("input.grp-search-field");
|
||||
searchbar.focus();
|
||||
};
|
||||
|
||||
grappelli.updateSelectFilter = function(form) {
|
||||
if (typeof SelectFilter != "undefined"){
|
||||
form.find(".selectfilter").each(function(index, value){
|
||||
var namearr = value.name.split('-');
|
||||
SelectFilter.init(value.id, namearr[namearr.length-1], false, "{% admin_media_prefix %}");
|
||||
});
|
||||
form.find(".selectfilterstacked").each(function(index, value){
|
||||
var namearr = value.name.split('-');
|
||||
SelectFilter.init(value.id, namearr[namearr.length-1], true, "{% admin_media_prefix %}");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
grappelli.reinitDateTimeFields = function(form) {
|
||||
form.find(".vDateField").datepicker({
|
||||
constrainInput: false,
|
||||
showOn: 'button',
|
||||
buttonImageOnly: false,
|
||||
buttonText: '',
|
||||
dateFormat: grappelli.getFormat('date')
|
||||
});
|
||||
form.find(".vTimeField").grp_timepicker();
|
||||
};
|
||||
|
||||
// autocomplete helpers
|
||||
grappelli.get_app_label = function(elem) {
|
||||
var link = elem.next("a");
|
||||
if (link.length > 0) {
|
||||
var url = link.attr('href').split('/');
|
||||
return url[url.length-3];
|
||||
}
|
||||
return false;
|
||||
};
|
||||
grappelli.get_model_name = function(elem) {
|
||||
var link = elem.next("a");
|
||||
if (link.length > 0) {
|
||||
var url = link.attr('href').split('/');
|
||||
return url[url.length-2];
|
||||
}
|
||||
return false;
|
||||
};
|
||||
grappelli.get_query_string = function(elem) {
|
||||
var link = elem.next("a");
|
||||
if (link.length > 0) {
|
||||
var url = link.attr('href').split('/');
|
||||
pairs = url[url.length-1].replace('?', '').split("&");
|
||||
return pairs.join(":");
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
})(grp.jQuery);
|
||||
|
||||
1
htdocs/grappelli/js/grappelli.min.js
vendored
Normal file
1
htdocs/grappelli/js/grappelli.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
144
htdocs/grappelli/js/jquery.grp_autocomplete_fk.js
Normal file
144
htdocs/grappelli/js/jquery.grp_autocomplete_fk.js
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* GRAPPELLI AUTOCOMPLETE FK
|
||||
* foreign-key lookup with autocomplete
|
||||
*/
|
||||
|
||||
(function($){
|
||||
|
||||
var methods = {
|
||||
init: function(options) {
|
||||
options = $.extend({}, $.fn.grp_autocomplete_fk.defaults, options);
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
// assign attributes
|
||||
$this.attr({
|
||||
"tabindex": "-1",
|
||||
"readonly": "readonly"
|
||||
}).addClass("grp-autocomplete-hidden-field");
|
||||
// remove djangos object representation (if given)
|
||||
if ($this.next().next() && $this.next().next().attr("class") != "errorlist" && $this.next().next().attr("class") != "grp-help") $this.next().next().remove();
|
||||
// build autocomplete wrapper
|
||||
$this.next().after(loader).after(remove_link($this.attr('id')));
|
||||
$this.parent().wrapInner("<div class='grp-autocomplete-wrapper-fk'></div>");
|
||||
$this.parent().prepend("<input id='" + $this.attr("id") + "-autocomplete' type='text' class='vTextField' value='' />");
|
||||
// extend options
|
||||
options = $.extend({
|
||||
wrapper_autocomplete: $this.parent(),
|
||||
input_field: $this.prev(),
|
||||
remove_link: $this.next().next().hide(),
|
||||
loader: $this.next().next().next().hide()
|
||||
}, $.fn.grp_autocomplete_fk.defaults, options);
|
||||
// lookup
|
||||
lookup_id($this, options); // lookup when loading page
|
||||
lookup_autocomplete($this, options); // autocomplete-handler
|
||||
$this.bind("change focus keyup", function() { // id-handler
|
||||
lookup_id($this, options);
|
||||
});
|
||||
// labels
|
||||
$("label[for='"+$this.attr('id')+"']").each(function() {
|
||||
$(this).attr("for", $this.attr("id")+"-autocomplete");
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.grp_autocomplete_fk = function(method) {
|
||||
if (methods[method]) {
|
||||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
} else if (typeof method === 'object' || ! method) {
|
||||
return methods.init.apply(this, arguments);
|
||||
} else {
|
||||
$.error('Method ' + method + ' does not exist on jQuery.grp_autocomplete_fk');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var loader = function() {
|
||||
var loader = $('<div class="grp-loader">loader</div>');
|
||||
return loader;
|
||||
};
|
||||
|
||||
var remove_link = function(id) {
|
||||
var removelink = $('<a class="grp-related-remove"></a>');
|
||||
removelink.attr('id', 'remove_'+id);
|
||||
removelink.attr('href', 'javascript://');
|
||||
removelink.attr('onClick', 'return removeRelatedObject(this);');
|
||||
removelink.hover(function() {
|
||||
$(this).parent().toggleClass("grp-autocomplete-preremove");
|
||||
});
|
||||
return removelink;
|
||||
};
|
||||
|
||||
var lookup_autocomplete = function(elem, options) {
|
||||
options.wrapper_autocomplete.find("input:first")
|
||||
.bind("focus", function() {
|
||||
options.wrapper_autocomplete.addClass("grp-state-focus");
|
||||
})
|
||||
.bind("blur", function() {
|
||||
options.wrapper_autocomplete.removeClass("grp-state-focus");
|
||||
})
|
||||
.autocomplete({
|
||||
minLength: 1,
|
||||
delay: 1000,
|
||||
source: function(request, response) {
|
||||
$.ajax({
|
||||
url: options.autocomplete_lookup_url,
|
||||
dataType: 'json',
|
||||
data: "term=" + encodeURIComponent(request.term) + "&app_label=" + grappelli.get_app_label(elem) + "&model_name=" + grappelli.get_model_name(elem) + "&query_string=" + grappelli.get_query_string(elem),
|
||||
beforeSend: function (XMLHttpRequest) {
|
||||
options.loader.show();
|
||||
},
|
||||
success: function(data){
|
||||
response($.map(data, function(item) {
|
||||
return {label: item.label, value: item.value};
|
||||
}));
|
||||
},
|
||||
complete: function (XMLHttpRequest, textStatus) {
|
||||
options.loader.hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
focus: function() { // prevent value inserted on focus
|
||||
return false;
|
||||
},
|
||||
select: function(event, ui) {
|
||||
options.input_field.val(ui.item.label);
|
||||
elem.val(ui.item.value);
|
||||
elem.val() ? $(options.remove_link).show() : $(options.remove_link).hide();
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.data("ui-autocomplete")._renderItem = function(ul,item) {
|
||||
if (!item.value) {
|
||||
return $("<li></li>")
|
||||
.data( "item.autocomplete", item )
|
||||
.append( "<span class='error'>" + item.label)
|
||||
.appendTo(ul);
|
||||
} else {
|
||||
return $("<li></li>")
|
||||
.data( "item.autocomplete", item )
|
||||
.append( "<a>" + item.label)
|
||||
.appendTo(ul);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var lookup_id = function(elem, options) {
|
||||
$.getJSON(options.lookup_url, {
|
||||
object_id: elem.val(),
|
||||
app_label: grappelli.get_app_label(elem),
|
||||
model_name: grappelli.get_model_name(elem)
|
||||
}, function(data) {
|
||||
$.each(data, function(index) {
|
||||
options.input_field.val(data[index].label);
|
||||
elem.val() ? $(options.remove_link).show() : $(options.remove_link).hide();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.grp_autocomplete_fk.defaults = {
|
||||
autocomplete_lookup_url: '',
|
||||
lookup_url: ''
|
||||
};
|
||||
|
||||
})(grp.jQuery);
|
||||
181
htdocs/grappelli/js/jquery.grp_autocomplete_generic.js
Normal file
181
htdocs/grappelli/js/jquery.grp_autocomplete_generic.js
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* GRAPPELLI AUTOCOMPLETE GENERIC
|
||||
* generic lookup with autocomplete
|
||||
*/
|
||||
|
||||
(function($){
|
||||
|
||||
var methods = {
|
||||
init: function(options) {
|
||||
options = $.extend({}, $.fn.grp_autocomplete_generic.defaults, options);
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
// assign attributes
|
||||
$this.attr({
|
||||
"tabindex": "-1",
|
||||
"readonly": "readonly"
|
||||
}).addClass("grp-autocomplete-hidden-field");
|
||||
// build autocomplete wrapper
|
||||
var val = $(options.content_type).val() || $(options.content_type).find(':checked').val();
|
||||
if (val) {
|
||||
$this.after(loader).after(remove_link($this.attr('id'))).after(lookup_link($this.attr("id"),val));
|
||||
}
|
||||
$this.parent().wrapInner("<div class='grp-autocomplete-wrapper-fk'></div>");
|
||||
$this.parent().prepend("<input id='" + $this.attr("id") + "-autocomplete' type='text' class='vTextField' value='' />");
|
||||
// defaults
|
||||
options = $.extend({
|
||||
wrapper_autocomplete: $(this).parent(),
|
||||
input_field: $(this).prev(),
|
||||
remove_link: $this.nextAll("a.grp-related-remove").hide(),
|
||||
loader: $this.nextAll("div.grp-loader").hide()
|
||||
}, $.fn.grp_autocomplete_generic.defaults, options);
|
||||
// lookup
|
||||
if (val) {
|
||||
lookup_id($this, options); // lookup when loading page
|
||||
}
|
||||
lookup_autocomplete($this, options); // autocomplete-handler
|
||||
$this.bind("change focus keyup", function() { // id-handler
|
||||
lookup_id($this, options);
|
||||
});
|
||||
$(options.content_type).bind("change", function() { // content-type-handler
|
||||
update_lookup($(this), options);
|
||||
});
|
||||
// labels
|
||||
$("label[for='"+$this.attr('id')+"']").each(function() {
|
||||
$(this).attr("for", $this.attr("id")+"-autocomplete");
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.grp_autocomplete_generic = function(method) {
|
||||
if (methods[method]) {
|
||||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
} else if (typeof method === 'object' || ! method) {
|
||||
return methods.init.apply(this, arguments);
|
||||
} else {
|
||||
$.error('Method ' + method + ' does not exist on jQuery.grp_autocomplete_generic');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var loader = function() {
|
||||
var loader = $('<div class="grp-loader">loader</div>');
|
||||
return loader;
|
||||
};
|
||||
|
||||
var remove_link = function(id) {
|
||||
var removelink = $('<a class="grp-related-remove"></a>');
|
||||
removelink.attr('id', 'remove_'+id);
|
||||
removelink.attr('href', 'javascript://');
|
||||
removelink.attr('onClick', 'return removeRelatedObject(this);');
|
||||
removelink.hover(function() {
|
||||
$(this).parent().toggleClass("grp-autocomplete-preremove");
|
||||
});
|
||||
return removelink;
|
||||
};
|
||||
|
||||
var lookup_link = function(id, val) {
|
||||
var lookuplink = $('<a class="related-lookup"></a>');
|
||||
lookuplink.attr('id', 'lookup_'+id);
|
||||
lookuplink.attr('href', "../../../" + MODEL_URL_ARRAY[val].app + "/" + MODEL_URL_ARRAY[val].model + '/?t=id');
|
||||
lookuplink.attr('onClick', 'return showRelatedObjectLookupPopup(this);');
|
||||
return lookuplink;
|
||||
};
|
||||
|
||||
var update_lookup = function(elem, options) {
|
||||
var obj = $(options.object_id);
|
||||
obj.val('');
|
||||
obj.prev().val('');
|
||||
// remove loader, a-related, related-lookup
|
||||
obj.nextAll("a.related-lookup").remove();
|
||||
obj.nextAll("a.grp-related-remove").remove();
|
||||
obj.nextAll("div.grp-loader").remove();
|
||||
var val = $(elem).val() || $(elem).find(':checked').val();
|
||||
if (val) {
|
||||
obj.after(loader).after(remove_link(obj.attr('id'))).after(lookup_link(obj.attr('id'),val));
|
||||
options.remove_link = obj.nextAll("a.grp-related-remove").hide();
|
||||
options.loader = obj.nextAll("div.grp-loader").hide();
|
||||
}
|
||||
};
|
||||
|
||||
var lookup_autocomplete = function(elem, options) {
|
||||
options.wrapper_autocomplete.find("input:first")
|
||||
.bind("focus", function() {
|
||||
options.wrapper_autocomplete.addClass("grp-state-focus");
|
||||
})
|
||||
.bind("blur", function() {
|
||||
options.wrapper_autocomplete.removeClass("grp-state-focus");
|
||||
})
|
||||
.autocomplete({
|
||||
minLength: 1,
|
||||
delay: 1000,
|
||||
source: function(request, response) {
|
||||
$.ajax({
|
||||
url: options.autocomplete_lookup_url,
|
||||
dataType: 'json',
|
||||
data: "term=" + encodeURIComponent(request.term) + "&app_label=" + grappelli.get_app_label(elem) + "&model_name=" + grappelli.get_model_name(elem) + "&query_string=" + grappelli.get_query_string(elem),
|
||||
beforeSend: function (XMLHttpRequest) {
|
||||
var val = $(options.content_type).val() || $(options.content_type).find(':checked').val();
|
||||
if (val) {
|
||||
options.loader.show();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
success: function(data){
|
||||
response($.map(data, function(item) {
|
||||
return {label: item.label, value: item.value};
|
||||
}));
|
||||
},
|
||||
complete: function (XMLHttpRequest, textStatus) {
|
||||
options.loader.hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
focus: function() { // prevent value inserted on focus
|
||||
return false;
|
||||
},
|
||||
select: function(event, ui) {
|
||||
options.input_field.val(ui.item.label);
|
||||
elem.val(ui.item.value);
|
||||
elem.val() ? $(options.remove_link).show() : $(options.remove_link).hide();
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.data("ui-autocomplete")._renderItem = function(ul,item) {
|
||||
if (!item.value) {
|
||||
return $("<li></li>")
|
||||
.data( "item.autocomplete", item )
|
||||
.append( "<span class='error'>" + item.label)
|
||||
.appendTo(ul);
|
||||
} else {
|
||||
return $("<li></li>")
|
||||
.data( "item.autocomplete", item )
|
||||
.append( "<a>" + item.label)
|
||||
.appendTo(ul);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var lookup_id = function(elem, options) {
|
||||
$.getJSON(options.lookup_url, {
|
||||
object_id: elem.val(),
|
||||
app_label: grappelli.get_app_label(elem),
|
||||
model_name: grappelli.get_model_name(elem)
|
||||
}, function(data) {
|
||||
$.each(data, function(index) {
|
||||
options.input_field.val(data[index].label);
|
||||
elem.val() ? $(options.remove_link).show() : $(options.remove_link).hide();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.grp_autocomplete_generic.defaults = {
|
||||
autocomplete_lookup_url: '',
|
||||
lookup_url: '',
|
||||
content_type: '',
|
||||
object_id: ''
|
||||
};
|
||||
|
||||
})(grp.jQuery);
|
||||
196
htdocs/grappelli/js/jquery.grp_autocomplete_m2m.js
Normal file
196
htdocs/grappelli/js/jquery.grp_autocomplete_m2m.js
Normal file
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* GRAPPELLI AUTOCOMPLETE M2M
|
||||
* many-to-many lookup with autocomplete
|
||||
*/
|
||||
|
||||
|
||||
(function($){
|
||||
|
||||
var methods = {
|
||||
init: function(options) {
|
||||
options = $.extend({}, $.fn.grp_autocomplete_m2m.defaults, options);
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
// assign attributes
|
||||
$this.attr({
|
||||
"tabindex": "-1",
|
||||
"readonly": "readonly"
|
||||
}).addClass("grp-autocomplete-hidden-field");
|
||||
// build autocomplete wrapper
|
||||
$this.next().after(loader).after(remove_link($this.attr('id')));
|
||||
$this.parent().wrapInner("<div class='grp-autocomplete-wrapper-m2m'></div>");
|
||||
//$this.parent().prepend("<ul class='search'><li class='search'><input id='" + $this.attr("id") + "-autocomplete' type='text' class='vTextField' value='' /></li></ul>").prepend("<ul class='repr'></ul>");
|
||||
$this.parent().prepend("<ul class='grp-repr'><li class='grp-search'><input id='" + $this.attr("id") + "-autocomplete' type='text' class='vTextField' value='' /></li></ul>");
|
||||
// defaults
|
||||
options = $.extend({
|
||||
wrapper_autocomplete: $this.parent(),
|
||||
wrapper_repr: $this.parent().find("ul.grp-repr"),
|
||||
wrapper_search: $this.parent().find("li.grp-search"),
|
||||
remove_link: $this.next().next().hide(),
|
||||
loader: $this.next().next().next().hide()
|
||||
}, $.fn.grp_autocomplete_m2m.defaults, options);
|
||||
// move errorlist outside the wrapper
|
||||
if ($this.parent().find("ul.errorlist")) {
|
||||
$this.parent().find("ul.errorlist").detach().appendTo($this.parent().parent());
|
||||
}
|
||||
// lookup
|
||||
lookup_id($this, options); // lookup when loading page
|
||||
lookup_autocomplete($this, options); // autocomplete-handler
|
||||
$this.bind("change focus keyup", function() { // id-handler
|
||||
lookup_id($this, options);
|
||||
});
|
||||
// labels
|
||||
$("label[for='"+$this.attr('id')+"']").each(function() {
|
||||
$(this).attr("for", $this.attr("id")+"-autocomplete");
|
||||
});
|
||||
// click on div > focus input
|
||||
options.wrapper_autocomplete.bind("click", function(e) {
|
||||
// prevent focus when clicking on remove/select
|
||||
if (!$(e.target).hasClass("related-lookup") && !$(e.target).hasClass("grp-related-remove")) {
|
||||
options.wrapper_search.find("input:first").focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.grp_autocomplete_m2m = function(method) {
|
||||
if (methods[method]) {
|
||||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
} else if (typeof method === 'object' || ! method) {
|
||||
return methods.init.apply(this, arguments);
|
||||
} else {
|
||||
$.error('Method ' + method + ' does not exist on jQuery.grp_autocomplete_m2m');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var value_add = function(elem, value, options) {
|
||||
var values = [];
|
||||
if (elem.val()) values = elem.val().split(",");
|
||||
values.push(value);
|
||||
elem.val(values.join(","));
|
||||
return values.join(",");
|
||||
};
|
||||
|
||||
var value_remove = function(elem, position, options) {
|
||||
var values = [];
|
||||
if (elem.val()) values = elem.val().split(",");
|
||||
values.splice(position,1);
|
||||
elem.val(values.join(","));
|
||||
return values.join(",");
|
||||
};
|
||||
|
||||
var loader = function() {
|
||||
var loader = $('<div class="grp-loader">loader</div>');
|
||||
return loader;
|
||||
};
|
||||
|
||||
var remove_link = function(id) {
|
||||
var removelink = $('<a class="grp-related-remove"></a>');
|
||||
removelink.attr('id', 'remove_'+id);
|
||||
removelink.attr('href', 'javascript://');
|
||||
removelink.attr('onClick', 'return removeRelatedObject(this);');
|
||||
removelink.hover(function() {
|
||||
$(this).parent().toggleClass("grp-autocomplete-preremove");
|
||||
});
|
||||
return removelink;
|
||||
};
|
||||
|
||||
var repr_add = function(elem, label, options) {
|
||||
var repr = $('<li class="grp-repr"></li>');
|
||||
var removelink = $('<a class="grp-m2m-remove" href="javascript://">' + label + '</a>');
|
||||
repr.append(removelink);
|
||||
repr.insertBefore(options.wrapper_search);
|
||||
removelink.bind("click", function(e) { // remove-handler
|
||||
var pos = $(this).parent().parent().children("li").index($(this).parent());
|
||||
value_remove(elem, pos, options);
|
||||
$(this).parent().remove();
|
||||
elem.val() ? $(options.remove_link).show() : $(options.remove_link).hide();
|
||||
e.stopPropagation(); // prevent focus on input
|
||||
});
|
||||
removelink.hover(function() {
|
||||
$(this).parent().toggleClass("grp-autocomplete-preremove");
|
||||
});
|
||||
};
|
||||
|
||||
var lookup_autocomplete = function(elem, options) {
|
||||
options.wrapper_search.find("input:first")
|
||||
.bind("keydown", function(event) { // don't navigate away from the field on tab when selecting an item
|
||||
if (event.keyCode === $.ui.keyCode.TAB && $(this).data("autocomplete").menu.active) {
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
.bind("focus", function() {
|
||||
options.wrapper_autocomplete.addClass("grp-state-focus");
|
||||
})
|
||||
.bind("blur", function() {
|
||||
options.wrapper_autocomplete.removeClass("grp-state-focus");
|
||||
})
|
||||
.autocomplete({
|
||||
minLength: 1,
|
||||
delay: 1000,
|
||||
position: {my: "left top", at: "left bottom", of: options.wrapper_autocomplete},
|
||||
open: function(event, ui) {
|
||||
$(".ui-menu").width(options.wrapper_autocomplete.outerWidth()-6);
|
||||
},
|
||||
source: function(request, response) {
|
||||
$.ajax({
|
||||
url: options.autocomplete_lookup_url,
|
||||
dataType: 'json',
|
||||
data: "term=" + encodeURIComponent(request.term) + "&app_label=" + grappelli.get_app_label(elem) + "&model_name=" + grappelli.get_model_name(elem) + "&query_string=" + grappelli.get_query_string(elem),
|
||||
beforeSend: function (XMLHttpRequest) {
|
||||
options.loader.show();
|
||||
},
|
||||
success: function(data){
|
||||
response($.map(data, function(item) {
|
||||
return {label: item.label, value: item.value};
|
||||
}));
|
||||
},
|
||||
complete: function (XMLHttpRequest, textStatus) {
|
||||
options.loader.hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
focus: function() { // prevent value inserted on focus
|
||||
return false;
|
||||
},
|
||||
select: function(event, ui) { // add repr, add value
|
||||
repr_add(elem, ui.item.label, options);
|
||||
value_add(elem, ui.item.value, options);
|
||||
elem.val() ? $(options.remove_link).show() : $(options.remove_link).hide();
|
||||
$(this).val("").focus();
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.data("ui-autocomplete")._renderItem = function(ul,item) {
|
||||
if (!item.value) {
|
||||
return $("<li></li>")
|
||||
.data( "item.autocomplete", item )
|
||||
.append( "<span class='error'>" + item.label)
|
||||
.appendTo(ul);
|
||||
} else {
|
||||
return $("<li></li>")
|
||||
.data( "item.autocomplete", item )
|
||||
.append( "<a>" + item.label)
|
||||
.appendTo(ul);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var lookup_id = function(elem, options) {
|
||||
$.getJSON(options.lookup_url, {
|
||||
object_id: elem.val(),
|
||||
app_label: grappelli.get_app_label(elem),
|
||||
model_name: grappelli.get_model_name(elem)
|
||||
}, function(data) {
|
||||
options.wrapper_repr.find("li.grp-repr").remove();
|
||||
options.wrapper_search.find("input").val("");
|
||||
$.each(data, function(index) {
|
||||
if (data[index].value) repr_add(elem, data[index].label, options);
|
||||
});
|
||||
elem.val() ? $(options.remove_link).show() : $(options.remove_link).hide();
|
||||
});
|
||||
};
|
||||
|
||||
})(grp.jQuery);
|
||||
35
htdocs/grappelli/js/jquery.grp_collapsible.js
Normal file
35
htdocs/grappelli/js/jquery.grp_collapsible.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* GRAPPELLI COLLAPSIBLES
|
||||
* handles collapsibles,
|
||||
* excluding open/closing all elements
|
||||
* within a group.
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
$.fn.grp_collapsible = function(options){
|
||||
var defaults = {
|
||||
toggle_handler_slctr: ".grp-collapse-handler:first",
|
||||
closed_css: "grp-closed",
|
||||
open_css: "grp-open",
|
||||
on_init: function() {},
|
||||
on_toggle: function() {}
|
||||
};
|
||||
var opts = $.extend(defaults, options);
|
||||
return this.each(function() {
|
||||
_initialize($(this), opts);
|
||||
});
|
||||
};
|
||||
var _initialize = function(elem, options) {
|
||||
options.on_init(elem, options);
|
||||
_register_handlers(elem, options);
|
||||
};
|
||||
var _register_handlers = function(elem, options) {
|
||||
_register_toggle_handler(elem, options);
|
||||
};
|
||||
var _register_toggle_handler = function(elem, options) {
|
||||
elem.children(options.toggle_handler_slctr).click(function() {
|
||||
elem.toggleClass(options.closed_css).toggleClass(options.open_css);
|
||||
options.on_toggle(elem, options);
|
||||
});
|
||||
};
|
||||
})(grp.jQuery);
|
||||
54
htdocs/grappelli/js/jquery.grp_collapsible_group.js
Normal file
54
htdocs/grappelli/js/jquery.grp_collapsible_group.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* GRAPPELLI GROUP COLLAPSIBLES
|
||||
* handles open/closing of all elements
|
||||
* with tabular- and stacked-inlines.
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
$.fn.grp_collapsible_group = function(options){
|
||||
var defaults = {
|
||||
open_handler_slctr: ".grp-open-handler",
|
||||
close_handler_slctr: ".grp-close-handler",
|
||||
collapsible_container_slctr: ".grp-collapse",
|
||||
closed_css: "grp-closed",
|
||||
open_css: "grp-open",
|
||||
on_init: function() {},
|
||||
on_open: function() {},
|
||||
on_close: function() {}
|
||||
};
|
||||
options = $.extend(defaults, options);
|
||||
return this.each(function() {
|
||||
_initialize($(this), options);
|
||||
});
|
||||
};
|
||||
var _initialize = function(elem, options) {
|
||||
options.on_init(elem, options);
|
||||
_register_handlers(elem, options);
|
||||
};
|
||||
var _register_handlers = function(elem, options) {
|
||||
_register_open_handler(elem, options);
|
||||
_register_close_handler(elem, options);
|
||||
};
|
||||
var _register_open_handler = function(elem, options) {
|
||||
elem.find(options.open_handler_slctr).each(function() {
|
||||
$(this).click(function() {
|
||||
options.on_open(elem, options);
|
||||
elem.find(options.collapsible_container_slctr)
|
||||
.removeClass(options.closed_css)
|
||||
.addClass(options.open_css);
|
||||
elem.removeClass(options.closed_css)
|
||||
.addClass(options.open_css);
|
||||
});
|
||||
});
|
||||
};
|
||||
var _register_close_handler = function(elem, options) {
|
||||
elem.find(options.close_handler_slctr).each(function() {
|
||||
$(this).click(function() {
|
||||
options.on_close(elem, options);
|
||||
elem.find(options.collapsible_container_slctr)
|
||||
.removeClass(options.open_css)
|
||||
.addClass(options.closed_css);
|
||||
});
|
||||
});
|
||||
};
|
||||
})(grp.jQuery);
|
||||
195
htdocs/grappelli/js/jquery.grp_inline.js
Normal file
195
htdocs/grappelli/js/jquery.grp_inline.js
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* GRAPPELLI INLINES
|
||||
* jquery-plugin for inlines (stacked and tabular)
|
||||
*/
|
||||
|
||||
|
||||
(function($) {
|
||||
$.fn.grp_inline = function(options) {
|
||||
var defaults = {
|
||||
prefix: "form", // The form prefix for your django formset
|
||||
addText: "add another", // Text for the add link
|
||||
deleteText: "remove", // Text for the delete link
|
||||
addCssClass: "grp-add-handler", // CSS class applied to the add link
|
||||
removeCssClass: "grp-remove-handler", // CSS class applied to the remove link
|
||||
deleteCssClass: "grp-delete-handler", // CSS class applied to the delete link
|
||||
emptyCssClass: "grp-empty-form", // CSS class applied to the empty row
|
||||
formCssClass: "grp-dynamic-form", // CSS class applied to each form in a formset
|
||||
predeleteCssClass: "grp-predelete",
|
||||
onBeforeInit: function(form) {}, // Function called before a form is initialized
|
||||
onBeforeAdded: function(inline) {}, // Function called before a form is added
|
||||
onBeforeRemoved: function(form) {}, // Function called before a form is removed
|
||||
onBeforeDeleted: function(form) {}, // Function called before a form is deleted
|
||||
onAfterInit: function(form) {}, // Function called after a form has been initialized
|
||||
onAfterAdded: function(form) {}, // Function called after a form has been added
|
||||
onAfterRemoved: function(inline) {}, // Function called after a form has been removed
|
||||
onAfterDeleted: function(form) {} // Function called after a form has been deleted
|
||||
};
|
||||
options = $.extend(defaults, options);
|
||||
|
||||
return this.each(function() {
|
||||
var inline = $(this); // the current inline node
|
||||
var totalForms = inline.find("#id_" + options.prefix + "-TOTAL_FORMS");
|
||||
// set autocomplete to off in order to prevent the browser from keeping the current value after reload
|
||||
totalForms.attr("autocomplete", "off");
|
||||
// init inline and add-buttons
|
||||
initInlineForms(inline, options);
|
||||
initAddButtons(inline, options);
|
||||
// button handlers
|
||||
addButtonHandler(inline.find("a." + options.addCssClass), options);
|
||||
removeButtonHandler(inline.find("a." + options.removeCssClass), options);
|
||||
deleteButtonHandler(inline.find("a." + options.deleteCssClass), options);
|
||||
});
|
||||
};
|
||||
|
||||
getFormIndex = function(elem, options, regex) {
|
||||
var formIndex = elem.find("[id^='id_" + options.prefix + "']").attr('id');
|
||||
if (!formIndex) { return -1; }
|
||||
return parseInt(regex.exec(formIndex)[1], 10);
|
||||
}
|
||||
|
||||
updateFormIndex = function(elem, options, replace_regex, replace_with) {
|
||||
elem.find(':input,span,table,iframe,label,a,ul,p,img,div').each(function() {
|
||||
var node = $(this),
|
||||
node_id = node.attr('id'),
|
||||
node_name = node.attr('name'),
|
||||
node_for = node.attr('for'),
|
||||
node_href = node.attr("href"),
|
||||
node_class = node.attr("class"),
|
||||
node_onclick = node.attr("onclick");
|
||||
if (node_id) { node.attr('id', node_id.replace(replace_regex, replace_with)); }
|
||||
if (node_name) { node.attr('name', node_name.replace(replace_regex, replace_with)); }
|
||||
if (node_for) { node.attr('for', node_for.replace(replace_regex, replace_with)); }
|
||||
if (node_href) { node.attr('href', node_href.replace(replace_regex, replace_with)); }
|
||||
if (node_class) { node.attr('class', node_class.replace(replace_regex, replace_with)); }
|
||||
if (node_onclick) { node.attr('onclick', node_onclick.replace(replace_regex, replace_with)); }
|
||||
});
|
||||
};
|
||||
|
||||
initInlineForms = function(elem, options) {
|
||||
elem.find("div.grp-module").each(function() {
|
||||
var form = $(this);
|
||||
// callback
|
||||
options.onBeforeInit(form);
|
||||
// add options.formCssClass to all forms in the inline
|
||||
// except table/theader/add-item
|
||||
if (form.attr('id') !== "") {
|
||||
form.not("." + options.emptyCssClass).not(".grp-table").not(".grp-thead").not(".add-item").addClass(options.formCssClass);
|
||||
}
|
||||
// add options.predeleteCssClass to forms with the delete checkbox checked
|
||||
form.find("li.grp-delete-handler-container input").each(function() {
|
||||
if ($(this).is(":checked") && form.hasClass("has_original")) {
|
||||
form.toggleClass(options.predeleteCssClass);
|
||||
}
|
||||
});
|
||||
// callback
|
||||
options.onAfterInit(form);
|
||||
});
|
||||
};
|
||||
|
||||
initAddButtons = function(elem, options) {
|
||||
var totalForms = elem.find("#id_" + options.prefix + "-TOTAL_FORMS");
|
||||
var maxForms = elem.find("#id_" + options.prefix + "-MAX_NUM_FORMS");
|
||||
var addButtons = elem.find("a." + options.addCssClass);
|
||||
// hide add button in case we've hit the max, except we want to add infinitely
|
||||
if ((maxForms.val() !== '') && (maxForms.val()-totalForms.val()) <= 0) {
|
||||
hideAddButtons(elem, options);
|
||||
}
|
||||
};
|
||||
|
||||
addButtonHandler = function(elem, options) {
|
||||
elem.bind("click", function() {
|
||||
var inline = elem.parents(".grp-group"),
|
||||
totalForms = inline.find("#id_" + options.prefix + "-TOTAL_FORMS"),
|
||||
maxForms = inline.find("#id_" + options.prefix + "-MAX_NUM_FORMS"),
|
||||
addButtons = inline.find("a." + options.addCssClass),
|
||||
empty_template = inline.find("#" + options.prefix + "-empty");
|
||||
// callback
|
||||
options.onBeforeAdded(inline);
|
||||
// create new form
|
||||
var index = parseInt(totalForms.val(), 10),
|
||||
form = empty_template.clone(true);
|
||||
form.removeClass(options.emptyCssClass)
|
||||
.attr("id", empty_template.attr('id').replace("-empty", index));
|
||||
// update form index
|
||||
var re = /__prefix__/g;
|
||||
updateFormIndex(form, options, re, index);
|
||||
// after "__prefix__" strings has been substituted with the number
|
||||
// of the inline, we can add the form to DOM, not earlier.
|
||||
// This way we can support handlers that track live element
|
||||
// adding/removing, like those used in django-autocomplete-light
|
||||
form.insertBefore(empty_template)
|
||||
.addClass(options.formCssClass);
|
||||
// update total forms
|
||||
totalForms.val(index + 1);
|
||||
// hide add button in case we've hit the max, except we want to add infinitely
|
||||
if ((maxForms.val() !== 0) && (maxForms.val() !== "") && (maxForms.val() - totalForms.val()) <= 0) {
|
||||
hideAddButtons(inline, options);
|
||||
}
|
||||
// callback
|
||||
options.onAfterAdded(form);
|
||||
});
|
||||
};
|
||||
|
||||
removeButtonHandler = function(elem, options) {
|
||||
elem.bind("click", function() {
|
||||
var inline = elem.parents(".grp-group"),
|
||||
form = $(this).parents("." + options.formCssClass).first(),
|
||||
totalForms = inline.find("#id_" + options.prefix + "-TOTAL_FORMS"),
|
||||
maxForms = inline.find("#id_" + options.prefix + "-MAX_NUM_FORMS"),
|
||||
re = /-(\d+)-/,
|
||||
removedFormIndex = getFormIndex(form, options, re);
|
||||
// callback
|
||||
options.onBeforeRemoved(form);
|
||||
// remove form
|
||||
form.remove();
|
||||
// update total forms
|
||||
totalForms.val(parseInt(totalForms.val(), 10) - 1);
|
||||
// show add button in case we've dropped below max
|
||||
if ((maxForms.val() !== 0) && (maxForms.val() - totalForms.val()) > 0) {
|
||||
showAddButtons(inline, options);
|
||||
}
|
||||
// update form index (only forms with a higher index than the removed form)
|
||||
inline.find("." + options.formCssClass).each(function() {
|
||||
var form = $(this),
|
||||
formIndex = getFormIndex(form, options, re);
|
||||
if (formIndex > removedFormIndex) {
|
||||
updateFormIndex(form, options, re, "-" + (formIndex - 1) + "-");
|
||||
}
|
||||
});
|
||||
// callback
|
||||
options.onAfterRemoved(inline);
|
||||
});
|
||||
};
|
||||
|
||||
deleteButtonHandler = function(elem, options) {
|
||||
elem.bind("click", function() {
|
||||
var deleteInput = $(this).prev(),
|
||||
form = $(this).parents("." + options.formCssClass).first();
|
||||
// callback
|
||||
options.onBeforeDeleted(form);
|
||||
// toggle options.predeleteCssClass and toggle checkbox
|
||||
if (form.hasClass("has_original")) {
|
||||
form.toggleClass(options.predeleteCssClass);
|
||||
if (deleteInput.prop("checked")) {
|
||||
deleteInput.removeAttr("checked");
|
||||
} else {
|
||||
deleteInput.prop("checked", true);
|
||||
}
|
||||
}
|
||||
// callback
|
||||
options.onAfterDeleted(form);
|
||||
});
|
||||
};
|
||||
|
||||
hideAddButtons = function(elem, options) {
|
||||
var addButtons = elem.find("a." + options.addCssClass);
|
||||
addButtons.hide().parents('.grp-add-item').hide();
|
||||
};
|
||||
|
||||
showAddButtons = function(elem, options) {
|
||||
var addButtons = elem.find("a." + options.addCssClass);
|
||||
addButtons.show().parents('.grp-add-item').show();
|
||||
};
|
||||
|
||||
})(grp.jQuery);
|
||||
66
htdocs/grappelli/js/jquery.grp_related_fk.js
Normal file
66
htdocs/grappelli/js/jquery.grp_related_fk.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* GRAPPELLI RELATED FK
|
||||
* foreign-key lookup
|
||||
*/
|
||||
|
||||
(function($){
|
||||
|
||||
var methods = {
|
||||
init: function(options) {
|
||||
options = $.extend({}, $.fn.grp_related_fk.defaults, options);
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var $parent = $this.parent();
|
||||
// remove djangos object representation
|
||||
if ($parent.find('a.related-lookup').next().is('strong')) {
|
||||
$parent.find('a.related-lookup').get(0).nextSibling.nodeValue="";
|
||||
$parent.find('a.related-lookup').next('strong').remove();
|
||||
}
|
||||
// add placeholder
|
||||
$parent.find('a.related-lookup').after(options.placeholder);
|
||||
// add related class
|
||||
$this.addClass('grp-has-related-lookup');
|
||||
// lookup
|
||||
lookup_id($this, options); // lookup when loading page
|
||||
$this.bind("change focus keyup", function() { // id-handler
|
||||
lookup_id($this, options);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.grp_related_fk = function(method) {
|
||||
if (methods[method]) {
|
||||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
} else if (typeof method === 'object' || ! method) {
|
||||
return methods.init.apply(this, arguments);
|
||||
} else {
|
||||
$.error('Method ' + method + ' does not exist on jQuery.grp_related_fk');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var lookup_id = function(elem, options) {
|
||||
var text = elem.parent().find('.grp-placeholder-related-fk');
|
||||
$.getJSON(options.lookup_url, {
|
||||
object_id: elem.val(),
|
||||
app_label: grappelli.get_app_label(elem),
|
||||
model_name: grappelli.get_model_name(elem),
|
||||
query_string: grappelli.get_query_string(elem)
|
||||
}, function(data) {
|
||||
if (data[0].label === "") {
|
||||
text.hide();
|
||||
} else {
|
||||
text.show();
|
||||
}
|
||||
text.html('<span class="grp-placeholder-label">' + data[0].label + '</span>');
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.grp_related_fk.defaults = {
|
||||
placeholder: '<div class="grp-placeholder-related-fk"></div>',
|
||||
repr_max_length: 30,
|
||||
lookup_url: ''
|
||||
};
|
||||
|
||||
})(grp.jQuery);
|
||||
89
htdocs/grappelli/js/jquery.grp_related_generic.js
Normal file
89
htdocs/grappelli/js/jquery.grp_related_generic.js
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* GRAPPELLI RELATED FK
|
||||
* generic lookup
|
||||
*/
|
||||
|
||||
(function($){
|
||||
|
||||
var methods = {
|
||||
init: function(options) {
|
||||
options = $.extend({}, $.fn.grp_related_generic.defaults, options);
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
// add placeholder
|
||||
var val = $(options.content_type).val() || $(options.content_type).find(':checked').val();
|
||||
if (val) {
|
||||
$this.after(options.placeholder).after(lookup_link($this.attr('id'),val));
|
||||
}
|
||||
// add related class
|
||||
$this.addClass('grp-has-related-lookup');
|
||||
// lookup
|
||||
if (val) {
|
||||
lookup_id($this, options); // lookup when loading page
|
||||
}
|
||||
$this.bind("change focus keyup", function() { // id-handler
|
||||
lookup_id($this, options);
|
||||
});
|
||||
$(options.content_type).bind("change", function() { // content-type-handler
|
||||
update_lookup($(this), options);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.grp_related_generic = function(method) {
|
||||
if (methods[method]) {
|
||||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
} else if (typeof method === 'object' || ! method) {
|
||||
return methods.init.apply(this, arguments);
|
||||
} else {
|
||||
$.error('Method ' + method + ' does not exist on jQuery.grp_related_generic');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var lookup_link = function(id, val) {
|
||||
var lookuplink = $('<a class="related-lookup"></a>');
|
||||
lookuplink.attr('id', 'lookup_'+id);
|
||||
lookuplink.attr('href', "../../../" + MODEL_URL_ARRAY[val].app + "/" + MODEL_URL_ARRAY[val].model + '/?t=id');
|
||||
lookuplink.attr('onClick', 'return showRelatedObjectLookupPopup(this);');
|
||||
return lookuplink;
|
||||
};
|
||||
|
||||
var update_lookup = function(elem, options) {
|
||||
var obj = $(options.object_id);
|
||||
obj.val('');
|
||||
obj.parent().find('a.related-lookup').remove();
|
||||
obj.parent().find('.grp-placeholder-related-generic').remove();
|
||||
var val = $(elem).val() || $(elem).find(':checked').val();
|
||||
if (val) {
|
||||
obj.after(options.placeholder).after(lookup_link(obj.attr('id'),val));
|
||||
}
|
||||
};
|
||||
|
||||
var lookup_id = function(elem, options) {
|
||||
var text = elem.next().next();
|
||||
$.getJSON(options.lookup_url, {
|
||||
object_id: elem.val(),
|
||||
app_label: grappelli.get_app_label(elem),
|
||||
model_name: grappelli.get_model_name(elem),
|
||||
query_string: grappelli.get_query_string(elem)
|
||||
}, function(data) {
|
||||
if (data[0].label === "") {
|
||||
text.hide();
|
||||
} else {
|
||||
text.show();
|
||||
}
|
||||
text.html('<span class="grp-placeholder-label">' + data[0].label + '</span>');
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.grp_related_generic.defaults = {
|
||||
placeholder: '<div class="grp-placeholder-related-generic" style="display:none"></div>',
|
||||
repr_max_length: 30,
|
||||
lookup_url: '',
|
||||
content_type: '',
|
||||
object_id: ''
|
||||
};
|
||||
|
||||
})(grp.jQuery);
|
||||
62
htdocs/grappelli/js/jquery.grp_related_m2m.js
Normal file
62
htdocs/grappelli/js/jquery.grp_related_m2m.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* GRAPPELLI RELATED M2M
|
||||
* m2m lookup
|
||||
*/
|
||||
|
||||
(function($){
|
||||
|
||||
var methods = {
|
||||
init: function(options) {
|
||||
options = $.extend({}, $.fn.grp_related_m2m.defaults, options);
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
// add placeholder
|
||||
$this.parent().find('a.related-lookup').after(options.placeholder);
|
||||
// change lookup class
|
||||
$this.next().addClass("grp-m2m");
|
||||
// add related class
|
||||
$this.addClass('grp-has-related-lookup');
|
||||
// lookup
|
||||
lookup_id($this, options); // lookup when loading page
|
||||
$this.bind("change focus keyup", function() { // id-handler
|
||||
lookup_id($this, options);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.grp_related_m2m = function(method) {
|
||||
if (methods[method]) {
|
||||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
} else if (typeof method === 'object' || ! method) {
|
||||
return methods.init.apply(this, arguments);
|
||||
} else {
|
||||
$.error('Method ' + method + ' does not exist on jQuery.grp_related_m2m');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
var lookup_id = function(elem, options) {
|
||||
$.getJSON(options.lookup_url, {
|
||||
object_id: elem.val(),
|
||||
app_label: grappelli.get_app_label(elem),
|
||||
model_name: grappelli.get_model_name(elem),
|
||||
query_string: grappelli.get_query_string(elem)
|
||||
}, function(data) {
|
||||
values = $.map(data, function (a) { return '<span class="grp-placeholder-label">' + a.label + '</span>'; });
|
||||
if (values === "") {
|
||||
elem.parent().find('.grp-placeholder-related-m2m').hide();
|
||||
} else {
|
||||
elem.parent().find('.grp-placeholder-related-m2m').show();
|
||||
}
|
||||
elem.parent().find('.grp-placeholder-related-m2m').html(values.join('<span class="grp-separator"></span>'));
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.grp_related_m2m.defaults = {
|
||||
placeholder: '<div class="grp-placeholder-related-m2m"></div>',
|
||||
repr_max_length: 30,
|
||||
lookup_url: ''
|
||||
};
|
||||
|
||||
})(grp.jQuery);
|
||||
186
htdocs/grappelli/js/jquery.grp_timepicker.js
Normal file
186
htdocs/grappelli/js/jquery.grp_timepicker.js
Normal file
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* GRAPPELLI TIMEPICKER
|
||||
* works pretty similar to ui.datepicker:
|
||||
* adds a button to the element
|
||||
* creates a node (div) at the bottom called ui-timepicker
|
||||
* element.onClick fills the ui-timepicker node with the time_list (all times you can select)
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
$.widget("ui.grp_timepicker", {
|
||||
// default options
|
||||
options: {
|
||||
// template for the container of the timepicker
|
||||
template: '<div id="ui-timepicker" class="module" style="position: absolute; display: none;"></div>',
|
||||
// selector to get the ui-timepicker once it's added to the dom
|
||||
timepicker_selector: "#ui-timepicker",
|
||||
// needed offset of the container from the element
|
||||
offset: {
|
||||
top: 0
|
||||
},
|
||||
// if time_list wasn't sent when calling the timepicker we use this
|
||||
default_time_list: [
|
||||
'now',
|
||||
'00:00',
|
||||
'01:00',
|
||||
'02:00',
|
||||
'03:00',
|
||||
'04:00',
|
||||
'05:00',
|
||||
'06:00',
|
||||
'07:00',
|
||||
'08:00',
|
||||
'09:00',
|
||||
'10:00',
|
||||
'11:00',
|
||||
'12:00',
|
||||
'13:00',
|
||||
'14:00',
|
||||
'15:00',
|
||||
'16:00',
|
||||
'17:00',
|
||||
'18:00',
|
||||
'19:00',
|
||||
'20:00',
|
||||
'21:00',
|
||||
'22:00',
|
||||
'23:00'
|
||||
],
|
||||
// leave this empty!!!
|
||||
// NOTE: you can't set a default for time_list because if you call:
|
||||
// $("node").timepicker({time_list: ["01:00", "02:00"]})
|
||||
// ui.widget will extend/merge the options.time_list with the one you sent.
|
||||
time_list: []
|
||||
},
|
||||
|
||||
// init timepicker for a specific element
|
||||
_create: function() {
|
||||
// for the events
|
||||
var self = this;
|
||||
|
||||
// to close timpicker if you click somewhere in the document
|
||||
$(document).mousedown(function(evt) {
|
||||
if (self.timepicker.is(":visible")) {
|
||||
var $target = $(evt.target);
|
||||
if ($target[0].id != self.timepicker[0].id && $target.parents(self.options.timepicker_selector).length === 0 && !$target.hasClass('hasTimepicker') && !$target.hasClass('ui-timepicker-trigger')) {
|
||||
self.timepicker.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
// close on esc
|
||||
$(document).keyup(function(e) {
|
||||
if (e.keyCode == 27) {
|
||||
self.timepicker.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// get/create timepicker's container
|
||||
if ($(this.options.timepicker_selector).size() === 0) {
|
||||
$(this.options.template).appendTo('body');
|
||||
}
|
||||
this.timepicker = $(this.options.timepicker_selector);
|
||||
this.timepicker.hide();
|
||||
|
||||
// modify the element and create the button
|
||||
this.element.addClass("hasTimepicker");
|
||||
this.button = $('<button type="button" class="ui-timepicker-trigger"></button>');
|
||||
this.element.after(this.button);
|
||||
|
||||
// disable button if element is disabled
|
||||
if (this.element.prop("disabled")) {
|
||||
this.button.prop("disabled", true);
|
||||
} else {
|
||||
// register event
|
||||
this.button.click(function() {
|
||||
self._toggleTimepicker();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// called when button is clicked
|
||||
_toggleTimepicker: function() {
|
||||
if (this.timepicker.is(":visible")) {
|
||||
this.timepicker.hide();
|
||||
} else {
|
||||
this.element.focus();
|
||||
this._generateTimepickerContents();
|
||||
this._showTimepicker();
|
||||
}
|
||||
},
|
||||
|
||||
// fills timepicker with time_list of element and shows it.
|
||||
// called by _toggleTimepicker
|
||||
_generateTimepickerContents: function() {
|
||||
var self = this,
|
||||
template_str = "<ul>";
|
||||
|
||||
// there is no time_list for this instance so use the default one
|
||||
if (this.options.time_list.length === 0) {
|
||||
this.options.time_list = this.options.default_time_list;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.options.time_list.length; i++) {
|
||||
if (this.options.time_list[i] == "now") {
|
||||
var now = new Date(),
|
||||
hours = now.getHours(),
|
||||
minutes = now.getMinutes();
|
||||
|
||||
hours = ((hours < 10) ? "0" + hours : hours);
|
||||
minutes = ((minutes < 10) ? "0" + minutes : minutes);
|
||||
|
||||
template_str += '<li class="ui-state-active row">' + hours + ":" + minutes + '</li>';
|
||||
} else {
|
||||
template_str += '<li class="ui-state-default row">' + this.options.time_list[i] + '</li>';
|
||||
}
|
||||
}
|
||||
template_str += "</ul>";
|
||||
|
||||
// fill timepicker container
|
||||
this.timepicker.html(template_str);
|
||||
|
||||
// click handler for items (times) in timepicker
|
||||
this.timepicker.find('li').click(function() {
|
||||
// remove active class from all items
|
||||
$(this).parent().children('li').removeClass("ui-state-active");
|
||||
// mark clicked item as active
|
||||
$(this).addClass("ui-state-active");
|
||||
// set the new value and hide the timepicker
|
||||
self.element.val($(this).html());
|
||||
self.timepicker.hide();
|
||||
});
|
||||
},
|
||||
|
||||
// sets offset and shows timepicker container
|
||||
_showTimepicker: function() {
|
||||
var browserHeight = document.documentElement.clientHeight;
|
||||
var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
|
||||
var tpInputHeight = this.element.outerHeight();
|
||||
var tpDialogHeight = this.timepicker.outerHeight() + tpInputHeight;
|
||||
var offsetTop = this.element.offset().top;
|
||||
var offsetLeft = this.element.offset().left;
|
||||
|
||||
// check the remaining space within the viewport
|
||||
// 60 counts for fixed header/footer
|
||||
var checkSpace = offsetTop - scrollY + tpDialogHeight + 60;
|
||||
|
||||
// position timepicker below or above the input
|
||||
if (checkSpace < browserHeight) {
|
||||
// place the timepicker below input
|
||||
var below = offsetTop + tpInputHeight;
|
||||
this.timepicker.css('left', offsetLeft + 'px').css('top', below + 'px');
|
||||
} else {
|
||||
// place timepicker above input
|
||||
var above = offsetTop - tpDialogHeight + tpInputHeight;
|
||||
this.timepicker.css('left', offsetLeft + 'px').css('top', above + 'px');
|
||||
}
|
||||
// show timepicker
|
||||
this.timepicker.show();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
$.Widget.prototype.destroy.apply(this, arguments); // default destroy
|
||||
// now do other stuff particular to this widget
|
||||
}
|
||||
|
||||
});
|
||||
})(grp.jQuery);
|
||||
Reference in New Issue
Block a user