var xD = $; var initDjangoHStoreWidget = function(hstore_field_name, inline_prefix) { // ignore inline templates // if hstore_field_name contains "__prefix__" if(hstore_field_name.indexOf('__prefix__') > -1){ return; } var $ = django.jQuery; // processing inlines if(hstore_field_name.indexOf('inline') > -1){ var inlineClass = $('#id_'+hstore_field_name).parents('.inline-related, .grp-group').attr('class'); // if using TabularInlines stop here // TabularInlines not supported if (inlineClass.indexOf('tabular') > -1) { return; } } // reusable function that retrieves a template even if ID is not correct // (written to support inlines) var retrieveTemplate = function(template_name, field_name){ var specific_template = $('#'+template_name+'-'+field_name); // if found specific template return that if(specific_template.length){ return specific_template.html(); } else{ // get fallback template var html = $('.'+template_name+'-inline').html(); // replace all occurrences of __prefix__ with field_name // and return html = html.replace(/__prefix__/g, inline_prefix); return html; } } // reusable function that compiles the UI var compileUI = function(params){ var hstore_field_id = 'id_'+hstore_field_name, original_textarea = $('#'+hstore_field_id), original_value = original_textarea.val(), original_container = original_textarea.parents('.form-row, .grp-row').eq(0), errorHtml = original_container.find('.errorlist').html(), json_data = {}; if(original_value !== ''){ // manage case in which textarea is blank try{ json_data = JSON.parse(original_value); } catch(e){ alert('invalid JSON:\n'+e); return false; } } var hstore_field_data = { "id": hstore_field_id, "label": original_container.find('label').text(), "name": hstore_field_name, "value": original_textarea.val(), "help": original_container.find('.grp-help, .help').text(), "errors": errorHtml, "data": json_data }, // compile template ui_html = retrieveTemplate('hstore-ui-template', hstore_field_name), compiled_ui_html = _.template(ui_html, hstore_field_data); // this is just to DRY up a bit if(params && params.replace_original === true){ // remove original textarea to avoid having two textareas with same ID original_textarea.remove(); // inject compiled template and hide original original_container.after(compiled_ui_html).hide(); } return compiled_ui_html; }; // generate UI compileUI({ replace_original: true }); // cache other objects that we'll reuse var row_html = retrieveTemplate('hstore-row-template', hstore_field_name), empty_row = _.template(row_html, { 'key': '', 'value': '' }), $hstore = $('#id_'+hstore_field_name).parents('.hstore'); // reusable function that updates the textarea value var updateTextarea = function(container) { // init empty json object var new_value = {}, raw_textarea = container.find('textarea'), rows = container.find('.form-row, .grp-row'); // loop over each object and populate json rows.each(function() { var inputs = $(this).find('input'), key = $(this).find('.hs-key').val(), value = $(this).find('.hs-val').val(); new_value[key] = value; }); // update textarea value $(raw_textarea).val(JSON.stringify(new_value, null, 4)); }; // remove row link $hstore.delegate('a.remove-row', 'click', function(e) { e.preventDefault(); // cache container jquery object before $(this) gets removed $(this).parents('.form-row, .grp-row').eq(0).remove(); updateTextarea($hstore); }); // add row link $hstore.delegate('a.hs-add-row, .hs-add-row a', 'click', function(e) { e.preventDefault(); $hstore.find('.hstore-rows').append(empty_row); xD('.django-select2').djangoSelect2() xD('select').on( 'select2:close', function () { $(this).focus(); }); }); // toggle textarea link $hstore.delegate('.hstore-toggle-txtarea', 'click', function(e) { e.preventDefault(); var raw_textarea = $hstore.find('.hstore-textarea'), hstore_rows = $hstore.find('.hstore-rows'), add_row = $hstore.find('.hs-add-row'); if(raw_textarea.is(':visible')) { var compiled_ui = compileUI(); // in case of JSON error if(compiled_ui === false){ return; } // jquery < 1.8 try{ var $ui = $(compiled_ui); } // jquery >= 1.8 catch(e){ var $ui = $($.parseHTML(compiled_ui)); } // update rows with only relevant content hstore_rows.html($ui.find('.hstore-rows').html()); raw_textarea.hide(); hstore_rows.show(); add_row.show(); xD('.django-select2').djangoSelect2() } else{ raw_textarea.show(); hstore_rows.hide(); add_row.hide(); } }); // update textarea whenever a field changes $hstore.delegate('.hs-val', 'keyup', function() { updateTextarea($hstore); }); $hstore.delegate('.hs-key', 'change', function() { updateTextarea($hstore); }); }; django.jQuery(window).load(function() { // support inlines // bind only once if(django.hstoreWidgetBoundInlines === undefined){ var $ = django.jQuery; $('.grp-group .grp-add-handler, .inline-group .hs-add-row a, .inline-group .add-row').click(function(e){ var hstore_original_textareas = $(this).parents('.grp-group, .inline-group').eq(0).find('.hstore-original-textarea'); // if module contains .hstore-original-textarea if(hstore_original_textareas.length > 0){ // loop over each inline $(this).parents('.grp-group, .inline-group').find('.grp-items div.grp-dynamic-form, .inline-related').each(function(e, i){ var prefix = i; // loop each textarea $(this).find('.hstore-original-textarea').each(function(e, i){ // cache field name var field_name = $(this).attr('name'); // ignore templates // if name attribute contains __prefix__ if(field_name.indexOf('prefix') > -1){ // skip to next return; } initDjangoHStoreWidget(field_name, prefix); }); }); } }); django.hstoreWidgetBoundInlines = true; } });