//-- copyright // OpenProject is a project management system. // Copyright (C) 2012-2013 the OpenProject Foundation (OPF) // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License version 3. // // OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: // Copyright (C) 2006-2013 Jean-Philippe Lang // Copyright (C) 2010-2013 the ChiliProject Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // See doc/COPYRIGHT.rdoc for more details. //++ // 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-ui-i18n //= require jquery.menu_expand //= require jquery_ujs //= require jquery_noconflict //= require prototype //= require effects //= require dragdrop //= require controls //= require i18n/translations //= require select2 //= require top_menu //= require action_menu //= require openproject //= require breadcrumb //= require findDomElement //= require context_menu //= require jstoolbar //= require ajaxappender //= require settings //= require modal //= require keyboard_shortcuts //= require top-shelf //= require unsupported-browsers //= require_tree ./pages //= require openproject_plugins //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; } jQuery(document).ready(function ($) { //remove modal layout=false if we ctrl-click! $(document.body).on("click", "a", function (e) { if (top != self && e.ctrlKey) { if (e.target && e.target.href) { var url = e.target.href; if (url.match(/(&)?layout=false/)) { url = url.replace(/(&)?layout=false/g, "").replace(/\?$/, ""); window.open(url); e.preventDefault(); } } } }); if (typeof CS !== "undefined") { var regions = $.datepicker.regional; var regional = regions[CS.lang] || regions[""]; $.datepicker.setDefaults(regional); var gotoToday = $.datepicker._gotoToday; $.datepicker._gotoToday = function (id) { gotoToday.call(this, id); var target = $(id), inst = this._getInst(target[0]), dateStr = $.datepicker._formatDate(inst); target.val(dateStr); target.blur(); $.datepicker._hideDatepicker(); }; var defaults = { showWeek: true, changeMonth: true, changeYear: true, dateFormat: 'yy-mm-dd', showButtonPanel: true, calculateWeek: function (d) { if (d.getDay() > 1) { d.setDate(d.getDate() - d.getDay() + 1); } return $.datepicker.iso8601Week(d); } }; if (CS.firstWeekDay && CS.firstWeekDay !== "") { defaults.firstDay = parseInt(CS.firstWeekDay, 10); } $.datepicker.setDefaults(defaults); } }); jQuery(document).ajaxError(function(event, request, settings) { if (request.status === 401 && /X-Reason: login needed/.match(request.getAllResponseHeaders())) { if (confirm(I18n.t("js.logoff") + "\r\n" + I18n.t("js.redirect_login"))) { location.href = openProject.loginUrl + "?back_url=" + encodeURIComponent(location.href); } } }); 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_id', 'parent_issue_candidates', url, { minChars: 1, frequency: 0.5, paramName: 'q', updateElement: function(value) { document.getElementById('issue_parent_id').value = value.id; }, parameters: 'scope=all' }); } function observeWorkPackageParentField(url) { new Ajax.Autocompleter('work_package_parent_id', 'parent_issue_candidates', url, { minChars: 1, frequency: 0.5, paramName: 'q', updateElement: function(value) { document.getElementById('work_package_parent_id').value = value.id; }, parameters: 'scope=all' }); } function observeRelatedIssueField(url) { new Ajax.Autocompleter('relation_to_id', 'related_issue_candidates', url, { minChars: 1, frequency: 0.5, paramName: 'q', updateElement: function(value) { document.getElementById('relation_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 types 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_types', 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 */ document.observe("dom:loaded", function() { Ajax.Responders.register({ onCreate: function(request){ var csrf_meta_tag = $$('meta[name=csrf-token]')[0]; if (!request.options.requestHeaders) { request.options.requestHeaders = {}; } if (csrf_meta_tag) { var header = 'X-CSRF-Token', token = csrf_meta_tag.readAttribute('content'); request.options.requestHeaders[header] = token; } request.options.requestHeaders['X-Authentication-Scheme'] = "Session"; if ($('ajax-indicator') && Ajax.activeRequestCount > 0) { Element.show('ajax-indicator'); } }, onComplete: function(request, result){ if (result.status === 401 && /X-Reason: login needed/.match(result.getAllResponseHeaders())) { if (confirm(I18n.t("js.logoff") + "\r\n" + I18n.t("js.redirect_login"))) { location.href = openProject.loginUrl + "?back_url=" + encodeURIComponent(location.href); } } 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; }; /* * 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 */ jQuery(document).ready(function($) { document.ajaxActive = false; $(document).ajaxSend(function (event, request) { document.ajaxActive = true; var csrf_meta_tag = $('meta[name=csrf-token]'); if (csrf_meta_tag) { var header = 'X-CSRF-Token', token = csrf_meta_tag.attr('content'); request.setRequestHeader(header, token); } request.setRequestHeader('X-Authentication-Scheme', "Session"); }); // ajaxStop gets called when ALL Requests finish, so we won't need a counter as in PT $(document).ajaxStop(function () { document.ajaxActive = false; if ($('#ajax-indicator')) { $('#ajax-indicator').hide(); } addClickEventToAllErrorMessages(); }); $.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; select = $(select); var select2, menu = select.parents('li.drop-down'), that = this; 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), dropdownCssClass : "project-search-results", containerCssClass : "select2-select", }). on('change', function (e) { // this handles expected 'new-tab behaviour' if (e.val) { // jQuery sets metaKey to true when pressing ctrl if (e.metaKey) { window.open(select2.data().project.url); } else { window.location = select2.data().project.url; } } }). on('close', function () { if (menu.is('.open')) { menu.trigger("closeMenu"); } }); select2 = select.data('select2'); // 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(); setTimeout(function () { $("#select2-drop-mask").hide(); }, 50); // 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); }); // 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(); }); $.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; }; $("#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(); } }); 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() { $('html').on('click','a.preview', function() { $.ajax({ url: $(this).attr('href'), type: 'PUT', 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);