// This is a manifest file that'll be compiled into application.js, which will include all the files // listed below. // // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. // // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the // the compiled file. // // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD // GO AFTER THE REQUIRES BELOW. // //= require jquery //= require jquery.ui.all //= require jquery.menu_expand //= require jquery_ujs //= require jquery_noconflict //= require prototype //= require prototype_ujs //= require effects //= require dragdrop //= require controls //= require breadcrumb //= require select2 //= require findDomElement //= require context_menu //= require jstoolbar //= require calendar //= require i18n/translations //source: http://stackoverflow.com/questions/8120065/jquery-and-prototype-dont-work-together-with-array-prototype-reverse if (typeof []._reverse == 'undefined') { jQuery.fn.reverse = Array.prototype.reverse; } else { jQuery.fn.reverse = Array.prototype._reverse; } function checkAll (id, checked) { var els = Element.descendants(id); for (var i = 0; i < els.length; i++) { if (els[i].disabled==false) { els[i].checked = checked; } } } function toggleCheckboxesBySelector(selector) { boxes = $$(selector); var all_checked = true; for (i = 0; i < boxes.length; i++) { if (boxes[i].checked == false) { all_checked = false; } } for (i = 0; i < boxes.length; i++) { boxes[i].checked = !all_checked; } } function setCheckboxesBySelector(checked, selector) { var boxes = $$(selector); boxes.each(function(ele) { ele.checked = checked; }); } function showAndScrollTo(id, focus) { Element.show(id); if (focus!=null) { Form.Element.focus(focus); } Element.scrollTo(id); } function toggleRowGroup(el) { var tr = Element.up(el, 'tr'); var n = Element.next(tr); tr.toggleClassName('open'); while (n != undefined && !n.hasClassName('group')) { Element.toggle(n); n = Element.next(n); } } function collapseAllRowGroups(el) { var tbody = Element.up(el, 'tbody'); tbody.childElements('tr').each(function(tr) { if (tr.hasClassName('group')) { tr.removeClassName('open'); } else { tr.hide(); } }) } function expandAllRowGroups(el) { var tbody = Element.up(el, 'tbody'); tbody.childElements('tr').each(function(tr) { if (tr.hasClassName('group')) { tr.addClassName('open'); } else { tr.show(); } }) } function toggleAllRowGroups(el) { var tr = Element.up(el, 'tr'); if (tr.hasClassName('open')) { collapseAllRowGroups(el); } else { expandAllRowGroups(el); } } function toggleFieldset(el) { var fieldset = Element.up(el, 'fieldset'); fieldset.toggleClassName('collapsed'); Effect.toggle(fieldset.down('>div'), 'slide', {duration:0.2}); } function hideFieldset(el) { var fieldset = Element.up(el, 'fieldset'); fieldset.toggleClassName('collapsed'); fieldset.down('>div').hide(); } var fileFieldCount = 1; function addFileField() { fileFieldCount++; if (fileFieldCount >= 10) return false var clone = $('attachment_template').cloneNode(true); clone.writeAttribute('id', ''); clone.innerHTML = clone.innerHTML.replace(/\[1\]/g, '['+ fileFieldCount + ']'); $('attachments_fields').appendChild(clone); } function showTab(name) { var f = $$('div#content .tab-content'); for(var i=0; i0) { lis[i-1].show(); } } function displayTabsButtons() { var lis; var tabsWidth = 0; var i; $$('div.tabs').each(function(el) { lis = el.down('ul').childElements(); for (i=0; i hyphen identifier = identifier.replace(/^[-\d]*|-*$/g, ''); // remove hyphens and numbers at beginning and hyphens at end identifier = identifier.toLowerCase(); // to lower identifier = identifier.substr(0,projectIdentifierMaxLength); // max characters return identifier; } function observeProjectName() { var f = function() { if(!projectIdentifierLocked) { $('project_identifier').setValue(generateProjectIdentifier()); } }; Event.observe('project_name', 'keyup', f); } function observeProjectIdentifier() { var f = function() { if($('project_identifier').getValue() != '' && $('project_identifier').getValue() != generateProjectIdentifier()) { projectIdentifierLocked = true; } else { projectIdentifierLocked = false; } }; Event.observe('project_identifier', 'keyup', f); } function observeParentIssueField(url) { new Ajax.Autocompleter('issue_parent_issue_id', 'parent_issue_candidates', url, { minChars: 1, frequency: 0.5, paramName: 'q', updateElement: function(value) { document.getElementById('issue_parent_issue_id').value = value.id; }, parameters: 'scope=all' }); } function observeRelatedIssueField(url) { new Ajax.Autocompleter('relation_issue_to_id', 'related_issue_candidates', url, { minChars: 1, frequency: 0.5, paramName: 'q', updateElement: function(value) { document.getElementById('relation_issue_to_id').value = value.id; }, parameters: 'scope=all' }); } function setVisible(id, visible) { var el = $(id); if (el) {if (visible) {el.show();} else {el.hide();}} } function observeProjectModules() { var f = function() { /* Hides trackers and issues custom fields on the new project form when issue_tracking module is disabled */ var c = ($('project_enabled_module_names_issue_tracking').checked == true); setVisible('project_trackers', c); setVisible('project_issue_custom_fields', c); }; Event.observe(window, 'load', f); Event.observe('project_enabled_module_names_issue_tracking', 'change', f); } /* * Class used to warn user when leaving a page with unsaved textarea * Author: mathias.fischer@berlinonline.de */ var WarnLeavingUnsaved = Class.create({ observedForms: false, observedElements: false, changedForms: false, message: null, initialize: function(message){ this.observedForms = $$('form'); this.observedElements = $$('textarea'); this.message = message; this.observedElements.each(this.observeChange.bind(this)); this.observedForms.each(this.submitAction.bind(this)); window.onbeforeunload = this.unload.bind(this); }, unload: function(){ if(this.changedForms) return this.message; }, setChanged: function(){ this.changedForms = true; }, setUnchanged: function(){ this.changedForms = false; }, observeChange: function(element){ element.observe('change',this.setChanged.bindAsEventListener(this)); }, submitAction: function(element){ element.observe('submit',this.setUnchanged.bindAsEventListener(this)); } }); /* * 1 - registers a callback which copies the csrf token into the * X-CSRF-Token header with each ajax request. Necessary to * work with rails applications which have fixed * CVE-2011-0447 * 2 - shows and hides ajax indicator */ Ajax.Responders.register({ onCreate: function(request){ var csrf_meta_tag = $$('meta[name=csrf-token]')[0]; if (csrf_meta_tag) { var header = 'X-CSRF-Token', token = csrf_meta_tag.readAttribute('content'); if (!request.options.requestHeaders) { request.options.requestHeaders = {}; } request.options.requestHeaders[header] = token; } if ($('ajax-indicator') && Ajax.activeRequestCount > 0) { Element.show('ajax-indicator'); } }, onComplete: function(){ if ($('ajax-indicator') && Ajax.activeRequestCount == 0) { Element.hide('ajax-indicator'); } addClickEventToAllErrorMessages(); } }); function hideOnLoad() { $$('.hol').each(function(el) { el.hide(); }); } function addClickEventToAllErrorMessages() { $$('a.afocus').each(function(a) { $(a).observe('click', function(event) { var field; field = $($(a).readAttribute('href').substr(1)); if (field == null) { // Cut off '_id' (necessary for select boxes) field = $($(a).readAttribute('href').substr(1).concat('_id')); } if (field) { field.down('input, textarea, select').focus(); } Event.stop(event); return false; }); }); } function toggleEmailDecoratorFields() { lang = jQuery("#emails_decorators_switch").val(); jQuery(".emails_decorators").hide(); jQuery("#emails_decorators_" + lang).show(); } $(document).observe('dom:loaded', function() { // Set focus on first error message var error_focus = $$('a.afocus').first(); var input_focus = $$('.autofocus').first(); if (error_focus != undefined) { error_focus.focus(); } else if (input_focus != undefined){ input_focus.focus(); if (input_focus.tagName === "INPUT") { input_focus.select(); } } // Focus on field with error addClickEventToAllErrorMessages(); }); Event.observe(window, 'load', hideOnLoad); // a few constants for animations speeds, etc. var animationRate = 100; /* jQuery code from #263 */ // returns viewport height jQuery.viewportHeight = function() { return self.innerHeight || jQuery.boxModel && document.documentElement.clientHeight || document.body.clientHeight; }; /* TODO: integrate with existing code and/or refactor */ jQuery(document).ready(function($) { $.extend($.fn.select2.defaults, { formatNoMatches: function () { return I18n.t("js.select2.no_matches"); }, formatInputTooShort: function (input, min) { return I18n.t("js.select2.input_too_short", {count: min - input.length}); }, formatSelectionTooBig: function (limit) { return I18n.t("js.select2.selection_too_big", {limit: limit}); }, formatLoadMore: function (pageNumber) { return I18n.t("js.select2.load_more"); }, formatSearching: function () { return I18n.t("js.select2.searching"); } }); $('#project-search-container .select2-select').each(function (ix, select) { var PROJECT_JUMP_BOX_PAGE_SIZE = 50; var select2, menu; select = $(select); menu = select.parents('li.drop-down'); select.select2({ formatResult : OpenProject.Helpers.Search.formatter, matcher : OpenProject.Helpers.Search.matcher, query : OpenProject.Helpers.Search.projectQueryWithHierarchy( jQuery.proxy(openProject, 'fetchProjects'), PROJECT_JUMP_BOX_PAGE_SIZE) }). on('change', function (e) { if (e.val) { window.location = select2.data().project.url; } }). on('close', function () { if (menu.is('.open')) { menu.slideAndFocus(); } }); select2 = select.data('select2'); // add custom css styling to result list select2.dropdown.attr("id", "project-search-results"); // Adding an event handler to change select2's default behavior concerning // TAB and ESC select2.search.keydown(function (e) { switch (e.which) { case 9: // TAB closestVisible = select2.container.children(".select2-choice").closest(":visible"); if (e.shiftKey) { closestVisible.previousElementInDom(":input:visible, a:visible").focus(); } else { closestVisible.nextElementInDom(":input:visible, a:visible").focus(); } e.stopImmediatePropagation(); e.preventDefault(); return false; case 27: // ESC e.stopImmediatePropagation(); e.preventDefault(); return false; } }); // Moving the newly attached handler to the beginning of the handler chain select2.search.data('events').keydown.unshift(select2.search.data('events').keydown.pop()); menu.bind("closed", function () { // Close select2 result list, when menu is closed select2.close(); }); menu.bind("opened", function () { // Open select2 element, when menu is opened select2.open(); // Include input in tab cycle by attaching keydown handlers to previous // and next interactive DOM element. select2.container.previousElementInDom(":input:visible, a:visible").keydown(function (e) { if (!e.shiftKey && e.which === 9) { select2.search.focus(); e.preventDefault(); } }); select2.container.nextElementInDom(":input:visible:not(.select2-input), a:visible:not(.select2-input)").keydown(function (e) { if (e.shiftKey && e.which === 9 && select2.search.is(":visible")) { select2.search.focus(); e.preventDefault(); } }); }); }); // file table thumbnails $("table a.has-thumb").hover(function() { $(this).removeAttr("title").toggleClass("active"); // grab the image dimensions to position it properly var thumbImg = $(this).find("img"); var thumbImgLeft = -(thumbImg.outerWidth() ); var thumbImgTop = -(thumbImg.height() / 2 ); thumbImg.css({top: thumbImgTop, left: thumbImgLeft}).show(); }, function() { $(this).toggleClass("active").find("img").hide(); }); // show/hide the files table $(".attachments h4").click(function() { $(this).toggleClass("closed").next().slideToggle(animationRate); }); // custom function for sliding the main-menu. IE6 & IE7 don't handle sliding very well $.fn.slideAndFocus = function(callback) { this.toggleClass("open").find("> ul").mySlide(function() { // actually a simple focus should be enough. // The rest is only there to work around a rendering bug in webkit (as of Oct 2011) TODO: fix if ($("input#username-pulldown").is(":visible")) { var input = $("input#username-pulldown"); } else { var input = $(this).find(".select2-search input"); } if (input.is(":visible")) { input.blur(); setTimeout(function() { input.focus(); }, 100); } else { $(this).find("li > a:first").focus(); } if (typeof callback === 'function') { callback.apply(this); } }); return false; }; // custom function for sliding the main-menu. IE6 & IE7 don't handle sliding very well $.fn.mySlide = function(callback) { if (parseInt($.browser.version, 10) < 8 && $.browser.msie) { // no animations, just toggle this.toggle(); if (typeof callback === 'function') { callback.apply(this); } // this forces IE to redraw the menu area, un-bollocksing things $("#main-menu").css({paddingBottom:5}).animate({paddingBottom:0}, 10); } else { this.slideToggle(animationRate,callback); } return this; }; $.fn.onClickDropDown = function(){ var that = this; $('html').click(function() { that.find(" > li.drop-down.open").removeClass("open").find("> ul").mySlide(); that.removeClass("hover"); }); // Do not close the login window when using it that.find("li li").click(function(event){ event.stopPropagation(); }); // trap all mouseevents inside dropdown menu items to prevent side effects this.find(" > li.drop-down").bind("mousedown mouseup click", function (event) { event.stopPropagation(); }); this.find(" > li.drop-down").click(function(event) { // if an h2 tag follows the submenu should unfold out at the border var menu_start_position; if (that.next().get(0) != undefined && (that.next().get(0).tagName == 'H2')){ menu_start_position = that.next().innerHeight() + that.next().position().top; that.find("ul.action_menu_more").css({ top: menu_start_position }); } else if(that.next().hasClass("wiki-content") && that.next().children().next().first().get(0) != undefined && that.next().children().next().first().get(0).tagName == 'H1'){ var wiki_heading = that.next().children().next().first(); menu_start_position = wiki_heading.innerHeight() + wiki_heading.position().top; that.find("ul.action_menu_more").css({ top: menu_start_position }); } $(this).toggleSubmenu(that); return false; }); }; $.fn.toggleSubmenu = function(menu){ if (menu.find(" > li.drop-down.open").get(0) !== $(this).get(0)){ menu.find(" > li.drop-down.open").removeClass("open").find("> ul").mySlide(function () { if ($(this).is(":visible")) { $(this).parents('li.drop-down').trigger("opened"); } else { $(this).parents('li.drop-down').trigger("closed"); } }); } $(this).slideAndFocus(function () { if ($(this).is(":visible")) { $(this).parents('li.drop-down').trigger("opened"); } else { $(this).parents('li.drop-down').trigger("closed"); } }); menu.toggleClass("hover"); }; // open and close the main-menu sub-menus $("#main-menu li:has(ul) > a").not("ul ul a") .append("") .click(function() { $(this).toggleClass("open").parent().find("ul").not("ul ul ul").mySlide(); return false; }); // submenu flyouts $("#main-menu li li:has(ul)").hover(function() { $(this).find(".profile-box").show(); $(this).find("ul").slideDown(animationRate); }, function() { $(this).find("ul").slideUp(animationRate); }); // add filter dropdown menu $(".button-large:has(ul) > a").click(function(event) { var tgt = $(event.target); // is this inside the title bar? if (tgt.parents().is(".title-bar")) { $(".title-bar-extras:hidden").slideDown(animationRate); } $(this).parent().find("ul").slideToggle(animationRate, function () { if ($(this).is(":visible")) { $(this).parents("li.drop-down").trigger("opened"); } else { $(this).parents("li.drop-down").trigger("closed"); } }); return false; }); jQuery("#account-nav > li").hover(function() { if ($("#account-nav").hasClass("hover") && ($("#account-nav > li.drop-down.open").get(0) !== $(this).get(0))){ //Close all other open menus //Used to work around the rendering bug TODO: fix jQuery("input#username-pulldown").blur(); $("#account-nav > li.drop-down.open").toggleClass("open").find("> ul").mySlide(function () { $(this).parents("li.drop-down").trigger("closed"); }); $(this).slideAndFocus(function () { $(this).parents("li.drop-down").trigger("opened"); }); return false; } }, function(){ return false; }); $("#account-nav").onClickDropDown(); $(".action_menu_main").onClickDropDown(); // deal with potentially problematic super-long titles $(".title-bar h2").css({paddingRight: $(".title-bar-actions").outerWidth() + 15 }); // rejigger the main-menu sub-menu functionality. $("#main-menu .toggler").remove(); // remove the togglers so they're inserted properly later. $(window).resize(function() { // wait 200 milliseconds for no further resize event // then readjust breadcrumb if(this.resizeTO) clearTimeout(this.resizeTO); this.resizeTO = setTimeout(function() { $(this).trigger('resizeEnd'); }, 200); }); $(window).bind('resizeEnd', function() { jQuery("div#breadcrumb ul.breadcrumb").adjustBreadcrumbToWindowSize(); }); $("#main-menu li:has(ul) > a").not("ul ul a") // 1. unbind the current click functions .unbind("click") // 2. wrap each in a span that we'll use for the new click element .wrapInner("") // 3. reinsert the so that it sits outside of the above .append("") // 4. attach a new click function that will follow the link if you clicked on the span itself and toggle if not .click(function(event) { if ($(event.target).hasClass("toggler") ) { var menuParent = $(this).toggleClass("open").parent().find("ul").not("ul ul ul"); menuParent.mySlide(); if ($(this).hasClass("open")) { menuParent.find("li > a:first").focus(); } return false; } }); // Do not close the login window when using it $('#nav-login-content').click(function(event){ event.stopPropagation(); }); jQuery('table.cal div.issue.tooltip').each(function(){ var div = $(this); div.find('a').first().focus(function(){ div.addClass('hover'); }); div.find('a').first().blur(function(){ div.removeClass('hover'); }); }); // Users of some old IEs are out of luck ATM. A userData implementation // could be provided though, that would be great! var remember_menu_state; if (typeof window.sessionStorage !== 'undefined') { remember_menu_state = function (match) { if (typeof match === 'undefined') { return sessionStorage.getItem('openproject:navigation-toggle'); } else { return sessionStorage.setItem('openproject:navigation-toggle', match.length > 0 ? 'collapsed' : 'expanded'); } }; } else { remember_menu_state = function (match) { return false; }; } var toggle_navigation = function() { var height = $(document).height() - $('#main-menu').offset().top - 32; $('#main-menu, #menu-sidebar').toggleClass('hidden'); $('#content').toggleClass('hidden-navigation'); $('#toggle-project-menu').removeAttr("style").toggleClass('show'); remember_menu_state($('#toggle-project-menu.show').css({height:height})); }; // register toggler, and toggle for the first time if remembered to be closed. jQuery('#toggle-project-menu .navigation-toggler').click(toggle_navigation); if ($('#main-menu').length > 0 && remember_menu_state() === "collapsed") { toggle_navigation(); } }); /* this could and should be moved into separate file once the asset pipeline is in place */ (function ($) { var append_href, close, dom_identifier = { 'indicator_class': 'ajax_indicator', 'window_class': 'ajax_appended_information', 'trigger_class': 'ajax_append' }, i18n = { 'hide': 'Hide' }, merge_with_defaults, replace_with_loading, replace_with_close, replace_with_open, slideIn, init; close = function () { var close_link = $(this), information_window = close_link.siblings('.' + dom_identifier.window_class); replace_with_open(close_link); information_window.slideUp(); }; append_href = function (link) { var container, url = link.attr('href'); container = link.siblings('.' + dom_identifier.window_class); if (container.size() > 0) { container.slideDown(); replace_with_close(link, true); } else { container = $(''), $.ajax({ url: url, headers: { Accept: 'text/javascript' }, complete: function (jqXHR) { container.html(jqXHR.responseText); slideIn(container); } }); link.parent().append(container); replace_with_loading(link); } }; replace_with_loading = function (link) { var loading = $(''); link.hide(); link.after(loading); }; replace_with_close = function (to_replace, hide) { var close_link = $('' + i18n.hide + ''); to_replace.after(close_link); if (hide) { to_replace.hide(); } else { to_replace.remove(); } close_link.click(close); }; replace_with_open = function(to_replace) { var load_link = to_replace.siblings('.' + dom_identifier.trigger_class); to_replace.remove(); /* this link is never removed, only hidden */ load_link.show(); }; slideIn = function (container) { container.slideDown(); replace_with_close(container.siblings('.' + dom_identifier.indicator_class)); }; merge_with_defaults = function (options) { $.extend(dom_identifier, options.dom_identifier); $.extend(i18n, options.i18n); }; if ($.ajaxAppend) { return; }; $.ajaxAppend = function (options) { merge_with_defaults(options); $('.' + dom_identifier.trigger_class).click(function(link) { append_href($(this)); return false; }); }; }(jQuery)); var Administration = (function ($) { var update_default_language_options, init_language_selection_handling, toggle_default_language_select; update_default_language_options = function (input) { var default_language_select = $('#setting_default_language select'), default_language_select_active; if (input.attr('checked')) { default_language_select.find('option[value="' + input.val() + '"]').removeAttr('disabled'); } else { default_language_select.find('option[value="' + input.val() + '"]').attr('disabled', 'disabled'); } default_language_select_active = default_language_select.find('option:not([disabled="disabled"])'); toggle_disabled_state(default_language_select_active.size() === 0); if (default_language_select_active.size() === 1) { default_language_select_active.attr('selected', true); } else if (default_language_select.val() === input.val() && !input.attr('checked')) { default_language_select_active.first().attr('selected', true); } }; toggle_disabled_state = function (active) { $('#setting_default_language select').attr('disabled', active) .closest('form') .find('input:submit') .attr('disabled', active); }; init_language_selection_handling = function () { $('#setting_available_languages input:not([checked="checked"])').each(function (index, input) { update_default_language_options($(input)); }); $('#setting_available_languages input').click(function () { update_default_language_options($(this)); }); }; return { init_language_selection_handling: init_language_selection_handling }; }(jQuery)); var I18nForms = (function ($) { var event_handler, init, id_memo = {}, memorize_ids, submit_preparer; event_handler = (function() { var active_locale_selectors, add_locale_fields, destroy_locale, init, observe_add_locale_link, observe_destroy_locale_links, select_first_untaken_option, taken_options, update_add_link_status, update_destroy_link_status, update_interaction_elements, update_locale_availability; active_locale_selectors = function (localized_p) { return localized_p.find('.locale_selector'); }; add_locale_fields = function (localized_p) { var backup = localized_p.find('.translation').first(), add_link = localized_p.find('.add_locale'), new_items = backup.clone(); new_items.find('input, textarea').val(""); new_items.insertBefore(add_link); select_first_untaken_option(new_items.find('.locale_selector'), active_locale_selectors(localized_p)); update_interaction_elements(localized_p); new_items.find('.destroy_locale').click(function () { destroy_locale($(this)); }); }; destroy_locale = function (element) { var localized_p = element.closest('p'); element.closest('.translation').remove(); update_interaction_elements(localized_p); }; observe_add_locale_link = function () { $('.add_locale').click(function () { add_locale_fields($(this).closest('p')); }); }; observe_destroy_locale_links = function () { $('.destroy_locale').click(function () { destroy_locale($(this)); }); }; select_first_untaken_option = function (select, others) { var taken, available; taken = taken_options(others); available = select.find('option').map(function (index, element) { element = $(element); if (taken.indexOf(element.val()) < 0) { return element.val(); } }).get(); select.val(available.pop()); }; update_add_link_status = function (localized_p) { var indicator_selector = active_locale_selectors(localized_p), taken = taken_options(indicator_selector), all_options, available, add_link = localized_p.find('.add_locale'); available = indicator_selector.first().find('option').map(function (index, element) { element = $(element); if (taken.indexOf(element.val()) < 0) { return element.val(); } }).get(); add_link.toggle(available.size() > 0); }; update_destroy_link_status = function (localized_p) { var active_selectors = active_locale_selectors(localized_p); localized_p.find('.destroy_locale').toggle(active_selectors.size() > 1); }; update_interaction_elements = function (localized_p) { update_locale_availability(localized_p); update_add_link_status(localized_p); update_destroy_link_status(localized_p); }; update_locale_availability = function (localized_p) { var active_selectors = active_locale_selectors(localized_p), active_locales = taken_options(active_selectors); active_selectors.each(function (index, element) { var selector = $(element), selected_value = selector.val(); selector.find('option').each(function (index, element) { var option = $(element); option.attr('disabled', (active_locales.indexOf(option.val()) >= 0 && option.val() !== selected_value)); }); }); }; taken_options = function (select_collection) { return select_collection.map(function (index, element) { element = $(element); return element.val(); }).get(); }; init = function () { observe_add_locale_link(); observe_destroy_locale_links(); $('form .translation').closest('p').each(function (i, element) { element = $(element); update_interaction_elements(element); }); } return { init : init } })(); memorize_ids = function (form) { var translations = $('.translation'); translations.each(function (i, element) { var id, locale; element = $(element); id = element.find(".translation_id").val(); locale = element.find(".locale_selector").val(); if (locale !== "" && id !== "" && id_memo[locale] === undefined) { id_memo[locale] = id; } }); }; submit_preparer = (function() { var add_destroy_elements, add_empty_values_for_missing_attributes, collect_elements_by_locale, collect_translation_classes, element_numerator, prepare, unify_translations_across_attribute; add_destroy_elements = function(locale) { var translation, destroy_element, id_element, destroy_id_elements; translation = $('.translation').first(); destroy_id_elements = translation.find('.destroy_flag, .translation_id').clone(); destroy_element = translation.filter('.destroy_flag'); id_element = translation.filter('.translation_id'); translation.after(destroy_id_elements); destroy_id_elements.filter('.destroy_flag').attr('disabled', false); destroy_id_elements.filter('.translation_id').attr('value', id_memo[locale]); element_numerator.set_next_number_in_name(destroy_id_elements); }; add_empty_values_for_missing_attributes = function (locale, translation_classes) { var new_translations = $(''); $.each(translation_classes, function(i, translated_attribute) { var translations = $('.' + translated_attribute), locale_selectors = translations.find('.locale_selector'), included, new_translation; included = locale_selectors.map(function(i, element) { return $(element).val(); }).get().indexOf(locale) >= 0; if (!included) { new_translation = translations.first().clone(); new_translation.hide(); new_translation.find('.destroy_flag').val('1') .attr('disabled', false); new_translation.find('input, textarea').val(''); new_translation.find('.locale_selector').val(locale); new_translation.insertAfter(translations.first()); new_translations = new_translations.add(new_translation); } }); return new_translations; }; collect_elements_by_locale = function(translations) { var locales = {}; translations.each(function (i, element) { var locale; element = $(element); locale = element.find('.locale_selector').val(); if (locales[locale] === undefined) { locales[locale] = element; } else { locales[locale] = locales[locale].add(element); } }); return locales; }; collect_translation_classes = function (translations) { var translation_classes = []; translations.each(function (i, element) { var translation_class; element = $(element); translation_class = /\w+_translation/.exec(element.attr('class'))[0]; if (translation_classes.indexOf(translation_class) < 0) { translation_classes.push(translation_class); } }); return translation_classes; }; element_numerator = (function() { var init, num, number_matcher = /([\[_])(\d+)([\]\[_]{1,2}\w+\]?$)/, replace_number_in_name, set_next_number_in_name; init = function() { num = 0; } set_next_number_in_name = function(elements) { replace_number_in_name(elements, num); num += 1; } replace_number_in_name = function (element, rep_number) { element.each(function (index, e) { e = $(e); e.attr('name', e.attr('name').replace(number_matcher, "$1" + rep_number + "$3")); }); }; return { init : init, set_next_number_in_name : set_next_number_in_name }; })(); prepare = function (form) { var locales, translation_classes = [], translations = form.find('.translation'); element_numerator.init(); translation_classes = collect_translation_classes(translations); locales = collect_elements_by_locale(translations); $.each(locales, function(locale, elements) { empty_value_elements = add_empty_values_for_missing_attributes(locale, translation_classes); unify_translations_across_attribute(locale, elements.add(empty_value_elements), translation_classes); }); $.each(id_memo, function(locale, id) { if (!locales.hasOwnProperty(locale)) { add_destroy_elements(locale); } }); }; unify_translations_across_attribute = function (locale, elements, translation_classes) { var current_id, i, to_destroy, to_keep; element_numerator.set_next_number_in_name(elements.find('select, textarea, input')); current_id = id_memo[locale] !== undefined ? id_memo[locale] : ''; elements.find('.translation_id').val(current_id); to_destroy = elements.filter(':hidden'); to_keep = elements.filter(':visible'); if (to_keep.size() > 0) { to_destroy.find('.destroy_flag').attr('disabled', true); for (i = 0; i < translation_classes.size(); i += 1) { if (to_keep.filter("." + translation_classes[i]).size() === 0) { to_destroy.filter("." + translation_classes[i]).find('input[type=text], textarea').val(''); } } } }; return { prepare : prepare }; })(); init = function () { var translated_paragraph = $('form .translation').closest('p'); if (translated_paragraph.size() > 0) { memorize_ids(); event_handler.init(); translated_paragraph.closest('form').submit(function () { submit_preparer.prepare($(this)); // allow default behaviour }); } }; return { init : init }; }(jQuery)); jQuery(document).ready(I18nForms.init); var SubmitConfirm = (function($) { var init; init = function(element, question) { element.submit(function() { if(!confirm(question)) { return false; } }); }; return { init: init }; })(jQuery); var Preview = (function ($) { $('document').ready(function() { $('a.preview').click(function() { $.ajax({ url: $(this).attr('href'), type: 'POST', data: $("#" + $(this).attr('id').replace(/-preview/, "")).serialize().replace('_method=put&', ''), success: function(data) { $('#preview').html(data); $('html, body').animate({ scrollTop: $('#preview').offset().top }, 'slow'); } }); return false; }); }); })(jQuery);